最近在實現類似網易新聞的首頁滑塊的編輯效果: 長按後進入編輯介面, 然後可以通過拖拽實現cell的移動, 研究後發現兩種實現方式: 第一種是直接利用系統提供的UICollectionView API實現移動, 不過只能在iOS9上面使用. 所以這裡就介紹另外一種方式.
原始碼效果示例:
原理部分
- 新增一個長按手勢到UICollectionView上
- 在這個手勢的selector中通過獲取到當前手勢在collectionVIew的location來獲取到一個indexPath, 如果這個indexPath是有效的, 那麼就可以通過這個indexPath獲取到對應的cell.
- 將獲取到的cell截圖, 然後將這個cell隱藏, 通過設定這個截圖的frame使得這個截圖跟隨手指同步移動, 如果截圖移動到了另外一個cell的位置, 則通過呼叫collectionView的方法將這兩個cell交換位置, 同時需要更新collectionView的dataSource.
實現部分
- 在collectionView上新增一個長按手勢
12let longGes = UILongPressGestureRecognizer(target: self, action: #selector(self.longPressd(_:)))collectionView.addGestureRecognizer(longGes)
2 在selector中處理手勢的響應
- 2.1 獲取當前手勢的位置
1let location = ges.locationInView(self.collectionView)
- 2.2 獲取這個位置對應的在collectionView中的indexPath, 注意這個indexPath可能為nil(比如手指沒有在cell的位置上時)
12// 當手指的位置不在collectionView的cell範圍內時為nillet notSureIndexPath = self.collectionView.indexPathForItemAtPoint(location) - 2.3 通過當前手勢的狀態不同的處理
遍歷狀態.png
- 2.3.1 case .Began 手勢開始的時候
1234567891011121314151617if let indexPath = notSureIndexPath { // 獲取到的indexPath是有效的, 可以放心使用// a.currentIndexPath = indexPath// b.let cell = collectionView.cellForItemAtIndexPath(indexPath)!// c.snapedImageView = getTheCellSnap(cell)deltaSize = CGSize(width: location.x - cell.frame.origin.x, height: location.y - cell.frame.origin.y)// d.snapedImageView.center = cell.centersnapedImageView.transform = CGAffineTransformMakeScale(1.1, 1.1)// e.cell.alpha = 0.0// f.collectionView.addSubview(snapedImageView)}
a. 記錄下當前的indexPath以便於在手指移動的過程中進入.Changed狀態的時候使用
b. 通過這個indexPath獲取到對應的cell
c. 獲取到這個cell截圖
d. 並且設定截圖的初始位置
e. 隱藏當前的cell
f. 將截圖新增到collectionView中
- 2.3.2 case .Changed 手指在移動的時候
1 2 3 4 5 6 7 8 |
// a. if snapedImageView == nil { return } // b. snapedImageView.frame.origin.x = location.x - deltaSize.width snapedImageView.frame.origin.y = location.y - deltaSize.height // c. if let newIndexPath = notSureIndexPath, let oldIndexPath = currentIndexPath { if newIndexPath != oldIndexPath |
a. 如果在began狀態中沒有獲取到截圖直接返回
b. 設定截圖的位置, 以達到和手指同步移動
c. 如果新獲取到的indexPath有效並且和原來的不相同
d. 移動cell, 更新dataSource
e. 設定新的cell的屬性
f. 更新當前的indexPath
- 2.3.3 case .End 手指離開螢幕的時候
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
if let oldIndexPath = currentIndexPath { // a. let cell = collectionView.cellForItemAtIndexPath(oldIndexPath)! // b. UIView.animateWithDuration(0.25, animations: {[unowned self] in self.snapedImageView.transform = CGAffineTransformIdentity self.snapedImageView.frame = cell.frame }, completion: {[unowned self] (_) in self.snapedImageView.removeFromSuperview() self.snapedImageView = nil self.currentIndexPath = nil cell.alpha = 1.0 }) } |
a. 獲取到當前移動完成的cell
b. 使用動畫移除截圖並且設定當前的移動完成的cell的屬性
到目前位置, 設定好collectionView的datasource和delegate方法後就可以實現以下的效果了
詳細請移步原始碼, 如果您覺得有幫助,不妨給個star鼓勵一下, 歡迎關注