UITapGestureRecognizer прерывает UITableView didSelectRowAtIndexPath


208

Я написал свою собственную функцию для прокрутки текстовых полей, когда появляется клавиатура. Для того, чтобы отклонить клавиатуру, нажав на текстовое поле, я создал функцию, 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 Чтобы явно отключить распознаватель жестов при выборе ячеек, которые я добавил ![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, равен ли он, так как вы можете нажимать на подпредставление. Зависит от того, что вам нужно.
Шим

72

Набор cancelsTouchesInViewвашего распознавателя на ложь. В противном случае он «потребляет» касание для себя и не передает его в табличное представление. Вот почему событие выбора никогда не происходит.

например в swift

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

1
Большое спасибо за это!
Бишан

1
Это самый чистый в Свифте. Спасибо
Dx_

хорошо работает внутри ячеек в виде таблицы, красиво и просто!
Абдельрхман

@laoyur очень верно
чертова дыра

все еще получил щелчок ячейки вместо жеста касания
famfamfam

42

И для Свифта (основываясь на ответе @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)

Спасибо за публикацию этого ответа
Густаво Байокки Коста

14

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

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

Предостережение: есть предположение, что 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
    }
}

при нажатии на табличное представление этот метод делегата возвращает false, а didSelectRowAtIndexPathметод табличного представления работает правильно.


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

5

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

Полученный код представляет собой комбинацию ответа Абдулрахмана Масуда и ответа Николая Нильсена.

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:

- Интерфейсный строитель

InterfaceBuilder

- Код

<#gestureRecognizer#>.cancelsTouchesInView = false

Что делать, если я хочу отключить его работу, не только позволяя didSelectRowAt работать с ним?
Eyad Shokry

1

Хотя уже поздно, и многие считают, что приведенные выше предложения работают нормально, я не мог заставить методы Джейсона или TMilligan работать.

У меня есть Static 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

Вот моё решение, которое напрямую связывает идентификатор mustReceiveTouch распознавателя с тем, показывает ли клавиатура.

В вашем кране жест распознавания делегата:

#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

В Swift вы можете использовать это внутри

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

func CheckTheTime() -> Bool{
    return true
}

0

Мой случай был другим, включая uisearchbar и uitableview для self.view. Я хотел отклонить клавиатуру uisearchbar, касаясь вида.

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
}

Когда пользователь касается self.view:

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.