Swipe to delete on CollectionView

↘锁芯ラ 提交于 2019-12-01 02:11:37

For my own curiosity's sake I tried to make a replicate of what you're trying to do, and got it to work somehow good. It differs from yours in the way I setup the swipe gestures as I didn't use pan, but you said you already had that part, and haven't spend too much time on it. Pan is obviously the more solid solution to make it interactive, but takes a little longer to calculate, but the effect and handling of it, shouldn't differ much from my example.

To resolve the issue not being able to swipe outside the cell I decided to check if the point was in the swiped rect, which is twice the height of the non-swiped rect like this:

            let cellFrame = activeCell.frame
            let rect = CGRectMake(cellFrame.origin.x, cellFrame.origin.y - cellFrame.height, cellFrame.width, cellFrame.height*2)
            if CGRectContainsPoint(rect, point) {
                // If swipe point is in the cell delete it

                let indexPath = myView.indexPathForCell(activeCell)
                cats.removeAtIndex(indexPath!.row)
                myView.deleteItemsAtIndexPaths([indexPath!])

            }

I created a demonstration with comments: https://github.com/imbue11235/swipeToDeleteCell

I hope it helps you in anyway!

So, if you want the swipes gesture recogniser to continue recording movement when they are outside of their collection view, you need to attach it to the parent of the collection view, so it's bounded to the full area where the user can swipe.

That does mean that you will get swipes for things outside the collection view, but you can quite easily ignore those using any number of techniques.

To register delete button taps, you'll need to call addTarget:action:forControlEvents: on the button

I would keep the cell as you have it, with the image and the button together. It will be much easier to manage, and they belong together.

To manage moving the image up and down, I would look at using a transform, or an NSLayoutConstraint. Then you just have to adjust one value to make it move up and down in sync with the user swipes. No messing with frames.

If you want to make it mare generic:

Make a costume Swipeable View:

import UIKit

class SwipeView: UIView {
lazy var label: UILabel = {
    let label = UILabel()
    label.textColor = .black
    label.backgroundColor = .green
    return label
}()

let visableView = UIView()
var originalPoint: CGPoint!
var maxSwipe: CGFloat! = 50 {
    didSet(newValue) {
        maxSwipe = newValue
    }
}

@IBInspectable var swipeBufffer: CGFloat = 2.0
@IBInspectable var highVelocity: CGFloat = 300.0

private let originalXCenter: CGFloat = UIScreen.main.bounds.width / 2
private var panGesture: UIPanGestureRecognizer!

public var isPanGestureEnabled: Bool {
    get { return panGesture.isEnabled }
    set(newValue) {
        panGesture.isEnabled = newValue
    }
}

override init(frame: CGRect) {
    super.init(frame: frame)
    setupViews()
    setupGesture()
}

private func setupViews() {
    addSubview(visableView)
    visableView.addSubview(label)

    visableView.edgesToSuperview()
    label.edgesToSuperview()

}

private func setupGesture() {
    panGesture = UIPanGestureRecognizer(target: self, action: #selector(swipe(_:)))
    panGesture.delegate = self
    addGestureRecognizer(panGesture)
}

@objc func swipe(_ sender:UIPanGestureRecognizer) {
    let translation = sender.translation(in: self)
    let newXPosition = center.x + translation.x
    let velocity = sender.velocity(in: self)

    switch(sender.state) {

    case .changed:
        let shouldSwipeRight = translation.x > 0 && newXPosition < originalXCenter
        let shouldSwipeLeft = translation.x < 0 && newXPosition > originalXCenter - maxSwipe
        guard shouldSwipeRight || shouldSwipeLeft else { break }
            center.x = newXPosition
    case .ended:
        if -velocity.x > highVelocity {
            center.x = originalXCenter - maxSwipe
            break
        }
        guard center.x > originalXCenter - maxSwipe - swipeBufffer, center.x < originalXCenter - maxSwipe + swipeBufffer, velocity.x < highVelocity  else {
            center.x = originalXCenter
            break
        }
    default:
        break
    }
    panGesture.setTranslation(.zero, in: self)
}

required init?(coder aDecoder: NSCoder) {
    fatalError("init(coder:) has not been implemented")
}

}

extension SwipeView: UIGestureRecognizerDelegate {
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
    return true
}
}

The embed swappable view in UICollectionViewCell:

import UIKit
import TinyConstraints

protocol DeleteCellDelegate {
func deleteCell(_ sender : UIButton)
}

class SwipeableCell: UICollectionViewCell {

lazy var deleteButton: UIButton = {
    let button = UIButton()
    button.backgroundColor = .red
    button.addTarget(self, action: #selector(didPressedButton(_:)), for: .touchUpInside)
    button.titleLabel?.text = "Delete"
    return button
}()

var deleteCellDelegate: DeleteCellDelegate?

@objc private func didPressedButton(_ sender: UIButton) {
    deleteCellDelegate?.deleteCell(sender)
    print("delete")
}
let swipeableview: SwipeView = {
    return SwipeView()
}()

override init(frame: CGRect) {
    super.init(frame: frame)
    addSubview(deleteButton)
    addSubview(swipeableview)
    swipeableview.edgesToSuperview()

    deleteButton.edgesToSuperview(excluding: .left, usingSafeArea: true)
    deleteButton.width(bounds.width * 0.3)
    swipeableview.maxSwipe = deleteButton.bounds.width

}

required init?(coder aDecoder: NSCoder) {
    fatalError("init(coder:) has not been implemented")
}
}

A sample ViewController:

import UIKit
import TinyConstraints

class ViewController: UIViewController, DeleteCellDelegate {

func deleteCell(_ sender: UIButton) {
    let indexPath = IndexPath(item: sender.tag, section: 0)
    items.remove(at: sender.tag)
    collectionView.deleteItems(at: [indexPath])
}

lazy var collectionView: UICollectionView = {
    let layout = UICollectionViewFlowLayout()
    layout.itemSize = CGSize(width: view.bounds.width, height: 40)
    layout.sectionInset = UIEdgeInsets(top: 8, left: 8, bottom: 8, right: 8)
    let cv = UICollectionView(frame: .zero, collectionViewLayout: layout)
    cv.backgroundColor = .yellow
    cv.isPagingEnabled = true
    cv.isUserInteractionEnabled = true
    return cv
}()

var items  = ["1", "2", "3"]

override func viewDidLoad() {
    super.viewDidLoad()
    view.addSubview(collectionView)
    collectionView.delegate = self
    collectionView.dataSource = self
    collectionView.edgesToSuperview(usingSafeArea: true)
    collectionView.register(SwipeableCell.self, forCellWithReuseIdentifier: "cell")
    let panGesture = UIPanGestureRecognizer()
    view.addGestureRecognizer(panGesture)
    panGesture.delegate = self
}

}

extension ViewController: UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
    return items.count
}

func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
    let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! SwipeableCell
    cell.backgroundColor = .blue
    cell.swipeableview.label.text = items[indexPath.item]
    cell.deleteButton.tag = indexPath.item
    cell.deleteCellDelegate = self
    return cell
}

func collectionView(_ collectionView: UICollectionView, performAction action: Selector, forItemAt indexPath: IndexPath, withSender sender: Any?) {
}

func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
}

}

extension ViewController: UIGestureRecognizerDelegate {
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
    return false
}
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!