How to prevent UINavigationBar from blocking clicks on viewing

I have a UIView that is partially stuck under a UINavigationBar on a UIViewController that is in full screen. UINavigationBar blocks the touch of this view for the part that it covers. I would like to be able to unlock these touches for the mentioned performance and skip them. I have subclassed the UINavigationBar with the following:

 - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event { UIView *view = [super hitTest:point withEvent:event]; if (view.tag == 399) { return view; } else { return nil; } } 

... where I am marked by the view under consideration with number 399. Is it possible to go through the strokes for this view without a pointer to it (for example, as I noted above)? A bit confused how to do this work using the hittest method (or, if possible).

+10
source share
3 answers

Subclass UINavigationBar and override - (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event so that it returns NO for the rectangle where the view you want to get touches is, and YES otherwise.

For instance:

Subclass of UINavigationBar.h:

 @property (nonatomic, strong) NSMutableArray *viewsToIgnoreTouchesFor; 

Subclass of UINavigationBar.m:

 - (NSMutableArray *)viewsToIgnoreTouchesFor { if (!_viewsToIgnoreTouchesFor) { _viewsToIgnoreTouchesFor = [@[] mutableCopy]; } return _viewsToIgnoreTouchesFor; } - (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event { BOOL pointInSide = [super pointInside:point withEvent:event]; for (UIView *view in self.viewsToIgnoreTouchesFor) { CGPoint convertedPoint = [view convertPoint:point fromView:self]; if ([view pointInside:convertedPoint withEvent:event]) { pointInSide = NO; break; } } return pointInSide; } 

In full screen view of the viewDidLoad , where you have a view behind the navigation bar, add these lines to viewDidLoad

 UINavigationBarSubclass *navBar = (UINavigationBarSubclass*)self.navigationController.navigationBar; [navBar.viewsToIgnoreTouchesFor addObject:self.buttonBehindBar]; 

Please note: this will not send touches to the navigation bar, that is, if you add a view that is behind the buttons on the navBar, the buttons on the navBar will not receive strokes.

Swift:

 var viewsToIgnore = [UIView]() override func point(inside point: CGPoint, with event: UIEvent?) -> Bool { let ignore = viewsToIgnore.first { let converted = $0.convert(point, from: self) return $0.point(inside: converted, with: event) } return ignore == nil && super.point(inside: point, with: event) } 

More on pointInside:withEvent:

see documentation .

Also, if pointInside:withEvent: does not work as you need, it might be worth trying the code above in hitTest:withEvent:

+7
source

Here is a version that does not require the installation of certain views that you want to include below. Instead, it skips any touch, except when that touch occurs within a UIControl or view with a UIGestureRecognizer .

 import UIKit /// Passes through all touch events to views behind it, except when the /// touch occurs in a contained UIControl or view with a gesture /// recognizer attached final class PassThroughNavigationBar: UINavigationBar { override func point(inside point: CGPoint, with event: UIEvent?) -> Bool { guard nestedInteractiveViews(in: self, contain: point) else { return false } return super.point(inside: point, with: event) } private func nestedInteractiveViews(in view: UIView, contain point: CGPoint) -> Bool { if view.isPotentiallyInteractive, view.bounds.contains(convert(point, to: view)) { return true } for subview in view.subviews { if nestedInteractiveViews(in: subview, contain: point) { return true } } return false } } fileprivate extension UIView { var isPotentiallyInteractive: Bool { guard isUserInteractionEnabled else { return false } return (isControl || doesContainGestureRecognizer) } var isControl: Bool { return self is UIControl } var doesContainGestureRecognizer: Bool { return !(gestureRecognizers?.isEmpty ?? true) } } 
+2
source

Quick solution for the above answer Goal C.

 class MyNavigationBar: UINavigationBar { var viewsToIgnoreTouchesFor:[UIView] = [] override func point(inside point: CGPoint, with event: UIEvent?) -> Bool { var pointInside = super.point(inside: point, with: event) for each in viewsToIgnoreTouchesFor { let convertedPoint = each.convert(point, from: self) if each.point(inside: convertedPoint, with: event) { pointInside = false break } } return pointInside } } 

Now set the views whose touches you want to capture under the navigation bar, as shown below, from the viewDidLoad method or any other suitable place in the code

 if let navBar = self.navigationController?.navigationBar as? MyNavigationBar { navBar.viewsToIgnoreTouchesFor = [btnTest] } 

Happy touch!

+1
source

All Articles