Отключите прикосновения к фону UIView, чтобы кнопки на нижних экранах можно было нажимать

В основном, это моя иерархия представлений (и я прошу прощения, если это трудно читать... Я новичок здесь, поэтому я с радостью принимаю предложения)


--AppControls.xib
------- (UIView) ControlsView
----------------- (UIView) TopBar
----------------- -------------- btn1, btn2, btn3
----------------- UIView) BottomBar
----------------- -------------- slider1 btn1, btn2
--PageContent.xib
----------------- (UIView) ContentView
----------------- -------------- btn1, btn2, btn3
----------------- -------------- (UIImageView) FullPageImage


Моя ситуация заключается в том, что я хочу скрыть и показать элементы управления при нажатии в любом месте на PageContent, который не является кнопкой, и чтобы элементы управления отображались, как в iPhone Video Player. Тем не менее, когда элементы управления отображаются, я все еще хочу иметь возможность нажимать кнопки на PageContent.

У меня все это работает, кроме последней части. Когда элементы управления показывают, фон элементов управления получает события касания вместо представления ниже. А отключение взаимодействия с пользователем в ControlsView отключает его для всех его дочерних элементов.

Я попытался переопределить HitTest на моем подклассе ControlsView следующим образом, который я нашел в аналогичном посте:

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event{
UIView *hitView = nil;
NSArray *subviews = [self subviews];
int subviewCount = [subviews count];
for (int subviewIndex = 0; !hitView && subviewIndex < subviewCount; subviewIndex++){
hitView = [[subviews objectAtIndex:subviewIndex] hitTest:point withEvent:event];
}   
return hitView;
}

Тем не менее, в этот момент мой ползунок не работает, как и большинство других кнопок, и на самом деле, все начинает становиться странным.

Итак, мой вопрос вкратце: как я могу позволить всем подпредставлениям вида иметь сенсорные события, в то время как фон суперпредставления не щелкает мышью, и кнопки на представлениях ниже могут получать сенсорные события.

Спасибо!

4 ответа

Решение

Ты рядом. Не переопределять -hitTest:withEvent:, К тому времени, когда вызывается, диспетчер событий уже решил, что ваше поддерево иерархии владеет событием и не будет искать в другом месте. Вместо этого переопределите -pointInside:withEvent:, который вызывается ранее в конвейере обработки событий. Это то, как система спрашивает: "Эй, взгляд, НИКТО в вашей иерархии реагирует на событие в этот момент?". Если вы говорите "НЕТ", обработка события продолжается ниже видимого стека.

Согласно документации, реализация по умолчанию просто проверяет, находится ли точка в границах представления вообще.

Ваша стратегия состоит в том, чтобы сказать "да", когда любое из ваших подпредставлений находится по этой координате, но сказать "нет", когда прикосновение коснется фона.

- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
{
    for (UIView * view in [self subviews]) {
        if (view.userInteractionEnabled && [view pointInside:[self convertPoint:point toView:view] withEvent:event]) {
            return YES;
        }
    }
    return NO;
}

Благодаря @Ben Zutto, решение Swift 3:

override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
    for view in self.subviews {
        if view.isUserInteractionEnabled, view.point(inside: self.convert(point, to: view), with: event) {
            return true
        }
    }

    return false
}

Небольшой вариант ответа Бена, имеющий дело с детьми, которые выходят за пределы их родителей. Если clipChildren имеет значение YES, это не вернет YES для точек, которые находятся вне основного элемента управления, но внутри какого-либо дочернего элемента. если clipChildren НЕТ, это то же самое, что и у Бена.

- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
{
    BOOL clipChildren = YES;

    if (!clipChildren || [super pointInside:point withEvent:event]) {
        for (UIView * view in [self subviews]) {
            if (view.userInteractionEnabled && [view pointInside:[self convertPoint:point toView:view] withEvent:event]) {
                return YES;
            }
        }
    }
    return NO;
}

Другой подход может заключаться в том, чтобы иметь невидимую полноэкранную кнопку позади всего остального и предпринимать соответствующие действия при ее нажатии.

Другие вопросы по тегам