iOS - переслати всі торкання через перегляд


141

У мене подання перекрите поверх багатьох інших поглядів. Я використовую в основному лише для виявлення деякої кількості дотиків до екрана, окрім того, я не хочу, щоб погляд зупиняв поведінку інших поглядів внизу, що є переглядами прокрутки і т. Д. Як я можу переслати всі торкання через цей вид перекриття? Це підканал UIView.


2
Ви вирішили свою проблему? Якщо так, то будь ласка, прийміть відповідь, щоб іншим було легше знайти рішення, оскільки я зіткнувся з тим же питанням.
Хушбу Дезай

ця відповідь працює добре stackoverflow.com/a/4010809/4833705
Lance Samaria

Відповіді:


121

Відключення взаємодії з користувачем було все, що мені потрібно!

Завдання-C:

myWebView.userInteractionEnabled = NO;

Швидкий:

myWebView.isUserInteractionEnabled = false

Це спрацювало в моїй ситуації, коли у мене був підвід кнопки. Я відключив взаємодію на підзагляді і кнопка працювала.
simple_code

2
У Swift 4,myWebView.isUserInteractionEnabled = false
Louis 'LYRO' Dupont

117

Для передачі дотиків із виду накладення до представлень, розташованих під ним, застосуйте такий спосіб у UIView:

Завдання-C:

- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event {
    NSLog(@"Passing all touches to the next view (if any), in the view stack.");
    return NO;
}

Швидкий 5:

override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
    print("Passing all touches to the next view (if any), in the view stack.")
    return false
}

3
У мене є те саме питання, але те, що я хочу, якщо я збільшую / обертаю / переміщую погляд жестом, то він повинен повернути так, а для відпочинку - він повинен повернутися ні, як я можу цього досягти?
Мінакші

@Minakshi - це зовсім інше питання, задайте нове запитання для цього.
Fattie

але пам’ятайте, що цей простий підхід НЕ МОЖЕ БУТИ КРАЇН і т. д. НА ВЕРШІ відповідного шару!
Fattie

56

Це стара тема, але вона з’явилася під час пошуку, тому я подумав, що додаю 2c. У мене є UIView, що охоплює субпрегляди, і хочу лише перехопити дотики, які потрапили в один із підпрезентацій, тому я змінив відповідь PixelCloudSt на

-(BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
{
    for (UIView* subview in self.subviews ) {
        if ( [subview hitTest:[self convertPoint:point toView:subview] withEvent:event] != nil ) {
            return YES;
        }
    }
    return NO;
}

1
Вам потрібно створити користувацький підклас UIView (або підклас будь-якого іншого підкласу UIView, наприклад UIImageView чи будь-якого іншого), і змінити цю функцію. По суті, підклас може містити абсолютно порожній "@ інтерфейс" у файлі .h, а функція, що знаходиться вище, - весь вміст "@implementation".
fresidue

Дякую, це саме те, що мені потрібно було пронести дотики через порожній простір мого UIView, щоб обробляти погляд ззаду. +100
Денні

40

Удосконалена версія відповіді @fresidue. Ви можете використовувати цей UIViewпідклас як прозорі види подання, що проходять поза його підпереглядом. Впровадження в ціль C :

@interface PassthroughView : UIView

@end

@implementation PassthroughView

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

@end

.

і в Свіфті:

class PassthroughView: UIView {
  override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
    return subviews.contains(where: {
      !$0.isHidden
      && $0.isUserInteractionEnabled
      && $0.point(inside: self.convert(point, to: $0), with: event)
    })
  }
}

ПОРАДА:

Скажіть, тоді у вас є велика панель «тримача», можливо, з видом столу позаду. Ви робите панель «власника» PassthroughView. Тепер він буде працювати, ви можете прокручувати таблицю "через" "тримач".

Але!

  1. Зверху на панелі "тримач" у вас є кілька міток чи піктограм. Не забувайте, звичайно, їх потрібно просто позначити Включеною взаємодією користувача!

  2. Зверху на панелі "тримача" у вас є кілька кнопок. Не забувайте, звичайно, вони повинні бути просто позначені як включена взаємодія користувача!

  3. Зверніть увагу , що кілька смутно, то «власник» сам по собі - вид ви використовуєте PassthroughViewна - повинні бути відзначені взаємодії з користувачем включений ON ! Це ON !! (Інакше код у PassthroughView просто ніколи не буде називатися.)


1
Я використав рішення для Swift. Він працює на ios 11, але кожен раз виходить з ладу на iOS 10. Показ поганого доступу. Будь-яка ідея?
Сумен

Ви врятували мої дні, це склалося для проходження дотиків до нижче UIViews.
AaoIi

1
Не забудьте також перевірити$0.isUserInteractionEnabled
Шон Тілен

7

У мене була схожа проблема з UIStackView (але це може бути будь-який інший вигляд). Моя конфігурація була такою: Перегляд контролера з контейнеромView та StackView

Це класичний випадок, коли у мене є контейнер, який потрібно було розмістити на задньому плані, з кнопками збоку. Для цілей компонування я включив кнопки в UIStackView, але тепер середня (порожня) частина перехоплення stackView торкається :-(

Що я зробив, це створити підклас UIStackView із властивістю, що визначає subView, до якого слід доторкнутися. Тепер будь-яке дотик до бічних кнопок (включених до масиву * viewsWithActiveTouch *) буде надано кнопкам, тоді як будь-яке дотик до стека перегляду ніде, крім цих поглядів, не буде перехоплене, і тому передається тому, що знаходиться під стеком вид.

/** Subclass of UIStackView that does not accept touches, except for specific subviews given in the viewsWithActiveTouch array */
class NoTouchStackView: UIStackView {

  var viewsWithActiveTouch: [UIView]?

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

    if let activeViews = viewsWithActiveTouch {
        for view in activeViews {
           if CGRectContainsPoint(view.frame, point) {
                return view

           }
        }
     }
     return nil
   }
}

6

Якщо представлення, на яке ви хочете переслати штрихи, не є підзаглядом / наглядом, ви можете встановити власну властивість у своєму підкласі UIView таким чином:

@interface SomeViewSubclass : UIView {

    id forwardableTouchee;

}
@property (retain) id forwardableTouchee;

Обов’язково синтезуйте його у вашому .m:

@synthesize forwardableTouchee;

А потім включіть таке в будь-який із своїх методів UIResponder, таких як:

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

    [self.forwardableTouchee touchesBegan:touches withEvent:event];

}

Де б ви не інстанціювали свій UIView, встановіть властивість forwardableTouchee у будь-який вид, на який ви хочете пересилати події:

    SomeViewSubclass *view = [[[SomeViewSubclass alloc] initWithFrame:someRect] autorelease];
    view.forwardableTouchee = someOtherView;

4

У Swift 5

class ThroughView: UIView {
    override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
        guard let slideView = subviews.first else {
            return false
        }

        return slideView.hitTest(convert(point, to: slideView), with: event) != nil
    }
}

4

Мені потрібно було передати дотики через UIStackView. UIView всередині був прозорим, але UIStackView вимагав усіх дотиків. Це працювало для мене:

class PassThrouStackView: UIStackView {

    override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
        let view = super.hitTest(point, with: event)
        if view == self {
            return nil
        }
        return view
    }
}

Усі влаштованіSubviews все ще отримують дотики, але дотики до самого UIStackView перейшли до поданого нижче перегляду (для мене mapView).


2

Здається, навіть у вас є досить багато відповідей, тут немає жодної чистої швидкості, яка мені потрібна. Тож я взяв тут відповідь від @fresidue і перетворив її на швидку, оскільки це в основному розробники хочуть використовувати тут.

Це вирішило мою проблему, коли у мене є якась прозора панель інструментів з кнопкою, але я хочу, щоб панель інструментів була непомітною для користувача, і сенсорні події повинні проходити.

isUserInteractionEnabled = false, оскільки деякі заяви не є варіантом на основі мого тестування.

 override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
    for subview in subviews {
        if subview.hitTest(convert(point, to: subview), with: event) != nil {
            return true
        }
    }
    return false
}

1

У StackView у мене було кілька міток, і я не мав великого успіху з рішеннями, наведеними вище, натомість я вирішив свою проблему за допомогою коду нижче:

    let item = ResponsiveLabel()

    // Configure label

    stackView.addArrangedSubview(item)

Підкласифікація UIStackView:

class PassThrouStackView:UIStackView{
    override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
        for subview in self.arrangedSubviews {
            let convertedPoint = convert(point, to: subview)
            let labelPoint = subview.point(inside: convertedPoint, with: event)
            if (labelPoint){
                return subview
            }

        }
        return nil
    }

}

Тоді ви можете зробити щось на кшталт:

class ResponsiveLabel:UILabel{
    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        // Respond to touch
    }
}

0

Спробуйте щось подібне ...

for (UIView *view in subviews)
  [view touchesBegan:touches withEvent:event];

Код, наведений вище, наприклад, у вашому методі touchesBegan передав би штрихи у всі підгляди перегляду.


6
Проблема такого підходу AFAIK полягає в тому, що спроба передати дотики до класів фреймворку UIKit найчастіше не дасть ефекту. Згідно з документацією Apple, рамкові класи UIKit не розроблені для отримання дотиків, у яких властивість перегляду встановлено на інший вигляд. Тож такий підхід працює здебільшого для користувацьких підкласів UIView.
maciejs

0

Ситуацію, яку я намагався зробити, було побудувати панель управління, використовуючи елементи управління всередині вкладених UIStackView. Деякі елементи управління мали UITextField, інші - UIButton. Також були етикетки для ідентифікації елементів управління. Що я хотів зробити, це поставити велику "невидиму" кнопку за панеллю управління, щоб, якщо користувач натиснув область поза кнопкою чи текстовим полем, я міг би потім знайти це і вжити заходів - насамперед відхилити будь-яку клавіатуру, якщо текст поле було активним (resignFirstResponder). Однак натискання на ярлик чи іншу порожню область на панелі керування не пропустить речі. Наведені вище дискусії були корисними, коли я міг відповісти нижче.

В основному я підкласифікував UIStackView і перезаписав процедуру "point (inside: with)", щоб шукати тип елементів управління, які потребували дотику та "ігнорувати" речі, такі як мітки, які я хотів ігнорувати. Він також перевіряє наявність у UIStackView, щоб все могло повторитись у структурі панелі управління.

Код - це, мабуть, трохи більш багатослівний, ніж повинен бути. Але це було корисно в налагодженні і, сподіваємось, надає більш чіткості в тому, що робить рутина. Просто переконайтеся в програмі Interface Builder, щоб змінити клас UIStackView на PassThruStack.

class PassThruStack: UIStackView {

override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {

    for view in self.subviews {
        if !view.isHidden {
            let isStack = view is UIStackView
            let isButton = view is UIButton
            let isText = view is UITextField
            if isStack || isButton || isText {
                let pointInside = view.point(inside: self.convert(point, to: view), with: event)
                if pointInside {
                    return true
                }
            }
        }
    }
    return false
}

}


-1

Як запропонував @PixelCloudStv, якщо ви хочете перекинутись з одного погляду на інший, але з деяким додатковим контролем над цим процесом - підклас UIView

// заголовок

@interface TouchView : UIView

@property (assign, nonatomic) CGRect activeRect;

@end

// реалізація

#import "TouchView.h"

@implementation TouchView

#pragma mark - Ovverride

- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
{
    BOOL moveTouch = YES;
    if (CGRectContainsPoint(self.activeRect, point)) {
        moveTouch = NO;
    }
    return moveTouch;
}

@end

Після в інтерфейсіBuilder просто встановіть клас View на TouchView і встановіть активну пряму за допомогою своєї прямої. Також ви можете змінювати та реалізовувати іншу логіку.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.