, поэтому я создаю свою собственную систему Drag & Drop.Чтобы это работало, мне нужен способ создать «пробелы» между ячейками в collectionViews
, где пользователь наводит перетаскиваемые элементы.
Я сейчас пробую материал и получил базовую демонстрацию, работающую с использованиемпользовательский макет потока, который перемещает ячейки вокруг.
Демонстрация, которую я сделал, состоит из простого collectionView
(с использованием IGListKit
, но это не имеет значения для этой проблемы, я уверен, я уверен) и UIPanGestureRecognizer
, который позволяет вам перемещаться по collectionView, чтобы создать промежуток под вашим пальцем.
Я добиваюсь этого путем отмены макета при каждом изменении pan gesture reconizer
.Это работает таким образом, но когда я одновременно прокручиваю по панораме collectionView
, ячейки, кажется, немного сбиваются.Это выглядит так (похоже, что рендеринг ячеек не может идти в ногу):
Я почти уверен, что проблема в том,в функции makeAGap
, которая содержит этот вызов:
collectionView?.performBatchUpdates({
self.invalidateLayout()
self.collectionView?.layoutIfNeeded()
}, completion: nil)
Если я не анимирую аннулирование, как это
self.invalidateLayout()
self.collectionView?.layoutIfNeeded()
Сбой вообще не появляется.Это как-то связано с анимацией.Есть ли у вас какие-либо идеи?
Спасибо
PS: вот код (есть еще IGListKit
вещи, но это не важно):
class MyCustomLayout: UICollectionViewFlowLayout {
fileprivate var cellPadding: CGFloat = 6
fileprivate var cache = [UICollectionViewLayoutAttributes]()
fileprivate var contentHeight: CGFloat = 300
fileprivate var contentWidth: CGFloat = 0
var gap: IndexPath? = nil
var gapPosition: CGPoint? = nil
override var collectionViewContentSize: CGSize {
return CGSize(width: contentWidth, height: contentHeight)
}
override func prepare() {
// cache contains the cached layout attributes
guard cache.isEmpty, let collectionView = collectionView else {
return
}
// I'm using IGListKit, so every cell is in its own section in my case
for section in 0..<collectionView.numberOfSections {
let indexPath = IndexPath(item: 0, section: section)
// If a gap has been set, just make the current offset (contentWidth) bigger to
// simulate a "missing" item, which creates a gap
if let gapPosition = self.gapPosition {
if gapPosition.x >= (contentWidth - 100) && gapPosition.x < (contentWidth + 100) {
contentWidth += 100
}
}
// contentWidth is used as x origin
let frame = CGRect(x: contentWidth, y: 10, width: 100, height: contentHeight)
contentWidth += frame.width + cellPadding
let attributes = UICollectionViewLayoutAttributes(forCellWith: indexPath)
attributes.frame = frame
cache.append(attributes)
}
}
public func makeAGap(at indexPath: IndexPath, position: CGPoint) {
gap = indexPath
self.cache = []
self.contentWidth = 0
self.gapPosition = position
collectionView?.performBatchUpdates({
self.invalidateLayout()
self.collectionView?.layoutIfNeeded()
}, completion: nil)
//invalidateLayout() // Using this, the glitch does NOT appear
}
override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
var visibleLayoutAttributes = [UICollectionViewLayoutAttributes]()
// Loop through the cache and look for items in the rect
for attributes in cache {
if attributes.frame.intersects(rect) {
visibleLayoutAttributes.append(attributes)
}
}
return visibleLayoutAttributes
}
override func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
return cache[indexPath.item]
}
}
class ViewController: UIViewController {
/// IGListKit stuff: Data for self.collectionView ("its cells", which represent the rows)
public var data: [String] {
return (0...100).compactMap { "\($0)" }
}
/// This collectionView will consist of cells, that each have their own collectionView.
private lazy var collectionView: UICollectionView = {
let layout = MyCustomLayout()
layout.scrollDirection = .horizontal
let collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout)
collectionView.backgroundColor = UIColor(hex: 0xeeeeee)
adapter.collectionView = collectionView
adapter.dataSource = self
view.addSubview(collectionView)
collectionView.translatesAutoresizingMaskIntoConstraints = false
collectionView.frame = CGRect(x: 0, y: 50, width: 1000, height: 300)
return collectionView
}()
/// IGListKit stuff. Data manager for the collectionView
private lazy var adapter: ListAdapter = {
let adapter = ListAdapter(updater: ListAdapterUpdater(), viewController: self, workingRangeSize: 0)
return adapter
}()
override func viewDidLoad() {
super.viewDidLoad()
_ = collectionView
let pan = UIPanGestureRecognizer(target: self, action: #selector(handlePan(pan:)))
pan.maximumNumberOfTouches = 1
pan.delegate = self
view.addGestureRecognizer(pan)
}
@objc private func handlePan(pan: UIPanGestureRecognizer) {
guard let indexPath = collectionView.indexPathForItem(at: pan.location(in: collectionView)) else {
return
}
(collectionView.collectionViewLayout as? MyCustomLayout)?.makeAGap(at: indexPath, position: pan.location(in: collectionView))
}
}
extension ViewController: ListAdapterDataSource, UIGestureRecognizerDelegate {
func objects(for listAdapter: ListAdapter) -> [ListDiffable] {
return data as [ListDiffable]
}
func listAdapter(_ listAdapter: ListAdapter, sectionControllerFor object: Any) -> ListSectionController {
return RowListSection()
}
func emptyView(for listAdapter: ListAdapter) -> UIView? {
return nil
}
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
return true
}
}