本文原鏈:【譯】UICollectionView 輕鬆重排
原文連接:UICollectionViews Now Have Easy Reorderinggit本來打算總結一下 UICollectionView 的一些用法,看到一篇比較好的文章,因此直接翻譯了。翻譯得比較生硬,見諒。github
我超喜歡UICollectionView
。相比UITableView
,它容易自定義得多。如今我使用甚至使用 collection view 比使用 table view 還要頻繁了。在 iOS9 中,它開始支持使用起來很簡單的重排。在以前是不可能直接重排的,並且實現起來很麻煩。讓咱們一塊兒來看看 API。你能夠在 Github 上找到對應的 Xcode 項目。ide
最簡單的實現重排是經過使用UICollectionViewController
。它如今有一個新的屬性叫作installsStandardGestureForInteractiveMovement
,做用是添加手勢(gestures)來重排 cells。這個屬性默認值爲True
,這意味着要使用它咱們只須要重寫一個方法。ui
func collectionView(collectionView: UICollectionView, moveItemAtIndexPath sourceIndexPath: NSIndexPath, toIndexPath destinationIndexPath: NSIndexPath) { // move your data order // 能夠留空 }
當前的 collection view 斷定 items 能夠被移動,由於moveItemAtIndexPath
被重寫了。spa
當咱們但願在一個簡單的UIViewController
中使用 collection view 時,會麻煩一點。咱們也要實現以前提到的UICollectionViewDataSource
方法,不過咱們須要重寫installsStandardGestureForInteractiveMovement
。不用擔憂,也很簡單。UILongPressGestureRecognizer
是一種持續性的手勢識別器而且徹底支持拖動。翻譯
override func viewDidLoad() { super.viewDidLoad() longPressGesture = UILongPressGestureRecognizer(target: self, action: "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() } }
咱們保存了在 long press gesture 中不活的被選中的 index path 而且基於它是否有值決定允不容許拖動手勢生效。而後,咱們根據手勢狀態調用一些新的 collection view 方法。code
beginInteractiveMovementForItemAtIndexPath(indexPath: NSIndexPath)
:開始指定位置 cell 的交互移動。對象
updateInteractiveMovementTargetPosition(targetPosition: CGPoint)
:更新交互移動對象的位置blog
endInteractiveMovement()
:在你結束拖動手勢以後結束交互移動rem
cancelInteractiveMovement()
:取消交互移動
這些讓搞定拖動手勢很是容易。
效果和標準的UICollectionViewController
同樣。很酷對吧,不過更酷的是咱們能夠將咱們自定義的 collection view layout 應用到重排中去。看看下面在簡單的瀑布視圖中的交互移動。
嗯,看起來不錯,不過若是咱們不想在移動的時候改變 cell 大小呢?選中的 cell 大小應該在交互移動時保持一致。這是能夠實現的。UICollectionViewLayout
也有一些其餘的方法來負責重排。
func invalidationContextForInteractivelyMovingItems(targetIndexPaths: [NSIndexPath], withTargetPosition targetPosition: CGPoint, previousIndexPaths: [NSIndexPath], previousPosition: CGPoint) -> UICollectionViewLayoutInvalidationContext func invalidationContextForEndingInteractiveMovementOfItemsToFinalIndexPaths(indexPaths: [NSIndexPath], previousIndexPaths: [NSIndexPath], movementCancelled: Bool) -> UICollectionViewLayoutInvalidationContext
前一個在目標 indexPath 和以前的 indexPath 之間進行移動時調用。另外一個相似,不過是在移動結束以後調用。有了這些咱們就能夠經過一些小手段達到咱們的要求。
internal override func invalidationContextForInteractivelyMovingItems(targetIndexPaths: [NSIndexPath], withTargetPosition targetPosition: CGPoint, previousIndexPaths: [NSIndexPath], previousPosition: CGPoint) -> UICollectionViewLayoutInvalidationContext { var context = super.invalidationContextForInteractivelyMovingItems(targetIndexPaths, withTargetPosition: targetPosition, previousIndexPaths: previousIndexPaths, previousPosition: previousPosition) self.delegate?.collectionView!(self.collectionView!, moveItemAtIndexPath: previousIndexPaths[0], toIndexPath: targetIndexPaths[0]) return context }
解決方案很是清晰。獲取正在移動的 cell 以前和目標 index path。而後調用UICollectionViewDataSource
來移動這些 item。
不用懷疑,collection view 重排是一個很是棒的更新。UIKit 工程師幹得太棒了!:)