iOS11 UIToolBar Contentview
Asked Answered
C

6

35

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.

Carousal answered 8/9, 2017 at 2:13 Comment(5)
Did you solve this issue?Recurve
Its weird but I experienced same issue but some case its not visibleZobkiw
I tried editing the question for you to take it out of closed status. The mods rejected my edits because I included an answer in the edits. They said I should just answer the question. I CAN'T answer the question if it is closed. Anyway, you should override the layoutSubviews method of your UIToolBar and either bring your unresponsive views to the front or send the last view to the back inside of that override.Photomechanical
This is actually very good question. No reason to be closed. It's takes time and research to find out the difference between iOS 10/11. Thanks for thatRosenzweig
I run into the same issue, too.I think the question is clear enough to state the issue.We should reopen this questionTurnpike
H
35

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

Herman answered 27/9, 2017 at 13:8 Comment(6)
What if we added the views in a interface builder ? any other solution other than send them to front ?Deplore
Thank you. Was facing the same problem.Desolation
I have added UIToolBar using storyboard, how do I solve this issue.Etherealize
@Etherealize When I try to add a textField or button into toolbar via xib, the element is automatically wrapped in BarButtonItem. So I have touch access to these element, also one can check that toolbarContentView is below these elements at work. Also if I add a button on viewDidLoad into toolbar from xib it also works fine on touch.Herman
My UIToolbar is part of a storyboard. The UIToolbar and its children (buttons) are referenced in a view controller. I'm unable to access them because at runtime they are obscured by _UIToolbarContentView I'm not sure how I should handle it. I'm not using a xib nor creating it in code.Marvismarwin
I have a view controller that uses a toolbar as its view. The solution in this answer worked for me up to iOS 12 and stopped working with iOS 13. My new solution is to override viewDidLayoutSubviews and invoke bringSubviewToFront:() on the toolbar.Kathykathye
Z
3

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;
         }
     }

}
Zooplankton answered 25/10, 2017 at 3:4 Comment(0)
L
1

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.

Lohengrin answered 3/10, 2017 at 13:59 Comment(0)
W
0

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.

Wivina answered 1/12, 2017 at 19:9 Comment(0)
G
0

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)

Gorilla answered 16/3, 2020 at 10:52 Comment(0)
H
-2

There is an odd way to do it.

[self.textInputbar sendSubviewToBack:[self.textInputbar.subviews lastObject]];
Hoenir answered 13/9, 2017 at 12:7 Comment(5)
What is "textInputbar" ?Carousal
The textInputbar is an object of UIToolbar.Hoenir
It is the solution described at githubHerman
Why be in context of SlackTextViewController only, it's a general problem related to UIToolBar. Anyway, it doesn't solve the issue even if I bring the lastObject to frontShortlived
@Shortlived Please see the working fix below. I've got the same issue.Herman

© 2022 - 2024 — McMap. All rights reserved.