長押しジェスチャー認識機能をUICollectionView(のサブクラス)に追加する方法を知りたいと思っていました。私はそれがデフォルトで追加されることをドキュメントで読みましたが、私は方法を理解することができません。
私がやりたいことは、セルを長押しします(私はgithubからカレンダーを用意しています)。どの細胞が長押しされているかを知る必要があります。この幅広い質問でごめんなさい、しかし私はグーグルでもSOでももっと良いものを見つけることができませんでした
長押しジェスチャー認識機能をUICollectionView(のサブクラス)に追加する方法を知りたいと思っていました。私はそれがデフォルトで追加されることをドキュメントで読みましたが、私は方法を理解することができません。
私がやりたいことは、セルを長押しします(私はgithubからカレンダーを用意しています)。どの細胞が長押しされているかを知る必要があります。この幅広い質問でごめんなさい、しかし私はグーグルでも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))
lpgr.delaysTouchesBegan = YES;
、didHighlightItemAtIndexPath
最初にトリガーされるのを避けるために追加する必要があります。
lpgr.delegate = self;
ですか?あなたが提供しなかったデリゲートなしでもうまく動作します。
UIGestureRecognizerStateBegan
するので、ジェスチャーはユーザーが指を離したときではなく、認識されたときに使用されます。
同じコード@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
UIGestureRecognizerState.Ended
するUIGestureRecognizerState.Began
ように変更します。
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
}
カスタム長押しジェスチャー認識機能を追加するためのここでの回答は正しいですが、ここのドキュメントによると、クラスの親クラスはスクロール操作を処理するためにを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];
}
The parent class of UICollectionView class installs a default tap gesture recognizer and a default long-press gesture recognizer to handle scrolling interactions. You should never try to reconfigure these default gesture recognizers or replace them with your own versions.
ています:したがって、デフォルトの長押し認識機能はスクロール用に作られています..これは、垂直方向の動きを伴う必要があることを意味します。OPは要求していませんその種の行動についても、彼がそれを置き換えようとしているのでもない
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の内部実装に行きます
private func setupLongGestureRecognizerOnCollection() {
let longPressedGesture = UILongPressGestureRecognizer(target: self, action: #selector(handleLongPress(gestureRecognizer:)))
longPressedGesture.minimumPressDuration = 0.5
longPressedGesture.delegate = self
longPressedGesture.delaysTouchesBegan = true
collectionView?.addGestureRecognizer(longPressedGesture)
}
@objc func handleLongPress(gestureRecognizer: UILongPressGestureRecognizer) {
if (gestureRecognizer.state != .began) {
return
}
let p = gestureRecognizer.location(in: collectionView)
if let indexPath = collectionView?.indexPathForItem(at: p) {
print("Long press at item: \(indexPath.row)")
}
}
また、UIGestureRecognizerDelegateを実装し、viewDidLoadまたはそれを呼び出す必要がある場所からsetupLongGestureRecognizerOnCollectionを呼び出すことを忘れないでください。
おそらく、UILongPressGestureRecognizerを使用することが最も普及しているソリューションです。しかし、私は2つの厄介な問題に遭遇しました。
少し力ずくで提案しますが、必要な提案どおりに機能します。
セルを長押しするためのコールバックの説明を宣言します。
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
セルクラスに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
}
これで、セルのロングクリックアクションをインターセプトできます。
UICollectionViewCell* cell = [self.collectionView cellForItemAtIndexPath:indexPath];
参照はすべてこれが正解の賞に値することを願っています:D