Обнаружить, если некоторые UIView были затронуты среди других UIViews

У меня есть 3 UIViews, расположенных поверх одного большого uiview. Я хочу знать, затрагивает ли пользователь верхнюю и не заботится о других. У меня будет несколько кнопок во втором UIView и UITable в третьем UIView.

Проблема заключается в том, что я включаю userInteractionEngabled на первом представлении и это работает, но все остальные представления отвечают одинаково, даже если я отключу его. Если я отключу userInteractionEnabled на self.view, никто из них не ответит. Я также не могу определить, какой вид был затронут в методе делегатов touchsBegan.

Мой код:

UIView *aView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 320, 150)];
aView = userInteractionEnabled = YES;
[self.view addSubview:aView];

UIView *bView = [[UIView alloc] initWithFrame:CGRectMake(0, 150, 320, 50)];
bView.userInteractionEnabled = NO;
[self.view addSubview:bView];

-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
//This gets called for a touch anywhere
}

Ответ 1

Чтобы проверить, коснулся ли какой-либо вид внутри другого вида, вы можете использовать hitTest.

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event;

В вашей пользовательской реализации touchsBegan проверьте каждое касание в настройках. Точку для метода hitTest можно получить, используя

- (CGPoint)locationInView:(UIView *)view;

где представление представляет собой ваш superView (тот, который содержит другие представления).

EDIT: здесь быстрая пользовательская реализация:

-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { 
    CGPoint locationPoint = [[touches anyObject] locationInView:self];
    UIView* viewYouWishToObtain = [self hitTest:locationPoint withEvent:event];
}

Я надеюсь, что это было полезно, Пол

Ответ 2

В моем случае UIView, который я хотел найти, не был возвращен hitTest. Но следующий код работал лучше:

    CGPoint locationPoint = [[touches anyObject] locationInView:self.view];
    CGPoint viewPoint = [myImage convertPoint:locationPoint fromView:self.view];
    if ([myImage pointInside:viewPoint withEvent:event]) {
       do something
    }

Ответ 3

Это также можно сделать следующим образом.

Сначала найдите местоположение касания в представлении, о котором вы заботитесь:

CGPoint location = [touches.anyObject locationInView:viewYouCareAbout];

Затем посмотрите, находится ли точка в пределах видимости:

BOOL withinBounds = CGRectContainsPoint(viewYouCareAbout.bounds, location);

Затем, если он находится в пределах границ, выполните свое действие.

Это можно сделать с помощью одного оператора, который, например, может использоваться непосредственно внутри оператора if:

CGRectContainsPoint(viewYouCareAbout.bounds, [touches.anyObject locationInView:viewYouCareAbout])

Ответ 4

быстрый код:

 override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
        if let touch = touches.first {
            if touch.view == self.view {
                self.hideInView()
            } else {
                return
            }

        }
    }

Ответ 5

В touchhesBegan проверьте, что затронутое представление - это тот, который вам нужен, решил проблему для меня, когда я попытался определить, коснулся ли пользователь определенного ImageView.. p >

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {

    if ([touch view] ==  imageView ) {
        ... your code to handle the touch
    }
    ...

Если вы настроите контент для диалогов вида, обязательно измените размер представления, иначе он распознает событие касания даже в области, где есть такое содержимое в представлении. В моем случае, когда я попытался сохранить пропорции изображения с UIViewContentModeScaleAspectFit, хотя изображение было отрегулировано по мере необходимости в областях "белых пробелов", также попадало событие касания.

Ответ 6

Моя быстрая версия для проверки этого касания имела место в infoView:

override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
    guard let touch = touches.first, infoView.bounds.contains(touch.location(in: infoView))  else { return }
    print("touchesBegan") // Replace by your own code
} // touchesBegan

Ответ 7

Это сработало для меня:

func respondToGesture(gesture: UIGestureRecognizer) {
    let containsPoint = CGRectContainsPoint(targetView.bounds, gesture.locationInView(targetView))
}

Ответ 8

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

Одним из решений этой проблемы является изменение макета иерархии представлений, чтобы ваше представление находилось в пределах родительского представления. Если по каким-либо причинам необходимо сохранить существующий макет, вы можете изменить поведение родительского представления для проверки попаданий, чтобы оно не игнорировало события касания. Это может быть сделано путем переопределения - (UIView *) hitTest: withEvent: метод родительского класса представления

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {

    // Convert the point to the target view coordinate system.
    // The target view isn't necessarily the immediate subview
    CGPoint pointForTargetView = [self.targetView convertPoint:point fromView:self];

    if (CGRectContainsPoint(self.targetView.bounds, pointForTargetView)) {

        // The target view may have its view hierarchy,
        // so call its hitTest method to return the right hit-test view
        return [self.targetView hitTest:pointForTargetView withEvent:event];
    }

    return [super hitTest:point withEvent:event]; }

Другие возможные причины этой проблемы включают в себя:

Свойство userInteractionEnabled вашего представления или любого из его родительских представлений имеет значение NO. Приложение вызвало свой метод beginIgnoringInteractionEvents без соответствующего вызова его метода endIgnoringInteractionEvents. Убедитесь, что для свойства userInteractionEnabled установлено значение YES, а приложение игнорирует пользовательские события, если вы хотите, чтобы представление было интерактивным.

Если ваш вид не получает сенсорные события во время анимации, это потому, что методы анимации UIView обычно отключают сенсорные события во время анимации. Вы можете изменить это поведение, настроив соответствующий параметр UIViewAnimationOptionAllowUserInteraction при запуске анимации UIView.

Другое решение

Добавьте свою собственную пользовательскую реализацию в представление

class CustomView: YourParentView {

    override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {

        super.hitTest(point, with: event)
        return overlapHitTest(point: point, withEvent: event)
    }
}


extension UIView {

    func overlapHitTest(point: CGPoint, withEvent event: UIEvent?) -> UIView? {

        // 1
        if !self.isUserInteractionEnabled || self.isHidden || self.alpha == 0 {

            return nil
        }

        // 2
        var hitView: UIView? = self
        if !self.point(inside: point, with: event) {

            if self.clipsToBounds {

                return nil
            } else {

                hitView = nil
            }
        }

        // 3
        for subview in self.subviews.reversed() {

            let insideSubview = self.convert(point, to: subview)
            if let sview = subview.overlapHitTest(point: insideSubview, withEvent: event) {
                return sview
            }
        }
        return hitView
    }
}