在CollectionView上滑动以删除

问题描述 投票:22回答:3

我正在尝试复制滑动以删除函数,就像在mailviewview中一样。只有这次我需要在收集视图上构建它,但我有点困难。这是一个水平滚动列表,向上滑动即可删除。我已经完成了向上滑动,但很难搞清楚我需要如何设置滑动以删除/点按以删除或忽略功能。

它看起来如下:Delete Image

所以我使用以下collectionview:

func buildCollectionView() {
    let layout: UICollectionViewFlowLayout = UICollectionViewFlowLayout()
    layout.scrollDirection = .horizontal
    layout.minimumInteritemSpacing = 0;
    layout.minimumLineSpacing = 4;

    collectionView = UICollectionView(frame: CGRect(x: 0, y: screenSize.midY - 120, width: screenSize.width, height: 180), collectionViewLayout: layout)

    collectionView.dataSource = self
    collectionView.delegate = self
    collectionView.register(VideoCell.self, forCellWithReuseIdentifier: "videoCell")
    collectionView.showsHorizontalScrollIndicator = false
    collectionView.showsVerticalScrollIndicator = false
    collectionView.contentInset = UIEdgeInsetsMake(0, 20, 0, 30)
    collectionView.backgroundColor = UIColor.white()
    collectionView.alpha = 0.0


    //can swipe cells outside collectionview region
    collectionView.layer.masksToBounds = false


    swipeUpRecognizer = UIPanGestureRecognizer(target: self, action: #selector(self.deleteCell))
    swipeUpRecognizer.delegate = self

    collectionView.addGestureRecognizer(swipeUpRecognizer)
    collectionView.isUserInteractionEnabled = true
}

我的自定义视频聊天包含一个图像,下面是删除按钮。因此,如果您向上滑动图像,则会弹出删除按钮。不确定这是否是正确的方法:

class VideoCell : UICollectionViewCell {
var deleteView: UIButton!
var imageView: UIImageView!

override init(frame: CGRect) {
    super.init(frame: frame)

    deleteView = UIButton(frame: CGRect(x: 0, y: 0, width: frame.size.width, height: frame.size.height))
    deleteView.contentMode = UIViewContentMode.scaleAspectFit
    contentView.addSubview(deleteView)

    imageView = UIImageView(frame: CGRect(x: 0, y: 0, width: frame.size.width, height: frame.size.height))
    imageView.contentMode = UIViewContentMode.scaleAspectFit
    contentView.addSubview(imageView)


}

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

我正在使用以下逻辑:

func deleteCell(sender: UIPanGestureRecognizer) {
    let tapLocation = sender.location(in: self.collectionView)
    let indexPath = self.collectionView.indexPathForItem(at: tapLocation)

    if velocity.y < 0 { 
        //detect if there is a swipe up and detect it's distance. If the distance is far enough we snap the cells Imageview to the top otherwise we drop it back down. This works fine already.
    }
}

但问题从那里开始。一旦我的单元格在collectionview边界之外,我就再也无法访问它了。我仍然想进一步刷它以删除它。我只能通过在删除按钮上滑动来完成此操作,但我希望它上面的Imageview也可以刷卡。或者,如果我点击集合视图外部的图像,它应该滑回到行中而不是删除它。

如果我增加了collectionview边界,我可以防止这个问题,但我也可以滑动以移出单元格的可见高度。这是由collectionview中的tapLocation引起的,它检测到indexPath。我不想要的东西。我只想向上滑动以在collectionview的单元格上工作。

按钮和图像也相互干扰,因为我无法区分它们。它们都在同一个单元格中,这就是为什么我想知道我是否应该在单元格中有删除按钮。或者我应该在哪里放置它?我也可以制作两个按钮并根据状态禁用用户交互,但不确定最终会如何。

ios swift uipangesturerecognizer collectionview
3个回答
4
投票

为了我自己的好奇心,我试图复制你想要做的事情,并让它以某种方式工作。它与我的不同之处在于我设置滑动手势的方式,因为我没有使用平移,但是你说你已经有了这个部分,并且没有花太多时间在它上面。 Pan显然是使其具有交互性的更可靠的解决方案,但需要更长的时间来计算,但它的效果和处理与我的例子不应有太大差别。

为了解决无法在单元格外滑动的问题,我决定检查点是否在滑动的矩形中,这是非滑动矩形的高度的两倍,如下所示:

            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!])

            }

我创建了一个带有评论的演示:https://github.com/imbue11235/swipeToDeleteCell

无论如何,我希望它可以帮到你!


9
投票

因此,如果您希望滑动手势识别器在它们位于其集合视图之外时继续记录移动,则需要将其附加到集合视图的父级,因此它被限制在用户可以滑动的整个区域。

这意味着您将在集合视图之外的内容中进行滑动,但您可以很容易地忽略使用任意数量技术的内容。

要注册删除按钮,您需要在按钮上调用addTarget:action:forControlEvents :.

我会保留你的单元格,图像和按钮在一起。它将更容易管理,并且它们属于一体。

要管理上下移动图像,我会考虑使用变换或NSLayoutConstraint。然后,您只需调整一个值,使其与用户滑动同步上下移动。没有弄乱框架。


0
投票

如果你想让它成为通用的:

制作服装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
}
}

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")
}
}

一个示例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
}
}
© www.soinside.com 2019 - 2024. All rights reserved.