Custom UIToolbar too close to the home indicator on iPhone X
Asked Answered
L

4

12

I have a custom UIToolbar that I'm showing when the tab bar is hidden. The toolbar buttons are too close to the home indicator on iPhone X:

let toolbar = UIToolbar()
let height = tabBarController?.tabBar.frame.height
toolbar.frame = CGRect(x: 0, y: view.bounds.height - height, width: view.bounds.width, height: height)
toolbar.autoresizingMask = [.flexibleWidth, .flexibleTopMargin]
view.addSubview(toolbar)

Buttons are too close to the home indicator Buttons are too close to the home indicator

This is what I want it to look like (Mail app)

This is what I want it to look like (Mail app) ^

Since this is a custom view, I know that I can change the y position and move it to start at the bottom of safe area but I'd rather move the buttons. I'm using plain UIBarButtonItem with flexible space in between.

Languid answered 13/1, 2018 at 2:19 Comment(4)
Answered hereFireweed
How did you resolve this issue? can you help ?Saloop
@Saloop Instead of using UIToolbar, I ended up creating my own UIView with a horizontal stack view.Languid
Got it. Thanks a bunchSaloop
R
8

In iOS 11, Apple is deprecating the top and bottom layout guides and being replaced with a single safe area layout guide. So use Safe Area Layout Guides to move the view above from the home indicator.

Using Storyboard :

  • Go to Storyboard and in the Interface Builder Document section
  • Tick the Use Safe Area Layout Guides check box
  • Then change the Bottom Constraint to be relative to the Safe Area

Now the views are aligned above the Home Indicator.

OR By way of Coding,

   toolbar.translatesAutoresizingMaskIntoConstraints = false

   NSLayoutConstraint.activate([
        toolbar.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor),
        toolbar.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor),
        toolbar.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor),
        toolbar.heightAnchor.constraint(equalToConstant: 50)
        ])

See this article for Positioning Content Relative to the Safe Area

Round answered 13/1, 2018 at 2:51 Comment(2)
I am hiding the tab bar and showing the toolbar when the user clicks a button. Just like what the Photos app does when you click on "Select". If I constrain the toolbar to the safe area, its bottom will be aligned with the top of the hidden tab bar (which means that there will be a gap of the size of the tab bar, between the toolbar and the bottom of the view).Languid
The user wants to extend the height of the toolbar while aligning content at the top of the bar. This answer offsets the entire bar up away from the safe area. While this is helpful for other scenarios, this is explicitly the outcome the user did not want.Brophy
G
6

You shouldn't need to set an explicit height or size to get this to work, simply take advantage of the layoutMarginsGuide and the UIBarPositioningDelegate protocol, which is adopted by UIToolbarDelegate. First, layout your toolbar so that it's pinned to the bottom of the view's layoutMarginsGuide.

let constraints = [
    toolbar.leadingAnchor.constraint(equalTo: view.leadingAnchor),
    toolbar.trailingAnchor.constraint(equalTo: view.trailingAnchor),
    toolbar.bottomAnchor.constraint(equalTo: view.layoutMarginsGuide.bottomAnchor)
]
NSLayoutConstraint.activate(constraints)

This will get the toolbar aligned to the safe area on iOS 11+ devices, but you'll need to do one last thing to get the toolbar to extend its background all the way to the bottom of the view. To get that simply conform to UIToolbarDelegate in your class, set yourself as the toolbar's delegate, implement the function position(for: UIBarPositioning) -> UIBarPosition, and return the value .bottom. The default value for UIToolbar's barPosition is bottom, so this last step may not be necessary in most use cases. After doing this, you should see your toolbar lay out its items relative to the safe area while extending the background all the way to the bottom of the view, just like you see in Mail and Safari.

The beauty of using layoutMarginsGuide over safeAreaLayoutGuide in this case is that the layoutMarginsGuide insets the layout margins by the safe area by default. Because you don't directly refer to the safe area, your code is backwards compatible all the way to iOS 9 without having to use availability checks.

Georgy answered 13/11, 2018 at 17:51 Comment(0)
A
3

I ran into this problem also. My solution was to use a generic UIView to account for the bottom safeAreaInset and then add the toolbar as a subview of that view.

private func addToolbar(_ toolbar: UIToolbar, toView view: UIView) {
    toolbar.frame = CGRect(
        x: 0,
        y: 0,
        width: view.frame.size.width,
        height: 0
    )
    toolbar.sizeToFit() // This sets the standard height for the toolbar.

    // Create a view to contain the toolbar:
    let toolbarParent = UIView()
    toolbarParent.frame = CGRect(
        x: 0,
        y: view.frame.size.height - toolbar.frame.size.height,
        width: toolbar.frame.size.width,
        height: toolbar.frame.size.height
    )

    // Adjust the position and height of the toolbar's parent view to account for safe area:
    if #available(iOS 11, *) {
        toolbarParent.frame.origin.y -= view.safeAreaInsets.bottom
        toolbarParent.frame.size.height += view.safeAreaInsets.bottom
    }

    toolbarParent.addSubview(toolbar)
    view.addSubview(toolbarParent)
}
Aime answered 30/5, 2018 at 1:41 Comment(0)
M
0

Apple's apps like Mail, Reminders, Calendar, they use UIViewController.setToolbarItems(_:animated:)

let navigationController: UINavigationController = .init(rootViewController: ViewController())
navigationController.setToolbarHidden(false, animated: false)

@MainActor final class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        setToolbarItems([.init(image: .init(systemName: "camera.aperture"), style: .plain, target: self, action: #selector(foo(_:)))], animated: false)
    }
}

enter image description here

Mull answered 22/10, 2023 at 11:21 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.