仿寫《今日頭條》的tag選擇頁面

在《今日頭條》中,該頁面是用來選擇自己感興趣的頻道標籤從而改變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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章