如何实现UITableView的刷卡删除UICollectionView

我只是想问如何实现UITableView的同样的行为在UICollectionView中删除。 我试图find一个教程,但我找不到任何。

另外,我正在使用PSTCollectionView包装来支持iOS 5。

谢谢!

编辑:滑动识别器已经很好。 我现在需要的是与取消删除模式时相同的function,例如当用户点击单元格或表格视图中的空白区域(即用户在“删除”button之外轻击时)。 UITapGestureRecognizer将无法工作,因为它只检测触摸释放的轻敲。 UITableView检测手势开始时的触摸(而不是释放时),并立即取消删除模式。

适用于iOS集合视图编程指南中 ,在合并手势支持部分中,文档如下:

您应该始终将您的手势识别器附加到集合视图本身,而不是特定的单元格或视图。

所以,我认为向UICollectionViewCell添加识别器并不是一个好习惯。

它非常简单。您需要在customContentView后添加一个customContentViewcustomBackgroundView

之后,当用户从右向左滑动时,需要将customContentView切换到左侧。 移动视图使customBackgroundView可见。

让我们代码:

首先,您需要将panGesture添加到您的UICollectionView

  override func viewDidLoad() { super.viewDidLoad() self.panGesture = UIPanGestureRecognizer(target: self, action: #selector(self.panThisCell)) panGesture.delegate = self self.collectionView.addGestureRecognizer(panGesture) } 

现在实现select器

  func panThisCell(_ recognizer:UIPanGestureRecognizer){ if recognizer != panGesture{ return } let point = recognizer.location(in: self.collectionView) let indexpath = self.collectionView.indexPathForItem(at: point) if indexpath == nil{ return } guard let cell = self.collectionView.cellForItem(at: indexpath!) as? CustomCollectionViewCell else{ return } switch recognizer.state { case .began: cell.startPoint = self.collectionView.convert(point, to: cell) cell.startingRightLayoutConstraintConstant = cell.contentViewRightConstraint.constant if swipeActiveCell != cell && swipeActiveCell != nil{ self.resetConstraintToZero(swipeActiveCell!,animate: true, notifyDelegateDidClose: false) } swipeActiveCell = cell case .changed: let currentPoint = self.collectionView.convert(point, to: cell) let deltaX = currentPoint.x - cell.startPoint.x var panningleft = false if currentPoint.x < cell.startPoint.x{ panningleft = true } if cell.startingRightLayoutConstraintConstant == 0{ if !panningleft{ let constant = max(-deltaX,0) if constant == 0{ self.resetConstraintToZero(cell,animate: true, notifyDelegateDidClose: false) }else{ cell.contentViewRightConstraint.constant = constant } }else{ let constant = min(-deltaX,self.getButtonTotalWidth(cell)) if constant == self.getButtonTotalWidth(cell){ self.setConstraintsToShowAllButtons(cell,animate: true, notifyDelegateDidOpen: false) }else{ cell.contentViewRightConstraint.constant = constant cell.contentViewLeftConstraint.constant = -constant } } }else{ let adjustment = cell.startingRightLayoutConstraintConstant - deltaX; if (!panningleft) { let constant = max(adjustment, 0); if (constant == 0) { self.resetConstraintToZero(cell,animate: true, notifyDelegateDidClose: false) } else { cell.contentViewRightConstraint.constant = constant; } } else { let constant = min(adjustment, self.getButtonTotalWidth(cell)); if (constant == self.getButtonTotalWidth(cell)) { self.setConstraintsToShowAllButtons(cell,animate: true, notifyDelegateDidOpen: false) } else { cell.contentViewRightConstraint.constant = constant; } } cell.contentViewLeftConstraint.constant = -cell.contentViewRightConstraint.constant; } cell.layoutIfNeeded() case .cancelled: if (cell.startingRightLayoutConstraintConstant == 0) { self.resetConstraintToZero(cell,animate: true, notifyDelegateDidClose: true) } else { self.setConstraintsToShowAllButtons(cell,animate: true, notifyDelegateDidOpen: true) } case .ended: if (cell.startingRightLayoutConstraintConstant == 0) { //Cell was opening let halfOfButtonOne = (cell.swipeView.frame).width / 2; if (cell.contentViewRightConstraint.constant >= halfOfButtonOne) { //Open all the way self.setConstraintsToShowAllButtons(cell,animate: true, notifyDelegateDidOpen: true) } else { //Re-close self.resetConstraintToZero(cell,animate: true, notifyDelegateDidClose: true) } } else { //Cell was closing let buttonOnePlusHalfOfButton2 = (cell.swipeView.frame).width if (cell.contentViewRightConstraint.constant >= buttonOnePlusHalfOfButton2) { //Re-open all the way self.setConstraintsToShowAllButtons(cell,animate: true, notifyDelegateDidOpen: true) } else { //Close self.resetConstraintToZero(cell,animate: true, notifyDelegateDidClose: true) } } default: print("default") } } 

帮助者方法来更新约束

  func getButtonTotalWidth(_ cell:CustomCollectionViewCell)->CGFloat{ let width = cell.frame.width - cell.swipeView.frame.minX return width } func resetConstraintToZero(_ cell:CustomCollectionViewCell, animate:Bool,notifyDelegateDidClose:Bool){ if (cell.startingRightLayoutConstraintConstant == 0 && cell.contentViewRightConstraint.constant == 0) { //Already all the way closed, no bounce necessary return; } cell.contentViewRightConstraint.constant = -kBounceValue; cell.contentViewLeftConstraint.constant = kBounceValue; self.updateConstraintsIfNeeded(cell,animated: animate) { cell.contentViewRightConstraint.constant = 0; cell.contentViewLeftConstraint.constant = 0; self.updateConstraintsIfNeeded(cell,animated: animate, completionHandler: { cell.startingRightLayoutConstraintConstant = cell.contentViewRightConstraint.constant; }) } cell.startPoint = CGPoint() swipeActiveCell = nil } func setConstraintsToShowAllButtons(_ cell:CustomCollectionViewCell, animate:Bool,notifyDelegateDidOpen:Bool){ if (cell.startingRightLayoutConstraintConstant == self.getButtonTotalWidth(cell) && cell.contentViewRightConstraint.constant == self.getButtonTotalWidth(cell)) { return; } cell.contentViewLeftConstraint.constant = -self.getButtonTotalWidth(cell) - kBounceValue; cell.contentViewRightConstraint.constant = self.getButtonTotalWidth(cell) + kBounceValue; self.updateConstraintsIfNeeded(cell,animated: animate) { cell.contentViewLeftConstraint.constant = -(self.getButtonTotalWidth(cell)) cell.contentViewRightConstraint.constant = self.getButtonTotalWidth(cell) self.updateConstraintsIfNeeded(cell,animated: animate, completionHandler: {(check) in cell.startingRightLayoutConstraintConstant = cell.contentViewRightConstraint.constant; }) } } func setConstraintsAsSwipe(_ cell:CustomCollectionViewCell, animate:Bool,notifyDelegateDidOpen:Bool){ if (cell.startingRightLayoutConstraintConstant == self.getButtonTotalWidth(cell) && cell.contentViewRightConstraint.constant == self.getButtonTotalWidth(cell)) { return; } cell.contentViewLeftConstraint.constant = -self.getButtonTotalWidth(cell) - kBounceValue; cell.contentViewRightConstraint.constant = self.getButtonTotalWidth(cell) + kBounceValue; self.updateConstraintsIfNeeded(cell,animated: animate) { cell.contentViewLeftConstraint.constant = -(self.getButtonTotalWidth(cell)) cell.contentViewRightConstraint.constant = self.getButtonTotalWidth(cell) self.updateConstraintsIfNeeded(cell,animated: animate, completionHandler: {(check) in cell.startingRightLayoutConstraintConstant = cell.contentViewRightConstraint.constant; }) } } func updateConstraintsIfNeeded(_ cell:CustomCollectionViewCell, animated:Bool,completionHandler:@escaping ()->()) { var duration:Double = 0 if animated{ duration = 0.1 } UIView.animate(withDuration: duration, delay: 0, options: [.curveEaseOut], animations: { cell.layoutIfNeeded() }, completion:{ value in if value{ completionHandler() } }) } 

我在Swift 3中创build了一个示例项目。

这是本教程的修改版本。

我遵循@JacekLampart类似的方法,但决定在UICollectionViewCell的awakeFromNib函数中添加UISwipeGestureRecognizer,所以只添加一次。

UICollectionViewCell.m

 - (void)awakeFromNib { UISwipeGestureRecognizer* swipeGestureRecognizer = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(swipeToDeleteGesture:)]; swipeGestureRecognizer.direction = UISwipeGestureRecognizerDirectionLeft; [self addGestureRecognizer:swipeGestureRecognizer]; } - (void)swipeToDeleteGesture:(UISwipeGestureRecognizer *)swipeGestureRecognizer { if (swipeGestureRecognizer.state == UIGestureRecognizerStateEnded) { // update cell to display delete functionality } } 

至于退出删除模式,我用UIViews的NSArray创build了一个自定义的UIGestureRecognizer。 我从这个问题借鉴了@iMS的想法: UITapGestureRecognizer – 使它在触摸下工作,而不是触摸?

触摸开始,如果触点不在任何UIViews中,则手势成功,退出删除模式。

通过这种方式,我可以将单元格内的删除button(以及任何其他视图)传递给UIGestureRecognizer,如果触摸点位于button的框架内,删除模式将不会退出。

TouchDownExcludingViewsGestureRecognizer.h

 #import <UIKit/UIKit.h> @interface TouchDownExcludingViewsGestureRecognizer : UIGestureRecognizer @property (nonatomic) NSArray *excludeViews; @end 

TouchDownExcludingViewsGestureRecognizer.m

 #import "TouchDownExcludingViewsGestureRecognizer.h" #import <UIKit/UIGestureRecognizerSubclass.h> @implementation TouchDownExcludingViewsGestureRecognizer - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { if (self.state == UIGestureRecognizerStatePossible) { BOOL touchHandled = NO; for (UIView *view in self.excludeViews) { CGPoint touchLocation = [[touches anyObject] locationInView:view]; if (CGRectContainsPoint(view.bounds, touchLocation)) { touchHandled = YES; break; } } self.state = (touchHandled ? UIGestureRecognizerStateFailed : UIGestureRecognizerStateRecognized); } } - (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event { self.state = UIGestureRecognizerStateFailed; } -(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event { self.state = UIGestureRecognizerStateFailed; } @end 

实现(在包含UICollectionView的UIViewController中):

 #import "TouchDownExcludingViewsGestureRecognizer.h" TouchDownExcludingViewsGestureRecognizer *touchDownGestureRecognizer = [[TouchDownExcludingViewsGestureRecognizer alloc] initWithTarget:self action:@selector(exitDeleteMode:)]; touchDownGestureRecognizer.excludeViews = @[self.cellInDeleteMode.deleteButton]; [self.view addGestureRecognizer:touchDownGestureRecognizer]; - (void)exitDeleteMode:(TouchDownExcludingViewsGestureRecognizer *)touchDownGestureRecognizer { // exit delete mode and disable or remove TouchDownExcludingViewsGestureRecognizer } 

您可以尝试为每个收集单元添加一个UISwipeGestureRecognizer,如下所示:

 -(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath { CollectionViewCell *cell = ... UISwipeGestureRecognizer* gestureRecognizer = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(userDidSwipe:)]; [gestureRecognizer setDirection:UISwipeGestureRecognizerDirectionRight]; [cell addGestureRecognizer:gestureRecognizer]; } 

其次是:

 - (void)userDidSwipe:(UIGestureRecognizer *)gestureRecognizer { if (gestureRecognizer.state == UIGestureRecognizerStateEnded) { //handle the gesture appropriately } }