在《今日頭條》中,該頁面是用來選擇自己感興趣的頻道標籤從而改變segment的。標籤功能應用的需求現在也比較多,主要使用collectionview中item可以移動的方法和思路來寫這樣的頁面。
關鍵點
-
在collectionview上添加的長按手勢
- 手勢狀態的變化和操作
- 手勢開始移動的時候調用此方法,可以獲取相應的datasource方法設置特殊的indexpath 能否移動,如果能移動返回的是YES ,不能移動返回的是NO , 這個方法的返回值由根據下面的canMoveItemAtIndexPath決定的
- (BOOL)beginInteractiveMovementForItemAtIndexPath:(NSIndexPath *)indexPath
- 更新移動過程的位置, 在移動的過程中會調用下面的collectionView: targetIndexPathForMoveFromItemAtIndexPath: toProposedIndexPath:
- (void)updateInteractiveMovementTargetPosition:(CGPoint)targetPosition
- 結束移動的時候調用此方法,collectionView 會響應相應的datasource方法,collectionView:moveItemAtIndexPath:toIndexPath: 我們可以在這個方法中將移動的數據源,與目標數據源交互位置。
- (void)endInteractiveMovement
- 取消移動的時候調用,會返回最原始的位置。
- (void)cancelInteractiveMovement;
- 在開始移動的時候會調用這個方法,如果有特殊的單元格不想被移動可以return NO, 如果沒有限制就返回YES 吧。
- (BOOL)collectionView:(UICollectionView *)collectionView canMoveItemAtIndexPath:(NSIndexPath *)indexPath
參數 | 說明 |
---|---|
collectionView | 調用這個代理方法的 collection View |
indexPath | collectionView 中想要移動的 indexpath |
- 移動過程中,會不斷的調用這個方法, originalIndexPath 能否移動到proposedIndexPath , 如果不重寫的話,會默認返回proposedIndexPath; 可以通過返回nil, 告知系統不可以移動到此位置
- (NSIndexPath *)collectionView:(UICollectionView *)collectionView targetIndexPathForMoveFromItemAtIndexPath:(NSIndexPath *)originalIndexPath toProposedIndexPath:(NSIndexPath *)proposedIndexPath ;
- 移動結束的時候會調用此datasource,想要拖拽完成之後數據正確必須實現此方法,使用新的路徑更新數據源,如果不實現此方法,剛剛移動cell中的數據不會重新排列。
- (void)collectionView:(UICollectionView *)collectionView moveItemAtIndexPath:(NSIndexPath *)sourceIndexPath
toIndexPath:(NSIndexPath *)destinationIndexPath
參數 | 說明 |
---|---|
collectionView | 正在響應這個方法的collectionView |
sourceIndexPath | 原始移動的indexpath |
destinationIndexPath | 移動到目標位置的indexpath |
- 剛剛我們已經給collectionView 添加了長按的手勢,目的就是長按的時候,可以拖動cell來進行移動,那怎麼開始移動呢?我們來實現長按事件看看。
// 在開始移動時會調用此代理方法,
- (BOOL)collectionView:(UICollectionView *)collectionView canMoveItemAtIndexPath:(NSIndexPath *)indexPath {
//根據indexpath判斷單元格是否可以移動,如果都可以移動,直接就返回YES ,不能移動的返回NO
return YES;
}
// 移動過程中調用,比如"關注"和"推薦"想要始終在前2個位置
// proposedIndexPath移動的目標IndexPath, 返回nil表示不可以移動到這裏
- (NSIndexPath *)collectionView:(UICollectionView *)collectionView targetIndexPathForMoveFromItemAtIndexPath:(NSIndexPath *)originalIndexPath toProposedIndexPath:(NSIndexPath *)proposedIndexPath {
if (proposedIndexPath.section == 0 && (proposedIndexPath.item == 0|| proposedIndexPath.item==1 )) {
return nil;
}
return proposedIndexPath;
}
// 在移動結束的時候調用此代理方法,修改數據源
- (void)collectionView:(UICollectionView *)collectionView moveItemAtIndexPath:(NSIndexPath *)sourceIndexPath toIndexPath:(NSIndexPath*)destinationIndexPath {
/**
*sourceIndexPath 原始數據 indexpath
* destinationIndexPath 移動到目標數據的 indexPath
*/
[_data removeObjectAtIndex:sourceIndexPath.row];
UIImage *img = _data[sourceIndexPath.row]
[_data insertObject:img atIndex:destinationIndexPath.row];
}
ios9以下,需要自己寫collectionView拖拽的方法
-
如果你的手機支持iOS9或更高版本,使用蘋果爲我們提供的方法實現拖拽功能是沒有問題的。相信還有很多都在支持ios8,所以下面的方法也許很適用。
-
在**- (UICollectionViewCell *)collectionView:(UICollectionView )collectionView cellForItemAtIndexPath:(NSIndexPath )indexPath 方法中,爲每個cell添加長按手勢,這樣就可以在你長按cell的時候響應事件。
-
下面我們看看長按事件中應該實現怎樣的方法。
-
- (void)longPressAction:(UILongPressGestureRecognizer *)longPress {
//獲取當前cell所對應的indexpath
MoveCollectionViewCell *cell = (MoveCollectionViewCell *)longPress.view;
NSIndexPath *cellIndexpath = [_collectionView indexPathForCell:cell];
//將此cell 移動到視圖的前面
[_collectionView bringSubviewToFront:cell];
_isChange = NO;
switch (longPress.state) {
case UIGestureRecognizerStateBegan: {
//使用數組將collectionView每個cell的 UICollectionViewLayoutAttributes 存儲起來。
[self.cellAttributesArray removeAllObjects];
for (int i = 0; i < self.data.count; i++) {
[self.cellAttributesArray addObject:[_collectionView layoutAttributesForItemAtIndexPath:[NSIndexPath indexPathForRow:i inSection:0]]];
}
}
break;
case UIGestureRecognizerStateChanged: {
//在移動過程中,使cell的中心與移動的位置相同。
cell.center = [longPress locationInView:_collectionView];
for (UICollectionViewLayoutAttributes *attributes in self.cellAttributesArray) {
//判斷移動cell的indexpath,是否和目的位置相同,如果相同isChange爲YES,然後將數據源交換
if (CGRectContainsPoint(attributes.frame, cell.center) && cellIndexpath != attributes.indexPath) {
_isChange = YES;
NSString *imgStr = self.data[cellIndexpath.row];
[self.data removeObjectAtIndex:cellIndexpath.row];
[self.data insertObject:imgStr atIndex:attributes.indexPath.row];
[self.collectionView moveItemAtIndexPath:cellIndexpath toIndexPath:attributes.indexPath];
}
}
}
break;
case UIGestureRecognizerStateEnded: {
//如果沒有改變,直接返回原始位置
if (!_isChange) {
cell.center = [_collectionView layoutAttributesForItemAtIndexPath:cellIndexpath].center;
}
}
break;
default:
break;
}
}
Git地址:https://github.com/Shin1122/YZChannelTag
ios9以下Git地址 : https://github.com/oneMoreTime1357/CollectionViewTips