UIButton всередині представлення, яке має UITapGestureRecognizer


184

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

Я не можу просто видалити UITapGestureRecognizer після того, як я отримав його натискання. Тому що з ним новий вигляд також можна видалити. Значить, я хочу поведінку, як на повноекранному режимі управління відео .

Відповіді:


256

Ви можете встановити контролер або перегляд (залежно від того, що створює розпізнавач жестів) як делегат UITapGestureRecognizer. Потім у делегаті ви можете реалізувати -gestureRecognizer:shouldReceiveTouch:. У вашій реалізації ви можете перевірити, чи дотик належить вашому новому підгляду, а якщо він є, доручіть розпізнавальнику жестів ігнорувати його. Щось таке:

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
    // test if our control subview is on-screen
    if (self.controlSubview.superview != nil) {
        if ([touch.view isDescendantOfView:self.controlSubview]) {
            // we touched our control surface
            return NO; // ignore the touch
        }
    }
    return YES; // handle the touch
}

У своєму заголовковому файлі я мав свій погляд реалізувати UIGestoreRegognizerDelegate, а в мій .mi додав ваш код вище. Кран ніколи не вводить цей метод, він прямує до мого обробника. Будь-які ідеї?
кмехта

1
@kmehta ви, швидше за все, забули встановити властивість делегата UIGestureRecognizer.
до

Чи можна узагальнити цю відповідь для вирішення всіх випадків, коли є участь МБА?
Мартін Вікман

@Martin IBAction збирається, щоб voidне залишати ніякої інформації під час виконання для використання для виявлення. Але те, що ти можеш зробити, - це підійти до ієрархії подання даних, протестувати UIControlта повернутися, NOякщо ти знайдеш якісь елементи управління.
Лілі Баллард

THX для цього, це мені допомагає. якщо ваша кнопка знаходиться в UIImageView, переконайтесь, що для параметра UserInteractionEnabled of imageView встановлено значення YES.
james075

156

Як наступні заходи щодо Кейсі до відповіді Кевіна Балларда:

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
        if ([touch.view isKindOfClass:[UIControl class]]) {
            // we touched a button, slider, or other UIControl
            return NO; // ignore the touch
        }
    return YES; // handle the touch
}

Це в основному змушує працювати всі типи елементів управління, такі як кнопки, повзунки тощо


1
Це гнучке рішення, яке може супроводжуватися тим, setCancelsTouchesInView = NOщоб не викликати жест натискання на взаємодію з елементами управління. Однак ви можете написати це краще:return ![touch.view isKindOfClass:[UIControl class]];
griga13

Рішення Swift 4+: повернення! (Touch.view - це UIControl)
nteissler

103

Знайшов цю відповідь тут: посилання

Ви також можете використовувати

tapRecognizer.cancelsTouchesInView = NO;

Що заважає розпізнавачу крана бути єдиним, хто вловлює всі крани

ОНОВЛЕННЯ - Майкл згадав посилання на документацію, що описує цю властивість: cancellsTouchesInView


8
Це має бути прийнятою відповіддю ІМО. Це дозволяє кожному підгляді самостійно обробляти кран і запобігає перегляду, який має прикріплений до нього табрекогнізатор, "не вибивати" крана. Не потрібно впроваджувати делегата, якщо все, що ви хочете зробити, - це зробити перегляд, який можна натиснути (подати у відставку першого відповідача / приховати клавіатуру на текстових полях тощо) .. приголомшливо!
EeKay

4
Це безумовно має бути прийнятою відповіддю. Ця відповідь призвела до того, що я прочитав документацію Apple належним чином, що дає зрозуміти, що розпізнавач жестів не дозволить субпреглядам отримати визнані події, якщо ви цього не зробите.
Майкл ван дер Вестхуйзен

1
Раніше це працювало, але зараз для мене це не працює. Можливо, у мене під кнопкою є scrollView? Мені довелося реалізувати делегата, як у відповідях вище.
xissburg

7
Трохи експериментуючи, я помітив, що це спрацює лише в тому випадку, якщо подання, яке містить розпізнавальник жестів, є побратимом UIControl, і не буде, якщо подання є батьківським UIControl.
xissburg

6
Насправді не так, як задумано ... Так, це забороняє розпізнавачу крана їсти подію на UIControl. Але це все ж виконати дію розпізнавача, яка виконується одночасно 2 дією.
Тек Інь

71

У результаті відповіді Кевіна Балларда у мене була така сама проблема, і я застосував цей код:

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
    if ([touch.view isKindOfClass:[UIButton class]]){
        return NO;
    }
    return YES;
}

Це має такий же ефект, але це буде працювати на будь-якому UIButton на будь-якій глибині перегляду (мій UIButton був глибоким у кількох переглядах, а делегат UIGestureRecognizer не мав на нього посилання.)


6
Не хочеш тут бути хуєм, але це дійсно ТАК і НІ. Будь ласка, використовуйте конвенції про какао. TRUE / FALSE / NULL призначений для рівня CoreFoundation-Layer.
steipete

з того, що я читав, ТАК і НЕ були створені для читабельності. читабельність є суб'єктивною. це випливає з методів та умов іменування властивостей, якими не всі надто переймаються. якщо ви хочете суворо дотримуватися конвенції про іменування, то так, використовуйте ТАК і НІ для будь-якого NSW. однак я скажу, що при опитуванні розробників ви отримаєте неоднозначні думки щодо того, хто з них є більш читабельним [кнопка встановленаHidden: YES]; -или- [кнопка встановлена ​​прихована: ІСТИНА];
сутенер супу МакДжонес

1
Я здогадуюсь, що я намагаюся сказати, що якщо передумова конвенцій про іменування є читабельним, то я думаю, що важко заперечити, що це в деяких випадках суб'єктивне. якщо ви пурист, ви не зрозумієте цього твердження.
сутенер супу МакДжонес

2
Це може здатися суб’єктивним, але через деякий час програмування какао ви дійсно потрапляєте у більш семантично точний потік коду ... "ІСТИННА" просто здається, що не так вже й після років Objective-C, оскільки вона не відповідає "прихованому" дуже Ну.
Kendall Helmstetter Gelner

З тих пір, коли можна / чи передати жест натискання на UIButton як подія Touch Up Inside UITouch (остання - це подія, породжена натисканням на UIButton)? Створюється розпізнавальний жест розпізнавання кнопки, яка містить 15 подій дотику, вже пов'язаних з жестом натискання, видається дублюючим і неправильним . Я хотів би бачити код, а не здогадки.
Джеймс Буш

10

В iOS 6.0 і пізніших версіях контрольні дії за замовчуванням запобігають перекриванню поведінки розпізнавача жестів. Наприклад, дія за замовчуванням для кнопки - це один дотик. Якщо у вас є один розпізнавач жестів при натисканні, приєднаний до батьківського подання кнопки, і користувач натискає кнопку, то метод дії кнопки отримує подію дотику замість розпізнавача жестів. Це стосується лише розпізнавання жестів, що перекриває дію за замовчуванням для елемента керування, що включає: .....

Від API Apple док


8

Ці відповіді були неповними. Мені довелося прочитати кілька постів про те, як використовувати цю булеву операцію.

У свій * .h файл додайте це

@interface v1ViewController : UIViewController <UIGestureRecognizerDelegate>

У свій * .m файл додайте це

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {

    NSLog(@"went here ...");

    if ([touch.view isKindOfClass:[UIControl class]])
    {
        // we touched a button, slider, or other UIControl
        return NO; // ignore the touch
    }
    return YES; // handle the touch
}
- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.



    //tap gestrure
    UITapGestureRecognizer *tapGestRecog = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(screenTappedOnce)];
    [tapGestRecog setNumberOfTapsRequired:1];
    [self.view addGestureRecognizer:tapGestRecog];


// This line is very important. if You don't add it then your boolean operation will never get called
tapGestRecog.delegate = self;

}


-(IBAction) screenTappedOnce
{
    NSLog(@"screenTappedOnce ...");

}

7

Знайшов інший спосіб зробити це звідси . Він визначає дотик усередині кожної кнопки чи ні.

(1) pointInside: withEvent: (2) locationInView:

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer 
       shouldReceiveTouch:(UITouch *)touch {
    // Don't recognize taps in the buttons
    return (![self.button1 pointInside:[touch locationInView:self.button1] withEvent:nil] &&
            ![self.button2 pointInside:[touch locationInView:self.button2] withEvent:nil] &&
            ![self.button3 pointInside:[touch locationInView:self.button3] withEvent:nil]);
}

Я спробував усі інші рішення для цього, але -pointInside: withEvent: підхід був єдиним, хто працював на мене.
Кенні Віланд

3

Ось швидка версія відповіді Лілі Баллард, яка працювала на мене:

func gestureRecognizer(gestureRecognizer: UIGestureRecognizer, shouldReceiveTouch touch: UITouch) -> Bool {
    if (scrollView.superview != nil) {
        if ((touch.view?.isDescendantOfView(scrollView)) != nil) { return false }
    }
    return true
}

3

Швидкий 5

Кнопка на огляді тапгесту

 func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
    if let _ = touch.view as? UIButton { return false }
    return true
}

У моєму випадку реалізація hitTest працювала на мені. У мене був вид колекції за допомогою кнопки

Цей метод обходить ієрархію подання, викликаючи point(inside:with:)метод кожного підвідного перегляду, щоб визначити, який підвід повинен отримати сенсорну подію. Якщо point(inside:with:)повертається істина, то ієрархія підгляду переглядається аналогічно до тих пір, поки не буде знайдено перегляд спереду, що містить вказану точку.

override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
    guard isUserInteractionEnabled else { return nil }

    guard !isHidden else { return nil }

    guard alpha >= 0.01 else { return nil }

    guard self.point(inside: point, with: event) else { return nil }

    for eachImageCell in collectionView.visibleCells {
        for eachImageButton in eachImageCell.subviews {
            if let crossButton = eachImageButton as? UIButton {
                if crossButton.point(inside: convert(point, to: crossButton), with: event) {
                    return crossButton
                }
            }
        }
    }
    return super.hitTest(point, with: event)
}

1

Ви можете зупинити UITapGestureRecognizer від скасування інших подій (наприклад, натискання на вашій кнопці), встановивши наступне булеве:

    [tapRecognizer setCancelsTouchesInView:NO];

1

Якщо ваш сценарій такий:

У вас є простий перегляд, а деякі елементи UIButtons, UITextField-контролі додані як підпогляди до цього подання. Тепер ви хочете відхилити клавіатуру, коли ви торкаєтесь будь-якого іншого місця перегляду, за винятком елементів керування (додані підзагляди)

Тоді рішення:

Додайте наступний метод до свого XYZViewController.m (який має ваш погляд)

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    [self.view endEditing:YES];
}

Спочатку я спробував відповісти @cdasher , але в моєму випадку навіть при натисканні на кнопку, я отримую touch.view як "перегляд" у ViewController, але не мій елемент управління "UIButton". Чи може хтось сказати, чому властивість подання дотику не повертає початковий вигляд, у якому відбулося натискання
NaveenRaghuveer

1
Не буде працювати, якщо у вас є перегляд прокрутки або інший вид, який вимагає подій на дотик.
lkraider

1

Оптимізувавши відповідь диска, ви отримаєте

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer
       shouldReceiveTouch:(UITouch *)touch 
{
    return ![touch.view isKindOfClass:[UIControl class]];
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.