vik*_*lla 15 ios uicollectionview swift
我可以像这样重新排序我的collectionView:
但是,我不想将所有细胞水平移动,而是只想交换以下行为(即更少的细胞洗牌):
我一直在使用以下委托方法:
func collectionView(_ collectionView: UICollectionView, targetIndexPathForMoveFromItemAt originalIndexPath: IndexPath, toProposedIndexPath proposedIndexPath: IndexPath) -> IndexPath
Run Code Online (Sandbox Code Playgroud)
但是,我不确定如何实现自定义重新排序行为.
Los*_*aty 36
我设法通过创建子类UICollectionView
并为交互式移动添加自定义处理来实现此目的.在查看如何解决问题的可能提示时,我发现了本教程:http://nshint.io/blog/2015/07/16/uicollectionviews-now-have-easy-reordering/.最重要的部分是交互式重新排序不仅可以进行UICollectionViewController
.相关代码如下所示:
var longPressGesture : UILongPressGestureRecognizer!
override func viewDidLoad() {
super.viewDidLoad()
// rest of setup
longPressGesture = UILongPressGestureRecognizer(target: self, action: #selector(ViewController.handleLongGesture(_:)))
self.collectionView?.addGestureRecognizer(longPressGesture)
}
func handleLongGesture(gesture: UILongPressGestureRecognizer) {
switch(gesture.state) {
case UIGestureRecognizerState.Began:
guard let selectedIndexPath = self.collectionView?.indexPathForItemAtPoint(gesture.locationInView(self.collectionView)) else {
break
}
collectionView?.beginInteractiveMovementForItemAtIndexPath(selectedIndexPath)
case UIGestureRecognizerState.Changed:
collectionView?.updateInteractiveMovementTargetPosition(gesture.locationInView(gesture.view!))
case UIGestureRecognizerState.Ended:
collectionView?.endInteractiveMovement()
default:
collectionView?.cancelInteractiveMovement()
}
}
Run Code Online (Sandbox Code Playgroud)
这需要位于放置集合视图的视图控制器中.我不知道这是否UICollectionViewController
合适,可能需要一些额外的修补.让我进行子类化的原因UICollectionView
是认识到所有其他相关的类/委托方法只被告知第一个和最后一个索引路径(即源和目标),并且没有关于重新排列的所有其他单元的信息,所以它需要被阻止在核心.
SwappingCollectionView.swift:
import UIKit
extension UIView {
func snapshot() -> UIImage {
UIGraphicsBeginImageContext(self.bounds.size)
self.layer.renderInContext(UIGraphicsGetCurrentContext()!)
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return image
}
}
extension CGPoint {
func distanceToPoint(p:CGPoint) -> CGFloat {
return sqrt(pow((p.x - x), 2) + pow((p.y - y), 2))
}
}
struct SwapDescription : Hashable {
var firstItem : Int
var secondItem : Int
var hashValue: Int {
get {
return (firstItem * 10) + secondItem
}
}
}
func ==(lhs: SwapDescription, rhs: SwapDescription) -> Bool {
return lhs.firstItem == rhs.firstItem && lhs.secondItem == rhs.secondItem
}
class SwappingCollectionView: UICollectionView {
var interactiveIndexPath : NSIndexPath?
var interactiveView : UIView?
var interactiveCell : UICollectionViewCell?
var swapSet : Set<SwapDescription> = Set()
var previousPoint : CGPoint?
static let distanceDelta:CGFloat = 2 // adjust as needed
override func beginInteractiveMovementForItemAtIndexPath(indexPath: NSIndexPath) -> Bool {
self.interactiveIndexPath = indexPath
self.interactiveCell = self.cellForItemAtIndexPath(indexPath)
self.interactiveView = UIImageView(image: self.interactiveCell?.snapshot())
self.interactiveView?.frame = self.interactiveCell!.frame
self.addSubview(self.interactiveView!)
self.bringSubviewToFront(self.interactiveView!)
self.interactiveCell?.hidden = true
return true
}
override func updateInteractiveMovementTargetPosition(targetPosition: CGPoint) {
if (self.shouldSwap(targetPosition)) {
if let hoverIndexPath = self.indexPathForItemAtPoint(targetPosition), let interactiveIndexPath = self.interactiveIndexPath {
let swapDescription = SwapDescription(firstItem: interactiveIndexPath.item, secondItem: hoverIndexPath.item)
if (!self.swapSet.contains(swapDescription)) {
self.swapSet.insert(swapDescription)
self.performBatchUpdates({
self.moveItemAtIndexPath(interactiveIndexPath, toIndexPath: hoverIndexPath)
self.moveItemAtIndexPath(hoverIndexPath, toIndexPath: interactiveIndexPath)
}, completion: {(finished) in
self.swapSet.remove(swapDescription)
self.dataSource?.collectionView(self, moveItemAtIndexPath: interactiveIndexPath, toIndexPath: hoverIndexPath)
self.interactiveIndexPath = hoverIndexPath
})
}
}
}
self.interactiveView?.center = targetPosition
self.previousPoint = targetPosition
}
override func endInteractiveMovement() {
self.cleanup()
}
override func cancelInteractiveMovement() {
self.cleanup()
}
func cleanup() {
self.interactiveCell?.hidden = false
self.interactiveView?.removeFromSuperview()
self.interactiveView = nil
self.interactiveCell = nil
self.interactiveIndexPath = nil
self.previousPoint = nil
self.swapSet.removeAll()
}
func shouldSwap(newPoint: CGPoint) -> Bool {
if let previousPoint = self.previousPoint {
let distance = previousPoint.distanceToPoint(newPoint)
return distance < SwappingCollectionView.distanceDelta
}
return false
}
}
Run Code Online (Sandbox Code Playgroud)
我确实意识到那里有很多事情,但我希望一切都会在一分钟内清楚.
UIView
使用辅助方法扩展以获取单元格的快照.CGPoint
使用辅助方法扩展以计算两点之间的距离.SwapDescription
辅助结构 - 需要防止同一对项目的多次交换,这导致了毛刺的动画.它的hashValue
方法可以改进,但为了这个概念证明就足够了.beginInteractiveMovementForItemAtIndexPath(indexPath: NSIndexPath) -> Bool
- 移动开始时调用的方法.一切都在这里设置.我们获取单元格的快照并将其添加为子视图 - 此快照将是用户实际在屏幕上拖动的内容.细胞本身被隐藏起来.如果false
从此方法返回,则不会开始交互式移动.updateInteractiveMovementTargetPosition(targetPosition: CGPoint)
- 每个用户移动后调用的方法,这很多.我们检查,如果从以前的点的距离小到足以交换项目 - 这样可以防止问题,当用户将快速拖动屏幕对面和多个项目将得到与非明显的效果交换.如果交换可能发生,我们检查它是否已经发生,如果不是,我们交换两个项目.endInteractiveMovement()
,cancelInteractiveMovement()
,cleanup()
-移动结束后,我们需要把我们的帮手恢复到默认状态.shouldSwap(newPoint: CGPoint) -> Bool
- 辅助方法检查运动是否足够小,以便我们可以交换细胞.这是它给出的结果:
请告诉我这是否是您需要的和/或您是否需要我澄清一些事情.
这是一个演示项目.
归档时间: |
|
查看次数: |
7484 次 |
最近记录: |