Как предотвратить UINavigationBar от блокировки касаний на просмотр
У меня есть UIView
, который частично застрял под UINavigationBar
на UIViewController
, который находится в полноэкранном режиме. 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 (или, если это возможно).
Ответы
Ответ 1
Подкласс UINavigationBar и переопределить - (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
так, чтобы он возвращал NO
для прямоугольника, где вид, который вы хотите получить касания, и 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;
}
В полноэкранном режиме просмотраController, где у вас есть вид за навигационной панелью, добавьте эти строки в viewDidLoad
UINavigationBarSubclass *navBar =
(UINavigationBarSubclass*)self.navigationController.navigationBar;
[navBar.viewsToIgnoreTouchesFor addObject:self.buttonBehindBar];
Обратите внимание: это не будет посылать касания к навигационной панели, то есть если вы добавите представление, которое находится за кнопками на navBar, кнопки на navBar не получат штрихов.
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)
}
Подробнее о pointInside:withEvent:
см. documentation.
Также, если pointInside:withEvent:
не работает так, как вам нужно, возможно, стоит попробовать выполнить код выше в hitTest:withEvent:
.
Ответ 2
Здесь версия, которая не требует установки определенных представлений, которые вы хотите включить внизу. Вместо этого он пропускает любое касание, кроме случаев, когда это касание происходит в пределах UIControl
или ракурса с 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)
}
}
Ответ 3
Быстрое решение для вышеупомянутого ответа Цель 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]
}
Счастливого прикосновения!