ios 11 - UIButton inside UIBarButtonItem causes an

2019-05-02 17:11发布

问题:

I've a known issue with adding a UIButton into UIBarButtonItem. I've tried to add auto layout constraints as suggested in stackoveflow but I'm getting an error described as below.

UIButton *sortButton = [UIButton buttonWithType:UIButtonTypeCustom];
[sortButton setFrame:CGRectMake(10, 0, 100, 30)];
[sortButton setTitle:@"Sort" forState:UIControlStateNormal];
[[sortButton titleLabel] setFont:[UIFont boldSystemFontOfSize:13]];
[sortButton setBackgroundImage:[UIImage imageNamed:@"backgroun-button.png"] forState:UIControlStateNormal];
[sortButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
[sortButton addTarget:self action:@selector(sortCollection:) forControlEvents:UIControlEventTouchUpInside];
[sortButton applyNavBarConstraints:100 height:30];

[self setSortCollection:item];

[self setToolbarItems:[NSArray arrayWithObjects:self.sortCollection, nil]];

Autolayout constraints:

- (void)applyNavBarConstraints:(CGFloat)width height:(CGFloat)height
{
    if (width == 0 || height == 0) {
        return;
    }

    NSLayoutConstraint* w = [self.widthAnchor constraintEqualToConstant:width];
    NSLayoutConstraint * h = [self.heightAnchor constraintEqualToConstant:height];

    [w setActive:YES];
    [h setActive:YES];
}

Stack trace:

[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:0x608000280af0 h=--& v=--& UIToolbar:0x7f85ca1327e0.width == 768   (active)>",
    "<NSLayoutConstraint:0x6000002826c0 _UIToolbarContentView:0x7f85ca1307b0.trailing == UIToolbar:0x7f85ca1327e0.trailing   (active)>",
    "<NSLayoutConstraint:0x600000282580 H:|-(0)-[_UIToolbarContentView:0x7f85ca1307b0]   (active, names: '|':UIToolbar:0x7f85ca1327e0 )>",
    "<NSLayoutConstraint:0x600000285410 H:|-(0)-[_UIButtonBarStackView:0x7f85ca135660]   (active, names: '|':_UIToolbarContentView:0x7f85ca1307b0 )>",
    "<NSLayoutConstraint:0x600000285460 _UIButtonBarStackView:0x7f85ca135660.trailing == _UIToolbarContentView:0x7f85ca1307b0.trailing   (active)>",
    "<NSLayoutConstraint:0x600000285500 UIButton:0x7f85ca131b90'Sortieren'.width == 100   (active)>",
    "<NSLayoutConstraint:0x608000280ff0 'UISV-canvas-connection' UILayoutGuide:0x6000001ba240'UIViewLayoutMarginsGuide'.leading == UIButton:0x7f85ca131b90'Sortieren'.leading   (active)>",
    "<NSLayoutConstraint:0x608000280d70 'UISV-canvas-connection' UILayoutGuide:0x6000001ba240'UIViewLayoutMarginsGuide'.trailing == UIButton:0x7f85ca131b90'Sortieren'.trailing   (active)>",
    "<NSLayoutConstraint:0x600000284f10 'UIView-leftMargin-guide-constraint' H:|-(0)-[UILayoutGuide:0x6000001ba240'UIViewLayoutMarginsGuide'](LTR)   (active, names: '|':_UIButtonBarStackView:0x7f85ca135660 )>",
    "<NSLayoutConstraint:0x600000285190 'UIView-rightMargin-guide-constraint' H:[UILayoutGuide:0x6000001ba240'UIViewLayoutMarginsGuide']-(0)-|(LTR)   (active, names: '|':_UIButtonBarStackView:0x7f85ca135660 )>"
)

Will attempt to recover by breaking constraint 
<NSLayoutConstraint:0x600000285500 UIButton:0x7f85ca131b90'Sortieren'.width == 100   (active)>

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

回答1:

The bar button is constrained to the left and right, so the width constraint has to be broken. You can fix this by adding a flexible space to the toolbar. This allows you to contraint the width of the button and have the flexible space fill the rest.

Obj C:

UIBarButtonItem *flexItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:self action:nil];
[self setToolbarItems:[NSArray arrayWithObjects:self.sortCollection, flexItem, nil]];

Swift:

let flexItem = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil)
toolbar.setItems([barButtonItem, flexItem], animated: false)


回答2:

I've had this issue also since iOS11. I solved it by creating a custom view with it's own XIB and place a button in it. Then you can easily set the constraints within InterfaceBuilder in the XIB. Then when you actually want to use it in a barbuttonitem, the code is very short:

self.customButtonView = [[[NSBundle mainBundle] loadNibNamed:NSStringFromClass([CustomButtonView class]) owner:nil options:nil] objectAtIndex:0];
[self.customButtonView.button addTarget:self action:@selector(buttonAction) forControlEvents:UIControlEventTouchUpInside];
self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:self.customButtonView];

Just don't forget the second line where you actually have to assign a selector for the button (which is an IBOutlet in the custom view of course).



回答3:

Use this simple way to create button with auto layout. Create button will have with flexible width according to size of the UIView. There is no need to add extra constraints.

UIButton *sortButton = [UIButton buttonWithType:UIButtonTypeCustom];
    [sortButton setFrame:CGRectMake(10, 0, self.view.frame.size.width - 10, 30)];