仿網易新聞欄目選擇頁面的基本效果,今天抽了點時間教大家如何實現UICollectionView拖動的效果!
其實實現起來並不復雜,這里只是基本的功能,沒有實現細節上的修改,連UI都是丑丑的樣子,隨手畫的。
相信大家都使用過網易新聞客戶端,里面的效果確定被不少人模仿,很多同類型的app都采用了人家的UI樣式,那么今天就教大家如何去實現。
效果圖
這是簡略的效果圖,樣子是有點丑,將就看看吧:
實現原理
給UICollectionView添加一個手勢或者其他方式,在長按時手動觸發拖動開始,在移動過程中又要不斷手動地更新位置,而在完成時手動觸發完成操作,在取消時也要手動觸發取消移動操作。
而要實現移動,必須設置是否可移動。比如我們的demo中第一個分區是要求移動的,而第二個分區是不允許移動的,因此我們需要通過代理來設置是否可移動。
在移動完成時,會有代理回調,此時我們要做的就是交換數據源來更新。
移動API介紹
首先我們必須要明確,這幾個API是在iOS9之后才能使用的。這幾個API來配合起來使用,才能最終達到我們期望的效果。
1
2
3
4
5
6
7
8
9
10
11
12
13
|
// 開始
- (BOOL)beginInteractiveMovementForItemAtIndexPath:(NSIndexPath *)indexPath NS_AVAILABLE_IOS(9_0);
// 更新位置
- (void)updateInteractiveMovementTargetPosition:(CGPoint)targetPosition NS_AVAILABLE_IOS(9_0);
// 完成
- (void)endInteractiveMovement NS_AVAILABLE_IOS(9_0);
// 取消
- (void)cancelInteractiveMovement NS_AVAILABLE_IOS(9_0);
|
這四個API組合起來才是完整的動作。
添加長按手勢
我們需要將手勢放到CollectionView上,因為我們要的是操作全范圍的!
1
2
3
4
5
6
|
self.collectionView.pagingEnabled = NO;
UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(onLongPressed:)];
[self.collectionView addGestureRecognizer:longPress];
|
處理手勢
這個基本是固定的,大家可以copy一下我的代碼過去用就可以了:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
|
- (void)onLongPressed:(UILongPressGestureRecognizer *)sender {
CGPoint point = [sender locationInView:sender.view];
NSIndexPath *indexPath = [self.collectionView indexPathForItemAtPoint:point];
// 只允許第一區可移動
if (indexPath.section != 0) {
return;
}
switch (sender.state) {
case UIGestureRecognizerStateBegan: {
if (indexPath) {
[self.collectionView beginInteractiveMovementForItemAtIndexPath:indexPath];
}
break;
}
case UIGestureRecognizerStateChanged: {
[self.collectionView updateInteractiveMovementTargetPosition:point];
break;
}
case UIGestureRecognizerStateEnded: {
[self.collectionView endInteractiveMovement];
break;
}
default: {
[self.collectionView cancelInteractiveMovement];
break;
}
}
}
|
是否允許移動
這里是只有在編輯狀態下且第一分區才能拖動重排,所以通過添加條件來控制是否可移動:
1
2
3
4
5
6
7
8
9
|
- (BOOL)collectionView:(UICollectionView *)collectionView canMoveItemAtIndexPath:(NSIndexPath *)indexPath {
if (self.isEditing && indexPath.section == 0) {
return YES;
}
return NO;
}
|
完成移動更新源
我們只有第一分區可以操作,而這里只有一個數組,所以直接交換一下數據源就OK了:
1
2
3
4
5
6
7
|
- (void)collectionView:(UICollectionView *)collectionView moveItemAtIndexPath:(NSIndexPath *)sourceIndexPath toIndexPath:(NSIndexPath *)destinationIndexPath {
if (sourceIndexPath.section == 0 && destinationIndexPath.section == 0) {
[self.firstList exchangeObjectAtIndex:sourceIndexPath.item withObjectAtIndex:destinationIndexPath.item];
}
}
|
添加刪除移動效果
在點擊第一分區時或者第二分區的cell時,會自動移動到第二分區或者第一分區。我們希望有移動的動畫效果的話,就需要通過collectionview提供的API來完成,而編輯狀態下有刪除按鈕,而到二分區時就不能顯示,而在一分區在編輯狀態下又可以顯示:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
|
#pragma mark - UICollectionViewDelegate
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath {
if (indexPath.section == 0) {
if (self.isEditing) {
TestModel *model = [self.secondList hyb_objectAtIndex:indexPath.item];
[self.firstList addObject:model];
[self.secondList removeObject:model];
NSInteger index = self.firstList.count - 1;
if (self.firstList.count == 0) {
index = 0;
}
TestColumnCell *cell = (TestColumnCell *)[collectionView cellForItemAtIndexPath:indexPath];
cell.isEditing = NO;
[collectionView moveItemAtIndexPath:indexPath toIndexPath:[NSIndexPath indexPathForItem:index inSection:1]];
[self saveColumns];
} else {
// 選擇某一個
}
} else {
TestModel *model = [self.firstList hyb_objectAtIndex:indexPath.item];
[self.secondList addObject:model];
[self.firstList removeObject:model];
NSInteger index = self.secondList.count - 1;
if (self.secondList.count == 0) {
index = 0;
}
if (self.isEditing) {
TestColumnCell *cell = (TestColumnCell *)[collectionView cellForItemAtIndexPath:indexPath];
cell.isEditing = YES;
}
[collectionView moveItemAtIndexPath:indexPath toIndexPath:[NSIndexPath indexPathForItem:index inSection:0]];
[self saveColumns];
}
[collectionView reloadData];
}
|