1

This issue is related to the UIToolBar layout constraints issue in iOS13. I am encountering the following layout constraints warning and I am using autolayout:

[LayoutConstraints] Unable to simultaneously satisfy constraints.
    Probably at least one of the constraints in the following list is one you don't want. 
    Try this: 
        (1) look at each constraint and try to figure out which you don't expect; 
        (2) find the code that added the unwanted constraint or constraints and fix it. 
    (Note: If you're seeing NSAutoresizingMaskLayoutConstraints that you don't understand, refer to the documentation for the UIView property translatesAutoresizingMaskIntoConstraints) 
(
    "<NSAutoresizingMaskLayoutConstraint:0x600001018c30 h=--& v=--& _UIToolbarContentView:0x7fc8e4eacdf0.width == 0   (active)>",
    "<NSLayoutConstraint:0x600001003d40 H:|-(0)-[_UIButtonBarStackView:0x7fc8e4ead4d0]   (active, names: '|':_UIToolbarContentView:0x7fc8e4eacdf0 )>",
    "<NSLayoutConstraint:0x600001003cf0 H:[_UIButtonBarStackView:0x7fc8e4ead4d0]-(0)-|   (active, names: '|':_UIToolbarContentView:0x7fc8e4eacdf0 )>",
    "<NSLayoutConstraint:0x60000100e5d0 'TB_Leading_Leading' H:|-(16)-[_UIModernBarButton:0x7fc8e4f4e050'Add category']   (active, names: '|':_UIButtonBarButton:0x7fc8e4f4de80 )>",
    "<NSLayoutConstraint:0x60000102ec10 'TB_Trailing_Trailing' H:[_UIModernBarButton:0x7fc8e4f4e050'Add category']-(16)-|   (active, names: '|':_UIButtonBarButton:0x7fc8e4f4de80 )>",
    "<NSLayoutConstraint:0x6000010183c0 'UISV-canvas-connection' UILayoutGuide:0x600000a2a3e0'UIViewLayoutMarginsGuide'.leading == UIView:0x7fc8e4f0d920.leading   (active)>",
    "<NSLayoutConstraint:0x6000010185a0 'UISV-canvas-connection' UILayoutGuide:0x600000a2a3e0'UIViewLayoutMarginsGuide'.trailing == UIView:0x7fc8e4f4e8e0.trailing   (active)>",
    "<NSLayoutConstraint:0x6000010185f0 'UISV-spacing' H:[UIView:0x7fc8e4f0d920]-(0)-[_UIButtonBarButton:0x7fc8e4f4de80]   (active)>",
    "<NSLayoutConstraint:0x600001018640 'UISV-spacing' H:[_UIButtonBarButton:0x7fc8e4f4de80]-(0)-[UIView:0x7fc8e4f4e8e0]   (active)>",
    "<NSLayoutConstraint:0x6000010007d0 'UIView-leftMargin-guide-constraint' H:|-(0)-[UILayoutGuide:0x600000a2a3e0'UIViewLayoutMarginsGuide'](LTR)   (active, names: '|':_UIButtonBarStackView:0x7fc8e4ead4d0 )>",
    "<NSLayoutConstraint:0x600001001310 'UIView-rightMargin-guide-constraint' H:[UILayoutGuide:0x600000a2a3e0'UIViewLayoutMarginsGuide']-(0)-|(LTR)   (active, names: '|':_UIButtonBarStackView:0x7fc8e4ead4d0 )>"
)

Will attempt to recover by breaking constraint 
<NSLayoutConstraint:0x60000102ec10 'TB_Trailing_Trailing' H:[_UIModernBarButton:0x7fc8e4f4e050'Add category']-(16)-|   (active, names: '|':_UIButtonBarButton:0x7fc8e4f4de80 )>

Make a symbolic breakpoint at UIViewAlertForUnsatisfiableConstraints to catch this in the debugger.
The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in <UIKitCore/UIView.h> may also be helpful.

Code:

let toolBar: UIToolbar = {
    let tb = UIToolbar()
    tb.translatesAutoresizingMaskIntoConstraints = false
    return tb
}()

fileprivate func setupView() {

    view.addSubview(toolBar)
    toolBar.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
    toolBar.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
    toolBar.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor).isActive = true

    toolBar.items = [
        UIBarButtonItem.flexibleSpace(),
        UIBarButtonItem(title: "Add category", style: .plain, target: self, action: #selector(addCategoryButtonTapped)),
        UIBarButtonItem.flexibleSpace()
    ]
}

As suggested by this post and this, specifying the toolBar's frame manually could remove the warning. However, I am not able to get the toolbar to anchor to safeAreaLayoutGuide.bottomAnchor.

Attempt:

let toolBar = UIToolbar(frame: CGRect(x: 0, y: view.bounds.height - 50 - view.safeAreaInsets.bottom, width: view.bounds.width, height: 50))
view.addSubview(toolBar)

The above code doesn't anchor the toolBar at the bottom of the safeAreaLayoutGuide and disappears on rotate.

UPDATE:

Attempting @Frankenstein's solution solves the problem when devices does not rotate. Upon rotation, the warning surfaces again. See gif:

enter image description here

Koh
  • 2,687
  • 1
  • 22
  • 62

2 Answers2

0

(Xcode 14.x + iOS 16.x in Simulator)

The problem is caused when the frame is set "too low" in the UIToolbar.

If the UIToolBarButton is "too tall", then the warnings go off.

See sample: the provided height should avoid warnings (and rotate on iPhone 14 Plus simulator).

Set it lower than 16, and you get warnings. Set it below 13, and you get twice as many warnings, because each button will trigger LayoutConstraints.

However, these appear to be warnings, the toolBar aways does display the buttons properly.

(What do for a more realistic scenario with toolbar button heights, I'm open to suggestions... I just decided I really needed to know what triggers the area, so I tested this by hand).

NOTE: setting the height constraint higher does not prevent this.

let toolBar = UIToolbar(frame: CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width, height: 16))
var items = [UIBarButtonItem]()

//pretty generic sample buttons
items.append(UIBarButtonItem(barButtonSystemItem: .save, target: nil, action: #selector(showCalendarChooser)))
//height: 13

items.append(UIBarButtonItem(image:UIImage(systemName:"calendar.badge.plus"), style: .plain, target: self, action: #selector(showCalendarChooser)))
//height: 16

// THIS IS WHERE THE WARNINGS GO OFF
toolBar.setItems(items, animated: false) 

// not the greatest choices, but you get the idea...
toolBar.translatesAutoresizingMaskIntoConstraints = false
let guide = self.view.safeAreaLayoutGuide
let margins = view.layoutMarginsGuide
toolBar.trailingAnchor.constraint(equalTo: margins.trailingAnchor).isActive = true
toolBar.leadingAnchor.constraint(equalTo: margins.leadingAnchor).isActive = true
toolBar.bottomAnchor.constraint(equalTo: guide.bottomAnchor).isActive = true
toolBar.heightAnchor.constraint(equalToConstant: 44).isActive = true
// this being 16+ doesn't prevent warnings with the sample buttons

navigationController?.setToolbarHidden(false, animated: true)
benc
  • 1,381
  • 5
  • 31
  • 39
-1

UIToolbar seems to work pretty specifically. You need to first unhide the toolbar of the navigation controller and then set the toolbarItems of the ViewController. There's the code:

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        setupToolbar()
    }

    func setupToolbar() {
        navigationController?.isToolbarHidden = false

        toolbarItems = [
            UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil),
            UIBarButtonItem(title: "Some Item", style: .plain, target: nil, action: nil),
            UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil)]
    }
}
Frankenstein
  • 15,732
  • 4
  • 22
  • 47
  • Your code almost works. When there is no rotation of the device, it does remove the warning. However when device rotates, and then navigates to the said ViewController, the warning surfaces again. See update – Koh May 09 '20 at 04:31