Длинный жест нажатия на UICollectionViewCell
Мне было интересно, как добавить длинный идентификатор распознавания жестов в (подкласс) UICollectionView. Я прочитал в документации, что он добавлен по умолчанию, но я не могу понять, как это сделать.
Что я хочу сделать:
Длительное нажатие на ячейку (У меня есть календарь из github), получите, какая ячейка нажата, а затем сделайте что-нибудь с ней. Мне нужно знать, какая ячейка подавлена. Извините за этот широкий вопрос, но я не мог найти ничего лучше ни в google, ни в SO
Ответы
Ответ 1
Objective-C
В вашем файле myCollectionViewController.h
добавьте протокол UIGestureRecognizerDelegate
@interface myCollectionViewController : UICollectionViewController<UIGestureRecognizerDelegate>
в вашем файле myCollectionViewController.m
:
- (void)viewDidLoad
{
// attach long press gesture to collectionView
UILongPressGestureRecognizer *lpgr
= [[UILongPressGestureRecognizer alloc]
initWithTarget:self action:@selector(handleLongPress:)];
lpgr.delegate = self;
lpgr.delaysTouchesBegan = YES;
[self.collectionView addGestureRecognizer:lpgr];
}
-(void)handleLongPress:(UILongPressGestureRecognizer *)gestureRecognizer
{
if (gestureRecognizer.state != UIGestureRecognizerStateEnded) {
return;
}
CGPoint p = [gestureRecognizer locationInView:self.collectionView];
NSIndexPath *indexPath = [self.collectionView indexPathForItemAtPoint:p];
if (indexPath == nil){
NSLog(@"couldn't find index path");
} else {
// get the cell at indexPath (the one you long pressed)
UICollectionViewCell* cell =
[self.collectionView cellForItemAtIndexPath:indexPath];
// do stuff with the cell
}
}
Swift
class Some {
@objc func handleLongPress(gesture : UILongPressGestureRecognizer!) {
if gesture.state != .Ended {
return
}
let p = gesture.locationInView(self.collectionView)
if let indexPath = self.collectionView.indexPathForItemAtPoint(p) {
// get the cell at indexPath (the one you long pressed)
let cell = self.collectionView.cellForItemAtIndexPath(indexPath)
// do stuff with the cell
} else {
print("couldn't find index path")
}
}
}
let some = Some()
let lpgr = UILongPressGestureRecognizer(target: some, action: #selector(Some.handleLongPress))
Swift 4
class Some {
@objc func handleLongPress(gesture : UILongPressGestureRecognizer!) {
if gesture.state != .ended {
return
}
let p = gesture.location(in: self.collectionView)
if let indexPath = self.collectionView.indexPathForItem(at: p) {
// get the cell at indexPath (the one you long pressed)
let cell = self.collectionView.cellForItem(at: indexPath)
// do stuff with the cell
} else {
print("couldn't find index path")
}
}
}
let some = Some()
let lpgr = UILongPressGestureRecognizer(target: some, action: #selector(Some.handleLongPress))
Ответ 2
Тот же код @abbood для Swift:
В viewDidLoad:
let lpgr : UILongPressGestureRecognizer = UILongPressGestureRecognizer(target: self, action: "handleLongPress:")
lpgr.minimumPressDuration = 0.5
lpgr.delegate = self
lpgr.delaysTouchesBegan = true
self.collectionView?.addGestureRecognizer(lpgr)
И функция:
func handleLongPress(gestureRecognizer : UILongPressGestureRecognizer){
if (gestureRecognizer.state != UIGestureRecognizerState.Ended){
return
}
let p = gestureRecognizer.locationInView(self.collectionView)
if let indexPath : NSIndexPath = (self.collectionView?.indexPathForItemAtPoint(p))!{
//do whatever you need to do
}
}
Не забывайте делегата UIGestureRecognizerDelegate
Ответ 3
Ответы здесь, чтобы добавить пользовательский дескриптор жестов дескриптора, являются правильными однако в соответствии с документацией здесь: родительский класс класса UICollectionView
устанавливает default long-press gesture recognizer
для обработки взаимодействий прокрутки, поэтому вы должны привязать свой собственный распознаватель жестов к распознавателю по умолчанию, связанный с вашим представлением коллекции.
Следующий код не позволит вашему пользовательскому распознавателю жестов вмешиваться в настройку по умолчанию:
UILongPressGestureRecognizer* longPressGesture = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(handleLongPressGesture:)];
longPressGesture.minimumPressDuration = .5; //seconds
longPressGesture.delegate = self;
// Make the default gesture recognizer wait until the custom one fails.
for (UIGestureRecognizer* aRecognizer in [self.collectionView gestureRecognizers]) {
if ([aRecognizer isKindOfClass:[UILongPressGestureRecognizer class]])
[aRecognizer requireGestureRecognizerToFail:longPressGesture];
}
Ответ 4
Использование делегата UICollectionView для получения продолжительного события
Вы должны использовать метод 3 ниже.
//UICollectionView menu delegate
- (BOOL)collectionView:(UICollectionView *)collectionView shouldShowMenuForItemAtIndexPath:(NSIndexPath *)indexPath{
//Do something
return YES;
}
- (BOOL)collectionView:(UICollectionView *)collectionView canPerformAction:(SEL)action forItemAtIndexPath:(NSIndexPath *)indexPath withSender:(nullable id)sender{
//do nothing
return NO;
}
- (void)collectionView:(UICollectionView *)collectionView performAction:(SEL)action forItemAtIndexPath:(NSIndexPath *)indexPath withSender:(nullable id)sender{
//do nothing
}
Ответ 5
UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(longPress:)];
[cell addGestureRecognizer:longPress];
и добавьте метод, подобный этому.
- (void)longPress:(UILongPressGestureRecognizer*)gesture
{
if ( gesture.state == UIGestureRecognizerStateEnded ) {
UICollectionViewCell *cellLongPressed = (UICollectionViewCell *) gesture.view;
}
}
Ответ 6
Чтобы иметь внешний распознаватель жестов и не вступать в конфликт с внутренними распознавателями жестов в UICollectionView, вам необходимо:
Добавьте свой распознаватель жестов, настройте его и запишите для него ссылку где-нибудь (лучший вариант находится в вашем подклассе, если вы подклассифицировали UICollectionView)
@interface UICollectionViewSubclass : UICollectionView <UIGestureRecognizerDelegate>
@property (strong, nonatomic, readonly) UILongPressGestureRecognizer *longPressGestureRecognizer;
@end
Переопределите методы инициализации по умолчанию initWithFrame:collectionViewLayout:
и initWithCoder:
и добавьте метод настройки для долгого распознавателя жестов нажатия
@implementation UICollectionViewSubclass
-(instancetype)initWithFrame:(CGRect)frame collectionViewLayout:(UICollectionViewLayout *)layout
{
if (self = [super initWithFrame:frame collectionViewLayout:layout]) {
[self setupLongPressGestureRecognizer];
}
return self;
}
-(instancetype)initWithCoder:(NSCoder *)aDecoder
{
if (self = [super initWithCoder:aDecoder]) {
[self setupLongPressGestureRecognizer];
}
return self;
}
@end
Напишите свой метод настройки, чтобы он запускал длинный идентификатор распознавания жестов, устанавливал его делегирование, устанавливал зависимости с помощью распознавателя жестов UICollectionView (так что это главный жест и все остальные жесты будут ждать, пока этот жест не сработает до его распознавания) и добавьте жест в вид
-(void)setupLongPressGestureRecognizer
{
_longPressGestureRecognizer = [[UILongPressGestureRecognizer alloc] initWithTarget:self
action:@selector(handleLongPressGesture:)];
_longPressGestureRecognizer.delegate = self;
for (UIGestureRecognizer *gestureRecognizer in self.collectionView.gestureRecognizers) {
if ([gestureRecognizer isKindOfClass:[UILongPressGestureRecognizer class]]) {
[gestureRecognizer requireGestureRecognizerToFail:_longPressGestureRecognizer];
}
}
[self.collectionView addGestureRecognizer:_longPressGestureRecognizer];
}
Также не забудьте реализовать методы UIGestureRecognizerDelegate, которые не позволяют этот жест и позволяют одновременное распознавание (вы можете или не можете его реализовать, это зависит от других распознавателей жестов, которые у вас есть или от зависимостей, от внутренних распознавателей жестов)
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
if ([self.longPressGestureRecognizer isEqual:gestureRecognizer]) {
return NO;
}
return NO;
}
учетные данные для этого относятся к внутренней реализации LXReorderableCollectionViewFlowLayout
Ответ 7
Возможно, использование UILongPressGestureRecognizer - наиболее распространенное решение. Но я сталкиваюсь с этим двумя неприятными неприятностями:
- иногда этот распознаватель работает некорректно, когда мы трогаем наше прикосновение;
- распознаватель перехватывает другие действия касания, поэтому мы не можем правильно использовать обратные вызовы нашего UICollectionView.
Позвольте мне предложить немного немного грубой силы, но работая, поскольку это требует предложения:
Объявление описания обратного вызова для длительного клика по нашей ячейке:
typealias OnLongClickListener = (view: OurCellView) -> Void
Расширение UICollectionViewCell с помощью переменных (мы можем назвать его, например, OurCellView):
/// To catch long click events.
private var longClickListener: OnLongClickListener?
/// To check if we are holding button pressed long enough.
var longClickTimer: NSTimer?
/// Time duration to trigger long click listener.
private let longClickTriggerDuration = 0.5
Добавление двух методов в наш класс ячеек:
/**
Sets optional callback to notify about long click.
- Parameter listener: A callback itself.
*/
func setOnLongClickListener(listener: OnLongClickListener) {
self.longClickListener = listener
}
/**
Getting here when long click timer finishs normally.
*/
@objc func longClickPerformed() {
self.longClickListener?(view: self)
}
И основные события прикосновения здесь:
/// Intercepts touch began action.
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
longClickTimer = NSTimer.scheduledTimerWithTimeInterval(self.longClickTriggerDuration, target: self, selector: #selector(longClickPerformed), userInfo: nil, repeats: false)
super.touchesBegan(touches, withEvent: event)
}
/// Intercepts touch ended action.
override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) {
longClickTimer?.invalidate()
super.touchesEnded(touches, withEvent: event)
}
/// Intercepts touch moved action.
override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?) {
longClickTimer?.invalidate()
super.touchesMoved(touches, withEvent: event)
}
/// Intercepts touch cancelled action.
override func touchesCancelled(touches: Set<UITouch>?, withEvent event: UIEvent?) {
longClickTimer?.invalidate()
super.touchesCancelled(touches, withEvent: event)
}
Затем где-то в контроллере нашего представления коллекции объявляется приемник обратного вызова:
let longClickListener: OnLongClickListener = {view in
print("Long click was performed!")
}
И наконец, в cellForItemAtIndexPath установите обратный вызов для наших ячеек:
/// Data population.
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("Cell", forIndexPath: indexPath)
let castedCell = cell as? OurCellView
castedCell?.setOnLongClickListener(longClickListener)
return cell
}
Теперь мы можем перехватывать длинные клики по нашим ячейкам.