UIToolbar issue in iOS 13

I'm adding one UIBarButtonItem with a custom view. The custom view uses autolayout to layout its inner components. Is this supported in iOS 13? This issue does not happen when the same code is run against an iOS 12 device. Adding just one UIBarButtonItem which has a custom view causes this to happen. None of the UIViews listed in the conflict below are mine (e.g: _UIToolbarContentView) and they're clearly private UIView classes.

I'm not even sure how to debug this issue. I've set the UIToolbar's translatesAutoresizingMaskIntoConstraints to NO, but this issue still happens.

Setting a breakpoint on UIViewAlertForUnsatisfiableConstraints doesn't help because the stack trace doesn't give any valuable clues as it's all iOS private classes


Any suggestions?


Thanks,

Neal


[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:0x2815ebe30 h=--& v=--& _UIToolbarContentView:0x100d6f8e0.width == 0 (active)>",

"<NSLayoutConstraint:0x2815ea120 H:|-(0)-[_UIButtonBarStackView:0x100d31080] (active, names: '|':_UIToolbarContentView:0x100d6f8e0 )>",

"<NSLayoutConstraint:0x2815ea170 _UIButtonBarStackView:0x100d31080.trailing == _UIToolbarContentView:0x100d6f8e0.trailing - 16 (active)>"

)


Will attempt to recover by breaking constraint

<NSLayoutConstraint:0x2815ea170 _UIButtonBarStackView:0x100d31080.trailing == _UIToolbarContentView:0x100d6f8e0.trailing - 16 (active)>


Make a symbolic breakpoint at UIViewAlertForUnsatisfiableConstraints to catch this in the debugger.

  • YUP! That's what it took, let toolbar = UIToolbar(frame: CGRectMake(0, 0, UIScreen.main.bounds.size.width, 20) toolbar.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint.activate([ . . . ])

    The frame is wider than it needs to be (constraints use safe area layout guides), but it seems to work as intended with autolayout.

Add a Comment

Accepted Reply

I solved this by not using a UIToolbar. UIStackView was all that I really needed.

Replies

I solved this by not using a UIToolbar. UIStackView was all that I really needed.

I am also troubled by this problem.


I heard that you used UIStackView. Can you put UIBarButtonItem in UIStackView?


The UIBarButtonItem reference states that the button is specialized for the placement of UIToolBar and UITabBar.


https://developer.apple.com/documentation/uikit/uibarbuttonitem


The following code I tried didn't work.


Please give us advice on what kind of code you have written.


  // note
  UIBarButtonItem *noteButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem: UIBarButtonSystemItemCompose target:self action:@selector(noteViewAction)];

  // camera
  UIBarButtonItem *cameraButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem: UIBarButtonSystemItemCamera target:self action:@selector(cameraViewAction)];

  // stackView
  UIStackView *stackview;
  stackview = [[UIStackView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 300.0f, 44.0f)];
  stackview.axis = UILayoutConstraintAxisHorizontal;
  stackview.alignment = UIStackViewAlignmentFill;
  stackview.distribution = UIStackViewDistributionFill;
  stackview.spacing = 20;

  [stackview addArrangedSubview: noteButton.customView];
  [stackview addArrangedSubview: cameraButton.customView];

  UIBarButtonItem *stackViewBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:stackview];

  self.navigationItem.leftBarButtonItem = stackViewBarButtonItem;

>I heard that you used UIStackView. Can you put UIBarButtonItem in UIStackView?


Hi Mario25,

You can't put a UIBarButtonItem in a UIStackView, only UIViews or UIControls, but in my case that was fine, because all of our UIBarButtonItems used real UIButtons and UIControls as custom views. So after migrating to a UIStackview we saved work because we didn't have to create UIBarButtonItem wrappers. Your use case is different in that you still use UIBarButtonItems.

Hi Mario25,


I was still stuck at this as well. Did you find anything in regards getting rid of the Constraints problem?

My code is still objective-c:


I did change the

[[UIToolbar alloc] initWithFrame:CGRectZero];


to

[[UIToolbar alloc] initWithFrame:CGRectMake(0, 0, 100, 100)];


while loading the view. This "fixed" the issue for me


Regards

Marcus

This also fixed the issue for me. I was only having this issue on ios 13. Not sure why this is happening.

I have noticed this as well.

This fixed it for me also. Thanks for posting.

Adding the frame size also fixed the problem for me (Using swift). But I still don't know why...


Here is my code for example, adding a tool bar for an inputView. I use a pickerView inside an inputView to choose a country for a textField. Please ask UX guy why I can't use a picker view directly... The textField was created in the storyboard and fixed with autolayout constraints:


    //MARK:- ConfigurarPickerView
    //Add the inputView for the textField
    func configurarPickerView() {
        paisPicker = UIPickerView()
        paisPicker?.delegate = self
        paisTextField.inputView = paisPicker
    }

    //MARK:- ConfigurarPickerToolBar
    //Add done & cancel buttons to the inputView toolBar
    func configurarPickerToolBar(){
     //If I don't set the frame size I'll get the LayoutConstraints warning. So I use 100x100 for no reason.
        paisPickerToolBar = UIToolbar(frame: CGRect(x: 0,
                                                    y: 0,
                                                    width: 100,
                                                    height: 100))

        paisPickerToolBar?.barStyle = UIBarStyle.default
        paisPickerToolBar?.isTranslucent = true
        paisPickerToolBar?.tintColor = UIColor(named: "ColorNaranja")
        let doneButton = UIBarButtonItem(title: "Hecho",
                                         style: UIBarButtonItem.Style.done,
                                         target: self,
                                         action: #selector(donePressed))
        let cancelButton = UIBarButtonItem(title: "Cancelar",
                                           style: UIBarButtonItem.Style.plain,
                                           target: self,
                                           action:  #selector(cancelPressed))
        let spaceButton = UIBarButtonItem(barButtonSystemItem: UIBarButtonItem.SystemItem.flexibleSpace,
                                          target: nil,
                                          action: nil)
        paisPickerToolBar?.setItems([cancelButton, spaceButton, doneButton], animated: false)

        paisPickerToolBar?.isUserInteractionEnabled = true
        paisPickerToolBar?.sizeToFit()
 
        paisTextField.inputAccessoryView = paisPickerToolBar
    }

Just to clarify, to get it to work in IOS 13 we can no longer use the UINavigationItem.leftBarButtonItems property or UINavigationItem.rightBarButtonItems property ?

both are UIBarButtonItem objects.


i.e. As per the doco

"When specifying buttons for a navigation item, you must use

UIBarButtonItem
objects. If you want to display custom views in the navigation bar, you must wrap those views inside a
UIBarButtonItem
object before adding them to the navigation item."
this worked for me:
do not use
Code Block
let toolbar = UIToolbar()
but
Code Block
let toolbar = UIToolbar(frame: CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width, height: 35))

from https://stackoverflow.com/questions/54284029/uitoolbar-with-uibarbuttonitem-layoutconstraint-issue
This solution worked for me:
Code Block swift
toolbar.setItems([item], animated: false)
toolbar.updateConstraintsIfNeeded()