我想知道如何向 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))
与 @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
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 。
使用UICollectionView的delegate接收长按事件
您必须实现以下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];
}
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;
}
的内部实现
也许,使用 UILongPressGestureRecognizer 是最广泛的解决方案。但我遇到了两个烦人的麻烦:
让我建议一个有点暴力的建议,但要按照要求的建议工作:
声明长按单元格的回调描述:
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
}
现在我们可以拦截单元格上的长点击操作。
更简单的解决方案。
在您的 cellForItemAt 委托中(稍后设置 .tag 属性):
cell.gestureRecognizers?.removeAll()
cell.tag = indexPath.row
let directFullPreviewer = UILongPressGestureRecognizer(target: self, action: #selector(directFullPreviewLongPressAction))
cell.addGestureRecognizer(directFullPreviewer)
长按回调:
@objc func directFullPreviewLongPressAction(g: UILongPressGestureRecognizer)
{
if g.state == UIGestureRecognizer.State.began
{
// Get index as g.view.tag and that's it
}
}
func collectionView(_ collectionView: UICollectionView, contextMenuConfigurationForItemAt indexPath: IndexPath, point: CGPoint) -> UIContextMenuConfiguration?
可以在现代 iOS 版本上使用。
从 iOS 16 开始
optional func collectionView(
_ collectionView: UICollectionView,
contextMenuConfigurationForItemsAt indexPaths: [IndexPath],
point: CGPoint
) -> UIContextMenuConfiguration?
https://developer.apple.com/documentation/uikit/uicollectionviewdelegate/4002186-collectionview