Ios 如何防止UINavigationBar阻止视图上的接触

Ios 如何防止UINavigationBar阻止视图上的接触,ios,iphone,cocoa-touch,uiview,Ios,Iphone,Cocoa Touch,Uiview,我有一个UIView部分卡在全屏模式下的UIView控制器上的UINavigationBar下面。UINavigationBar会阻止该视图覆盖部分的接触。我想能够解除这些接触说的看法,并让他们通过。我使用以下内容对UINavigationBar进行了子类化: - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event { UIView *view = [super hitTest:point withEvent:event

我有一个
UIView
部分卡在全屏模式下的
UIView控制器上的
UINavigationBar
下面。
UINavigationBar
会阻止该视图覆盖部分的接触。我想能够解除这些接触说的看法,并让他们通过。我使用以下内容对UINavigationBar进行了子类化:

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
    UIView *view = [super hitTest:point withEvent:event];

    if (view.tag == 399)
    {
        return view;
    }
    else
    {
        return nil;
    }
}

…在这里,我用数字399标记了有问题的视图。是否可以在没有指向该视图的指针的情况下通过该视图的触摸(例如,就像我在上面标记的那样)?我对如何使用hittest方法(或者如果可能的话)实现这一点有点困惑。

子类UINavigationBar并覆盖
-(BOOL)pointInside:(CGPoint)pointwithevent:(UIEvent*)event
,这样它会返回
NO
,对于您想要接收的视图所在的rect,则返回
YES

例如:

UINavigationBar子类h:

@property (nonatomic, strong) NSMutableArray *viewsToIgnoreTouchesFor; 
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;
}
在导航栏后面有视图的全屏viewController中,将这些行添加到
viewDidLoad

UINavigationBarSubclass *navBar = 
(UINavigationBarSubclass*)self.navigationController.navigationBar;
[navBar.viewsToIgnoreTouchesFor addObject:self.buttonBehindBar];
请注意:这不会向导航栏发送触摸,这意味着如果您在导航栏按钮后面添加视图,导航栏上的按钮将不会接收触摸

斯威夫特:

有关
pointInside:withEvent:


另外,如果
pointInside:withEvent:
无法按您的意愿工作,则可能值得在
hitTest:withEvent:
中尝试上面的代码。

这里有一个版本,它不需要设置要在下面启用的特定视图。相反,它允许任何触摸通过,除非该触摸发生在
UIControl
或带有
UIGestureRecognitizer
的视图中

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

Swift上述目标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
    }
}

现在,从viewDidLoad方法或您在代码中的任何适用位置,在导航栏下设置要捕捉其触摸的视图,如下所示

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

我修改了Tricky的解决方案,将其作为一个
扩展使用。很好地解决了这个问题。将此代码添加到代码库后,所有视图都将能够捕获屏幕顶部的单击

也张贴了这个改动


考虑隐藏导航条,如果您想在它下面的视图响应用户触摸。这是一个奇怪的情况,在这里我不能@ KudoCCWill导航栏下的视图如果“代码> >(UIVIEW*)HITETHECT:(CGPOOT)点WiTeVe:事件
始终返回nil。您无法通过标记属性获取导航栏下的
ui视图
,因为
ui视图
不在导航栏的层次结构中。可能需要一个指针来查看触摸是否在视图范围内@库多克特这正是我最后所做的。谢谢。太好了,也为我工作了!虽然我认为写一个分类比较简单。你能分享你完整的
customNavBar
代码和它的初始化代码吗。@ShashankAgarwal我没有我可以分享的格式。正如Bogdan Weidmann所建议的那样,我的答案中的代码片段应该可以很容易地创建一个类别(或Swift的扩展)。
pointInside:withEvent:
在与类一起使用时会触发,但不会让触动传递。简单而完美。感谢您与Swift 5完美配合。非常感谢。
if let navBar = self.navigationController?.navigationBar as? MyNavigationBar {
    navBar.viewsToIgnoreTouchesFor = [btnTest]
}
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
extension UINavigationBar {
    open 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
    }
}

private 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)
    }
}