Mustard
Mustard

Reputation: 377

iOS11 UIToolBar Contentview

In iOS 11 buttons and text field are unresponsive being subviews of UIToolBar. Comparing view hierarchy to iOS 10 we see there is a _UIToolBarContentView over all subview of UIToolBar.

For instance, this new layout of the UIToolBar breaks slacktextviewcontroller https://github.com/slackhq/SlackTextViewController/issues/604

Need a solution working in iOS 10/11.

Upvotes: 35

Views: 6040

Answers (6)

Vicente Garcia
Vicente Garcia

Reputation: 6380

In Swift with autolayout and code only, what worked for me was to do layout as malex mentions just before adding items, but after setting constraints.

  • Instantiate your toolbar
  • Add it to your view
  • Add constraints

    toolbar.layoutIfNeeded()

    toolbar.setItems([... (your items)], animated: true)

Upvotes: 0

voromax
voromax

Reputation: 3389

The new UIToolbar object actively uses layout based on constraints, so it is better to override - (void)updateConstraints method. To present custom views over UIToolbar object it is better to subclass it and add custom container view:

- (UIView *)containerView
{
    if (_containerView) {
        return _containerView;
    }
    _containerView = [[UIView alloc] initWithFrame:self.bounds];
    _containerView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
    return _containerView;
}

Now you can safely add your custom views to the container view. To make the custom views responsive we need change the order of toolbar subviews after the constraints update:

- (void)updateConstraints
{
    [super updateConstraints];

    [self bringSubviewToFront:self.containerView];
}

Note, that if you are using UINavigationController with custom toolbar, you should force it to update its layout before adding your custom subviews.

Upvotes: 0

dk_checkitPortal
dk_checkitPortal

Reputation: 126

I have solved this problem in my case. I rewrite the layoutSubviews method in subclass of UIToobar and change the userInteractionEnable of _UIToolbarContentView into NO.

- (void)layoutSubviews {
    [super layoutSubviews];


    NSArray *subViewArray = [self subviews];

    for (id view in subViewArray) {
        if ([view isKindOfClass:(NSClassFromString(@"_UIToolbarContentView"))]) {
            UIView *testView = view;
            testView.userInteractionEnabled = NO;
         }
     }

}

Upvotes: 3

hengyu
hengyu

Reputation: 76

You can just use the hitTest(_:with:) method.

  1. First, create a property contentView in UIToolbar:

    open private(set) var contentView: UIView = UIView()
    
  2. Then, make the contentView's frame the same as the UIToolbar's. For example:

    contentView.frame = bounds
    contentView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
    addSubview(contentView)
    
  3. Finally, override the hitTest(_:with:) method:

    open override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
        if self.point(inside: point, with: event) {
            if let hitTestView = contentView.hitTest(point, with: event) {
                return hitTestView
            } else {
                return self
            }
        } else {
            return nil
        }
    }
    

In this situation, if you want to customize a toolbar by simply adding additional views, you should add them to the contentView so they will be positioned appropriately.

Upvotes: 1

malex
malex

Reputation: 10096

To solve the problem for iOS11 (compatible with lower versions) you only need to make layoutSubview right after UIToolBar was added as a subview to UI hierarchy.

In this case _UIToolbarContentView lowers to the first subview of UIToolBar, and you can add all your subviews higher as before.

For example in ObjC,

    UIToolbar *toolbar = [UIToolbar new];
    [self addSubview: toolbar];
    [toolbar layoutIfNeeded];

    <here one can add all subviews needed>

The same problem happens with slacktextviewcontroller

Upvotes: 35

user3077369
user3077369

Reputation: 9

There is an odd way to do it.

[self.textInputbar sendSubviewToBack:[self.textInputbar.subviews lastObject]];

Upvotes: -2

Related Questions