Перерви UITapGestureRecognizer UITableView didSelectRowAtIndexPath


207

Я написав власну функцію прокручування текстових полів вгору, коли з’являється клавіатура. Для того, щоб відхилити клавіатуру, торкнувшись подалі від текстового поля, я створив команду, UITapGestureRecognizerяка піклується про відставку першого відповідача в текстовому полі при натисканні на нього.

Тепер я також створив автозаповнення для текстового поля, яке створює UITableViewтрохи нижче текстового поля та заповнює його елементами, коли користувач вводить текст.

Однак, вибираючи одну з записів у автоматично завершеній таблиці, didSelectRowAtIndexPathне викликається. Натомість, здається, що розпізнавальник жестів натискання телефонує і просто віддає першу відповідь.

Я здогадуюсь, є якийсь спосіб сказати розпізнавальному жесту натискання, щоб продовжувати передавати повідомлення про торкнення вниз UITableView, але я не можу зрозуміти, що це таке. Будь-яка допомога буде дуже вдячна.


Відповіді:


258

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

Рішення полягало в тому, щоб реалізувати UIGestureRecognizerDelegateта додати наступне:

////////////////////////////////////////////////////////////
// UIGestureRecognizerDelegate methods

#pragma mark UIGestureRecognizerDelegate methods

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
{
    if ([touch.view isDescendantOfView:autocompleteTableView]) {

        // Don't let selections of auto-complete entries fire the 
        // gesture recognizer
        return NO;
    }

    return YES;
}

Це подбало про це. Сподіваємось, це допоможе і іншим.


Вам також, мабуть, потрібно пропустити крани в текстовому полі.
Стівен Крамер

5
У мене пощастило більшеreturn ![touch.view isKindOfClass:[UITableViewCell class]];
Джош Парадроїд

1
@Josh Це не працює, якщо натиснути на мітки всередині комірки. Насправді відповідь Джейсона ідеально підходить для того, що мені потрібно.
Вільям Т.

Але це не відповідь на вашу проблему, ні? Це або / або. По суті, будь-який дотик до подання таблиці незначний, якщо розпізнавальник жестів .... ви хочете, щоб ОСОБЛИВО розпізнавати жести І вибір таблиці перегляду працювати
PostCodeism

5
@Durgaprasad Щоб явно відключити розпізнавальник жестів при виборі комірок, я додав a ![touch.view isEqual: self.tableView] &&перед тим, [touch.view isDescendantOfView: self.tableView]щоб виключити tableViewсебе (тому що isDescendantOfView:також повертається, YESякщо аргумент-перегляд є самим переглядом приймача). Сподіваємось, що допоможе іншим людям, які хочуть того ж результату.
anneblue

220

Найпростіший спосіб вирішити цю проблему:

UITapGestureRecognizer *tapRec = [[UITapGestureRecognizer alloc] 
    initWithTarget:self action:@selector(tap:)];
[tapRec setCancelsTouchesInView:NO];

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

Крім того, розширюючи відповідь Роберта, якщо у вас є вказівник на відповідний перегляд таблиці, ви можете безпосередньо порівняти його клас замість того, щоб перетворювати на рядок і сподіваємось, що Apple не змінить номенклатуру:

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer 
     shouldReceiveTouch:(UITouch *)touch
{
    if([touch.view class] == tableview.class){
        return //YES/NO
    }

    return //YES/NO

}

Пам'ятайте, ви також повинні оголосити UIGestureRecognizerделегата з цим кодом.


4
Дякую, setCancelsTouchesInView все змінює :)
PostCodeism

Можливо, все-таки потрібно використовувати isDescendantOfViewзамість того, щоб просто перевірити, чи значення classрівне, оскільки ви можете натиснути на підперегляд. Залежить від того, що вам потрібно.
шим

71

Встановіть cancelsTouchesInViewваш розпізнавач на помилковий. В іншому випадку він "споживає" дотик для себе, а не передає його на подання таблиці. Ось чому подія відбору ніколи не буває.

наприклад в swift

let tapOnScreen: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: "CheckTheTime")
tapOnScreen.cancelsTouchesInView = false
view.addGestureRecognizer(tapOnScreen)

1
Дякую за це!
Бішан

1
Це найчистіше у Свіфта. Дякую
Dx_

добре працює в осередках у наборі зображень, мило і просто!
Абдоелрман

@laoyur дуже правда
bLacK hoLE

все ще отримали клік клітини замість жесту натискання
famfamfam

42

А для Swift (на основі відповіді від @Jason):

class MyAwesomeClass: UIViewController, UIGestureRecognizerDelegate

private var tap: UITapGestureRecognizer!

override func viewDidLoad() {
   super.viewDidLoad()

   self.tap = UITapGestureRecognizer(target: self, action: "viewTapped:")
   self.tap.delegate = self
   self.view.addGestureRecognizer(self.tap)
}

// UIGestureRecognizerDelegate method
func gestureRecognizer(gestureRecognizer: UIGestureRecognizer, shouldReceiveTouch touch: UITouch) -> Bool {
    if touch.view?.isDescendantOfView(self.tableView) == true {
        return false
    }
    return true
}

Дякую багато, це заощадило мій час.
Бішан

22

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

func gestureRecognizer(gestureRecognizer: UIGestureRecognizer, shouldReceiveTouch touch: UITouch) -> Bool {
    if gestureRecognizer is UITapGestureRecognizer {
        let location = touch.locationInView(tableView)
        return (tableView.indexPathForRowAtPoint(location) == nil)
    }
    return true
}

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


1
Цей розчин гойдається! Дуже корисно відрізнити клітинку від порожнього поля таблиці ..
Алессандро Орнано

18

Я думаю , немає необхідності писати блоки кодів просто набір cancelsTouchesInViewдля falseвашого об'єкта жесту, за замовчуванням це , trueі ви просто повинні встановити його false. Якщо ви використовуєте UITapGestureоб'єкт у своєму коді, а також використовуєте UIScrollView(tableview, collectionview), тоді встановіть це властивістьfalse

let tap: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(self.dismissKeyboard))
tap.cancelsTouchesInView = false
view.addGestureRecognizer(tap)

Дякуємо, що опублікували цю відповідь
Gustavo Baiocchi Costa,

14

Аналогічне рішення полягає в реалізації, gestureRecognizer:shouldReceiveTouch:використовуючи клас перегляду, щоб визначити, яку дію вжити. Цей підхід має перевагу в тому, що не маскує дотики в регіоні, безпосередньо оточуючому таблицю (представлення цих областей все ще походять від екземплярів UITableView, але вони не представляють комірок).

Це також має бонус, що він працює з декількома таблицями в одному огляді (без додавання додаткового коду).

Caveat: є припущення, що Apple не змінить назву класу.

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
{
    return ![NSStringFromClass([touch.view class]) isEqualToString:@"UITableViewCellContentView"];
}

1
Для усунення рядка назви класу використовуйте цей рядокreturn ![[touch.view.superview class] isSubclassOfClass:[UITableViewCell class]];
Nianliang

7

Для Swift 4.2 рішення було реалізувати UIGestureRecognizerDelegateта додати наступне:

extension ViewController : UIGestureRecognizerDelegate {
    func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
        if touch.view!.isDescendant(of: tblView) {
            return false
        }
        return true
    }
}

при натисканні на перегляд таблиці цей метод делегування повертає помилку і didSelectRowAtIndexPathметод перегляду таблиці працює належним чином.


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

4

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

Отриманий код - це поєднання відповіді Абдулрахмана Масуда та відповіді Миколи Нільсена.

extension MyViewController: UIGestureRecognizerDelegate {
    func addGestureRecognizer() {
        let tapOnScreen = UITapGestureRecognizer(target: self,
                                                action: #selector(functionToCallWhenUserTapsOutsideOfTableView))

        // stop the gesture recognizer from "consuming" the touch event, 
        // so that the touch event can reach other buttons on view.
        tapOnScreen.cancelsTouchesInView = false

        tapOnScreen.delegate = self

        self.view.addGestureRecognizer(tapOnScreen)
    }

    // if your tap event is on the menu, don't run the touch event.
    func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
        if touch.view?.isDescendant(of: self.tableView) == true {
            return false
        }
        return true
    }

    @objc func functionToCallWhenUserTapsOutsideOfTableView() {

        print("user tapped outside table view")
    }
}

І в MyViewControllerкласі, в класі, який має UITableView, в onViewDidLoad(), я обов'язково зателефонував addGestureRecognizer():

class MyViewController: UIViewController {
    ...
    override func viewDidLoad() {
        super.viewDidLoad()
        ...
        self.addGestureRecognizer()
        ...
    }
    ...
}

3

Просто встановіть cancels touches in viewна falseбудь-який жест під (table/collection)View:

- Інтерфейсбудівник

Інтерфейсбудівник

- Кодекс

<#gestureRecognizer#>.cancelsTouchesInView = false

що робити, якщо я хочу відключити його роботу, не лише дозволяючи didSelectRowAt працювати з ним?
Еяд Шокри

1

Хоча вже пізно і багато людей вважають, що вищезазначені пропозиції справно працюють, я не міг змусити методів Джейсона чи Тміллігана працювати.

У мене є статичний tableView з декількома клітинками, що містять текстові поля, які отримують числові вводи, використовуючи лише цифрову клавіатуру. Це було ідеально для мене:

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
    if(![touch.view isKindOfClass:[UITableViewCell class]]){

        [self.firstTF resignFirstResponder];
        [self.secondTF resignFirstResponder];
        [self.thirdTF resignFirstResponder];
        [self.fourthTF resignFirstResponder];

        NSLog(@"Touches Work ");

        return NO;
    }
    return YES;
}

Переконайтеся, що ви реалізували це <UIGestureRecognizerDelegate>у своєму .h файлі.

У цьому рядку ![touch.view isKindOfClass:[UITableViewCell class]]перевіряється, чи було натиснено таблицю TableViewCell та відхилена будь-яка активна клавіатура.


1

Просте рішення - використовувати екземпляри UIControl в UITableViewCell, щоб отримати дотики. Ви можете додати будь-які представлення даних за допомогою UserInteractionEnables == НІ до UIControl, щоб отримати крани.


0

Ось моє рішення, яке пов'язує розпізнавальник повиненReReiveiveTouch безпосередньо з тим, чи відображається клавіатура.

У делегаті розпізнавача жестів розпізнавання:

#pragma mark - UIGestureRecognizerDelegate

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
{
    if ([PFXKeyboardStateListener sharedInstance].visible) {
        return YES;
    }

    return NO;
}

І PFXKeyboardStateListener.h:

@interface PFXKeyboardStateListener : NSObject
{
    BOOL _isVisible;
}

+ (PFXKeyboardStateListener *)sharedInstance;

@property (nonatomic, readonly, getter=isVisible) BOOL visible;

@end

І PFXKeyboardStateListener.m:

static PFXKeyboardStateListener *sharedInstance;

@implementation PFXKeyboardStateListener

+ (PFXKeyboardStateListener *)sharedInstance
{
    return sharedInstance;
}

+ (void)load
{
    @autoreleasepool {
        sharedInstance = [[self alloc] init];
    }
}

- (void)dealloc
{
    [[NSNotificationCenter defaultCenter] removeObserver:self];
}

- (BOOL)isVisible
{
    return _isVisible;
}

- (void)didShow
{
    _isVisible = YES;
}

- (void)didHide
{
    _isVisible = NO;
}

- (id)init
{
    if ((self = [super init])) {
        NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
        [center addObserver:self selector:@selector(didShow) name:UIKeyboardDidShowNotification object:nil];
        [center addObserver:self selector:@selector(didHide) name:UIKeyboardWillHideNotification object:nil];
    }
    return self;
}

@end

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


0

Реалізуйте цей метод для делегата UIGestureRecognizer:

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer
       shouldReceiveTouch:(UITouch *)touch
{
  UIView *superview = touch.view;
  do {
    superview = superview.superview;
    if ([superview isKindOfClass:[UITableViewCell class]])
      return NO;
  } while (superview && ![superview isKindOfClass:[UITableView class]]);

  return superview != nil;
}

0

Швидко можна використовувати це всередині

func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
    if CheckTheTime() == true {
        // do something 
    }else{
    }
}

func CheckTheTime() -> Bool{
    return true
}

0

У мене справа була іншою, включаючи панель uisearch та uitableview на self.view. Я хотів відхилити клавіатуру uisearch, торкнувшись перегляду.

var tapGestureRecognizer:UITapGestureRecognizer?

override func viewWillAppear(animated: Bool) {
    tapGestureRecognizer = UITapGestureRecognizer(target: self, action:Selector("handleTap:"))
}

На методах делегування UISearchBar:

func searchBarShouldBeginEditing(searchBar: UISearchBar) -> Bool {
    view.addGestureRecognizer(tapGestureRecognizer!)
    return true
}
func searchBarShouldEndEditing(searchBar: UISearchBar) -> Bool {
    view.removeGestureRecognizer(tapGestureRecognizer!)
    return true
}

Коли користувач торкається самостійного перегляду:

func handleTap(recognizer: UITapGestureRecognizer) {
    sampleSearchBar.endEditing(true)
    sampleSearchBar.resignFirstResponder()
}

0

Свіфт 5, травень 2020 року.

У мене є textField та tableView, які стають видимими при введенні тексту.

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

Відображаються клавіатура та tableView

Спочатку додаємо tapGestureRecognizer.

tap = UITapGestureRecognizer(target: self, action: #selector(viewTapped))
tap.delegate = self
view.addGestureRecognizer(tap)

@objc func viewTapped() {
        view.endEditing(true)
}

Потім ми додаємо в UIGestureRecognizerDelegate наступний чек:

extension StadtViewController: UIGestureRecognizerDelegate {

func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
    if touch.view?.isDescendant(of: self.tableView) == true {
        return false
    } else {
        view.endEditing(true)
        return true
    }
}

}

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

введіть тут опис зображення


-1

Це моє рішення, засноване на вищезазначених відповідях ... Це працювало для мене ...

//Create tap gesture for menu transparent view
UITapGestureRecognizer *rightTableTransparentViewTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(rightTableTransparentViewTapMethod:)];
[rightTableTransparentViewTap setCancelsTouchesInView:NO];
[_rightTableTransparentView addGestureRecognizer:rightTableTransparentViewTap];

- (void)rightTableTransparentViewTapMethod:(UITapGestureRecognizer *)recognizer {
    //Write your code here
}

-1

ПИТАННЯ: У моєму випадку проблема полягала в тому, що я спочатку розміщував кнопку в кожній комірці collectionView і встановлював обмеження для заповнення комірки, щоб при натисканні на клітинку натискав кнопку, проте функція кнопок порожня, тому нічого не було видається, що відбувається.

Виправлення: я виправив це, видаливши кнопку з комірки подання колекції.

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