Подтвердить что ты не робот

Проведите по экрану в 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 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.
    }
}

Но проблема начинается там. Как только моя ячейка находится за пределами границ коллекции, я больше не могу ее получить. Я все еще хочу провести его дальше, чтобы удалить его. Я могу сделать это только, щелкнув по кнопке удаления, но я хочу, чтобы Imageview над ним также был прокручиваем. Или, если я коснусь изображения вне коллекции, он должен вернуться в строку и не удалить его.

Если я увеличиваю границы коллекции, я могу предотвратить эту проблему, но я также могу прокрутить, чтобы удалить за пределами видимой высоты ячейки. Это вызвано ссылкой tapLocation, которая находится внутри коллекции и обнаруживает indexPath. Что-то, чего я не хочу. Я хочу, чтобы проведите по экрану только для работы с ячейкой коллекции.

Также кнопка и изображение мешают друг другу, потому что я не могу их отличить. Они оба находятся в одной и той же ячейке, поэтому мне интересно, есть ли у меня кнопка удаления в ячейке. Или где я должен разместить его иначе? Я мог бы также сделать из него две кнопки и отключить взаимодействие с пользователем в зависимости от состояния, но не знаю, как это закончится.

4b9b3361

Ответ 1

Ради собственного любопытства я попытался воспроизвести то, что вы пытаетесь сделать, и заставил его работать как-то хорошо. Он отличается от вашего в том, как я настраиваю жесты смахивания, поскольку я не использовал панорамирование, но вы сказали, что у вас уже была эта часть, и вы не тратили на нее слишком много времени. Очевидно, что панорамирование - более надежное решение, чтобы сделать его интерактивным, но его вычисление занимает немного больше времени, но эффект и обработка этого не должны сильно отличаться от моего примера.

Чтобы решить проблему, связанную с невозможностью провести вне ячейки, я решил проверить, была ли точка в прямоугольнике со смахиванием, что в два раза больше высоты прямоугольника без пролистывания, например:

            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

Я надеюсь, что это поможет вам в любом случае!

Ответ 2

Итак, если вы хотите, чтобы распознаватель жестов распознавания продолжал записывать движение, когда он находится вне своего вида коллекции, вам необходимо прикрепить его к родительскому объекту коллекции, поэтому он ограничен всей областью, в которой пользователь может прокручивать.

Это означает, что вы получите подсказки для вещей вне представления коллекции, но вы можете легко игнорировать тех, кто использует любое количество методов.

Чтобы зарегистрировать кнопки удаления кнопки, вам нужно вызвать addTarget: действие: forControlEvents: на кнопке

Я бы сохранил ячейку, как у вас, с изображением и кнопкой вместе. Нам будет намного легче управлять, и они будут вместе.

Чтобы управлять перемещением изображения вверх и вниз, я бы посмотрел на использование преобразования или NSLayoutConstraint. Затем вам нужно настроить одно значение, чтобы оно перемещалось вверх и вниз в синхронизации с пользовательскими прогибами. Не вмешиваться в рамки.

Ответ 3

Если вы хотите сделать его общим:

Сделать костюм 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
}
}