Reputation: 377
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
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.
Add constraints
toolbar.layoutIfNeeded()
toolbar.setItems([... (your items)], animated: true)
Upvotes: 0
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
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
Reputation: 76
You can just use the hitTest(_:with:)
method.
First, create a property contentView
in UIToolbar
:
open private(set) var contentView: UIView = UIView()
Then, make the contentView
's frame the same as the UIToolbar
's. For example:
contentView.frame = bounds
contentView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
addSubview(contentView)
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
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
Reputation: 9
There is an odd way to do it.
[self.textInputbar sendSubviewToBack:[self.textInputbar.subviews lastObject]];
Upvotes: -2