長押しジェスチャー認識機能をUICollectionView(のサブクラス)に追加する方法を知りたいと思いました。私はそれがデフォルトで追加されていることをドキュメントで読みましたが、どのように理解することはできません。
私がやりたいのは、セルを長押しして( githubからカレンダーのことです )、どのセルがタップされているのかを取得してから、それを使って処理します。どのセルが長押しされているかを知る必要があります。この広範な質問で申し訳ありませんが、GoogleまたはSOでこれ以上良いものを見つけることができませんでした
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
}
}
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))
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))
Swift用の同じコード@abboodのコード:
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
を忘れないでください
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
}
カスタムの長押しジェスチャー認識機能を追加するためのここの答えは正しいですしかし、ドキュメントによるとhere :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];
}
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;
}
}
外部ジェスチャレコグナイザーを使用し、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 の内部実装に送られます
おそらく、ILongPressGestureRecognizerを使用することが最も普及しているソリューションです。しかし、私はそれに2つの迷惑なトラブルに遭遇します:
少しブルートフォースを提案しますが、提案が必要なように機能します:
セルのロングクリックのコールバックの説明を宣言する:
typealias OnLongClickListener = (view: OurCellView) -> Void
ICollectionViewCellを変数で拡張します(たとえば、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
セルクラスに2つのメソッドを追加します。
/**
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
}
これで、セルに対するロングクリックアクションをインターセプトできます。