Длительное нажатие на UITableView


191

Я хотел бы обработать долгое нажатие на, UITableViewCellчтобы напечатать «меню быстрого доступа». Кто-то уже сделал это?

Особенно этот жест распознавать UITableView?

Ответы:


427

Сначала добавьте распознаватель жестов длинного нажатия в табличное представление:

UILongPressGestureRecognizer *lpgr = [[UILongPressGestureRecognizer alloc] 
  initWithTarget:self action:@selector(handleLongPress:)];
lpgr.minimumPressDuration = 2.0; //seconds
lpgr.delegate = self;
[self.myTableView addGestureRecognizer:lpgr];
[lpgr release];

Затем в обработчике жестов:

-(void)handleLongPress:(UILongPressGestureRecognizer *)gestureRecognizer
{
    CGPoint p = [gestureRecognizer locationInView:self.myTableView];

    NSIndexPath *indexPath = [self.myTableView indexPathForRowAtPoint:p];
    if (indexPath == nil) {
        NSLog(@"long press on table view but not on a row");
    } else if (gestureRecognizer.state == UIGestureRecognizerStateBegan) {
        NSLog(@"long press on table view at row %ld", indexPath.row);
    } else {
        NSLog(@"gestureRecognizer.state = %ld", gestureRecognizer.state);
    }
}

Вы должны быть осторожны с этим, чтобы он не мешал обычному постукиванию пользователя по ячейке, а также обратите внимание, что он handleLongPressможет срабатывать несколько раз (это будет связано с изменениями состояния распознавателя жестов).


1
Потрясающие !!! Большое спасибо! Но последний маленький вопрос: почему метод handleLongPress является вызовом, когда касание закончилось ???
2010 года

111
Исправление: он запускается несколько раз, чтобы указать на различные состояния жеста (начался, изменился, закончился и т. Д.). Поэтому в методе-обработчике проверьте свойство состояния средства распознавания жестов, чтобы избежать выполнения действий в каждом состоянии жеста. Например: if (gestureRecognizer.state == UIGestureRecognizerStateBegan) ....

3
Не забывайте, что распознаватели жестов теперь могут быть добавлены к элементам пользовательского интерфейса непосредственно в Интерфейсном Разработчике и подключены через метод IBAction, так что этот ответ еще проще ;-) (просто не забудьте присоединить распознаватель к UITableView, а не к UITableViewCell...)

10
Также подтвердите протокол UIGestureRecognizerDelegate в файле
class.h

1
Как предотвратить перемещение табличного представления в ячейку (если у вас реализован 'didSelectRowAtIndexPath'?)
Marchy

46

Я использовал ответ Анны-Карениной, и он работает почти отлично с очень серьезной ошибкой.

Если вы используете разделы, длительное нажатие на заголовок раздела приведет к неверному результату нажатия на первую строку в этом разделе, я добавил ниже фиксированную версию (включая фильтрацию фиктивных вызовов на основе состояния жеста, в соответствии с Предложение Анны-Карениной).

- (IBAction)handleLongPress:(UILongPressGestureRecognizer *)gestureRecognizer
{
    if (gestureRecognizer.state == UIGestureRecognizerStateBegan) {

        CGPoint p = [gestureRecognizer locationInView:self.tableView];

        NSIndexPath *indexPath = [self.tableView indexPathForRowAtPoint:p];
        if (indexPath == nil) {
            NSLog(@"long press on table view but not on a row");
        } else {
            UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:indexPath];
            if (cell.isHighlighted) {
                NSLog(@"long press on table view at section %d row %d", indexPath.section, indexPath.row);
            }
        }
    }
}

Привет @marmor: Я хочу спросить, возможно ли определить только часть представления, к которому обратился пользователь?
Мантан

Эй, вы можете использовать hitTest для этого ( developer.apple.com/library/ios/documentation/uikit/reference/… ). Проверьте этот ответ, например, о том, как использовать: stackoverflow.com/a/2793253/819355
marmor

Пока принятый ответ работает. Он регистрирует несколько пожаров, а также ведение журнала при перетаскивании на экране. Этот ответ не дает. Законный ответ скопировать и вставить. Спасибо.
ChrisOSX

29

Ответ в Swift 5 (продолжение ответа Рики в Swift)

Добавьте UIGestureRecognizerDelegateк вашему ViewController

 override func viewDidLoad() {
    super.viewDidLoad()

    //Long Press
    let longPressGesture = UILongPressGestureRecognizer(target: self, action: #selector(handleLongPress))
    longPressGesture.minimumPressDuration = 0.5
    self.tableView.addGestureRecognizer(longPressGesture)
 }

И функция:

@objc func handleLongPress(longPressGesture: UILongPressGestureRecognizer) {
    let p = longPressGesture.location(in: self.tableView)
    let indexPath = self.tableView.indexPathForRow(at: p)
    if indexPath == nil {
        print("Long press on table view, not row.")
    } else if longPressGesture.state == UIGestureRecognizer.State.began {
        print("Long press on row, at \(indexPath!.row)")
    }
}

20

Здесь разъясняются инструкции, объединяющие ответ Dawn Song и Marmor.

Перетащите длинное нажатие распознавателя жестов и поместите его в ячейку таблицы. Он прыгнет в конец списка слева.

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

Затем подключите распознаватель жестов так же, как подключите кнопку. введите описание изображения здесь

Добавьте код из Marmor в обработчик действий

- (IBAction)handleLongPress:(UILongPressGestureRecognizer *)sender {
if (sender.state == UIGestureRecognizerStateBegan) {

    CGPoint p = [sender locationInView:self.tableView];

    NSIndexPath *indexPath = [self.tableView indexPathForRowAtPoint:p];
    if (indexPath == nil) {
        NSLog(@"long press on table view but not on a row");
    } else {
        UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:indexPath];
        if (cell.isHighlighted) {
            NSLog(@"long press on table view at section %d row %d", indexPath.section, indexPath.row);
        }
    }
}

}


2
Лучший ответ на мой взгляд
Асен Касимов

8
Распознаватель жестов при длинном нажатии должен применяться к табличному представлению, а не к ячейке табличного представления. Отбрасывание этого в ячейку табличного представления будет иметь только строку 0, слушающую долгое нажатие.
Алекс

14

Похоже, более эффективно добавить распознаватель непосредственно в ячейку, как показано здесь:

Нажмите и удерживайте для ячеек TableView, тогда и сейчас

(выделите пример внизу)


7
Как выделить новый объект распознавателя жестов для каждой строки более эффективно, чем один распознаватель для всей таблицы?
user2393462435

7
Помните, что при правильной работе dequeue создается всего несколько ячеек.
Муравьи

11

Ответ в Swift:

Добавить делегата UIGestureRecognizerDelegate к вашему UITableViewController.

Внутри UITableViewController:

override func viewDidLoad() {
    super.viewDidLoad()

    let longPressGesture:UILongPressGestureRecognizer = UILongPressGestureRecognizer(target: self, action: "handleLongPress:")
    longPressGesture.minimumPressDuration = 1.0 // 1 second press
    longPressGesture.delegate = self
    self.tableView.addGestureRecognizer(longPressGesture)

}

И функция:

func handleLongPress(longPressGesture:UILongPressGestureRecognizer) {

    let p = longPressGesture.locationInView(self.tableView)
    let indexPath = self.tableView.indexPathForRowAtPoint(p)

    if indexPath == nil {
        print("Long press on table view, not row.")
    }
    else if (longPressGesture.state == UIGestureRecognizerState.Began) {
        print("Long press on row, at \(indexPath!.row)")
    }

}

6

Я собрал небольшую категорию на UITableView, основанную на превосходном ответе Анны Карениной.

Таким образом, у вас будет удобный метод делегата, к которому вы привыкли при работе с обычными табличными представлениями. Проверьте это:

//  UITableView+LongPress.h

#import <UIKit/UIKit.h>

@protocol UITableViewDelegateLongPress;

@interface UITableView (LongPress) <UIGestureRecognizerDelegate>
@property(nonatomic,assign)   id <UITableViewDelegateLongPress>   delegate;
- (void)addLongPressRecognizer;
@end


@protocol UITableViewDelegateLongPress <UITableViewDelegate>
- (void)tableView:(UITableView *)tableView didRecognizeLongPressOnRowAtIndexPath:(NSIndexPath *)indexPath;
@end



//  UITableView+LongPress.m

#import "UITableView+LongPress.h"

@implementation UITableView (LongPress)
@dynamic delegate;

- (void)addLongPressRecognizer {
    UILongPressGestureRecognizer *lpgr = [[UILongPressGestureRecognizer alloc]
                                          initWithTarget:self action:@selector(handleLongPress:)];
    lpgr.minimumPressDuration = 1.2; //seconds
    lpgr.delegate = self;
    [self addGestureRecognizer:lpgr];
}


- (void)handleLongPress:(UILongPressGestureRecognizer *)gestureRecognizer
{
    CGPoint p = [gestureRecognizer locationInView:self];

    NSIndexPath *indexPath = [self indexPathForRowAtPoint:p];
    if (indexPath == nil) {
        NSLog(@"long press on table view but not on a row");
    }
    else {
        if (gestureRecognizer.state == UIGestureRecognizerStateBegan) {
            // I am not sure why I need to cast here. But it seems to be alright.
            [(id<UITableViewDelegateLongPress>)self.delegate tableView:self didRecognizeLongPressOnRowAtIndexPath:indexPath];
        }
    }
}

Если вы хотите использовать это в UITableViewController, вам, вероятно, нужно создать подкласс и соответствовать новому протоколу.

Это прекрасно работает для меня, надеюсь, это поможет другим!


Удивительное использование шаблонов делегирования и категорий
valeCocoa

5

Ответ Swift 3, использующий современный синтаксис, включающий другие ответы и исключающий ненужный код.

override func viewDidLoad() {
    super.viewDidLoad()
    let recognizer = UILongPressGestureRecognizer(target: self, action: #selector(tablePressed))
    tableView.addGestureRecognizer(recognizer)
 }

@IBAction func tablePressed(_ recognizer: UILongPressGestureRecognizer) {
    let point = recognizer.location(in: tableView)

    guard recognizer.state == .began,
          let indexPath = tableView.indexPathForRow(at: point),
          let cell = tableView.cellForRow(at: indexPath),
          cell.isHighlighted
    else {
        return
    }

    // TODO
}

2

Просто добавьте UILongPressGestureRecognizer в указанную ячейку прототипа в раскадровке, затем перетащите жест в файл .m viewController, чтобы создать метод действия. Я сделал это, как я сказал.


Можете ли вы объяснить немного больше? Вы сделали прототипную ячейку свойством в вашем венчурном капитале?
Итан Паркер

-2

Используйте свойство метки времени UITouch в touchesBegan, чтобы запустить таймер или остановить его при срабатывании touchesEnded.


Спасибо за ваш ответ, но как я могу определить, какая строка касается касания?
2010 года

Я могу ошибаться, но ничто не поможет вам в этом. Вам нужно получить индексы текущих видимых ячеек с помощью [tableView indexPathsForVisibleRows], а затем, используя некоторые вычисления (смещение tableView от вершины + X умноженное на строки), вы узнаете, что координаты вашего пальца находятся на какой строка.
Томас Джулин

Я уверен, что есть более простой способ сделать это, в любом случае, если у вас есть другая идея, я буду здесь :)
foOg

Я также был бы рад узнать, возможно ли что-то более легкое. Но я не думаю, что это происходит, главным образом потому, что Apple не хочет, чтобы мы обрабатывали взаимодействия ... Это похоже на способ мышления Android в этом «меню быстрого доступа». Если бы это было мое приложение, я бы справился с ним, как приложение Twitter.
Размах

Да, я думал об этом, поэтому, если я действительно не могу сделать это с длинным событием прессы, я буду использовать метод смахивания. Но, может быть, кто-то в переполнении стека сделал это ...
foOg
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.