Довго натискайте на UITableView


191

Мені хотілося б довго натиснути на a, 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 викликає, коли дотик закінчився ???
10.10

111
Виправлення: вона спрацьовує кілька разів, щоб вказати на різні стани жесту (почалися, змінені, закінчені тощо). Отже, в оброблювальному методі перевіряйте властивість стану розпізнавача жестів, щоб уникнути дії в кожному стані жесту. Наприклад: if (gestureRecognizer.state == UIGestureRecognizerStateBegan) ....

3
Не забувайте, розпізнавачі жестів тепер можуть бути додані до елементів інтерфейсу безпосередньо в Interface Builder та підключені за допомогою методу IBAction, тому ця відповідь ще простіше ;-) (просто пам’ятайте, щоб приєднати розпізнавальник до UITableView, а не UITableViewCell...)

10
Також підтвердьте протокол UIGestureRecognizerDelegate у файлі class.h
Vaquita

1
Як ви не можете переглядати таблицю у клітинку (якщо у вас реалізовано "didSelectRowAtIndexPath?")
березень

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

Ось уточнена інструкція, що поєднує відповідь Світанку Сонця та відповідь Мармора.

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

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

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

Додайте код від 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, щоб запустити таймер або зупинити його, коли дотикиЗакінчено, звільняться


Дякую за вашу відповідь, але як я можу виявити, який рядок стосується дотику?
10.10

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

Я впевнений, що є простіший спосіб зробити це, все одно якщо у вас є інша ідея, я буду тут :)
foOg

Я був би радий також знати, чи можливо щось легше. Але я не думаю, що це є, здебільшого тому, що Apple не є тим, як Apple хоче, щоб ми працювали з взаємодіями ... Це схоже на Android-спосіб мислення цього "меню швидкого доступу". Якби це був мій додаток, я оброблятиму це як додаток Twitter. Проведення пальцем ліворуч відображає параметри
Thomas Joulin

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