之前很喜欢收藏dribbble里面的一些动效gif,里面不仅是动效还是配色等等都非常的美,作为一个视觉动物,真的想把他们一个一个都实现出来,作为自己的一个作品,那真的是一件赏心悦目的事情,看着G20也快结束了,赶紧趁还闲着,先实现一个,我先挑选了一个比较简单的动效—–抽屉式效果列表

我们先来看下效果:

原来的动效地址找不到了…有知道的请留言给我…

代码地址:https://github.com/Yuzeyang/GCDrawerTableView

老样子,我们来分析下步骤

0x00 cell的处理

我们将cell详情界面分开来处理

我们可以看到,当我们选中其中一个cell的时候,该cell会移动到列表上方的某一个位置,其他cell则不显示,点击x的时候,cell返回为原位,其他cell又重新显示

选中处理:首先是先将其他cell隐藏,我们取出tableview的可见cell,然后将除了选中的cell之外的透明度都设置为0

1
2
3
4
5
6
7
GCTableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
[self.tableView bringSubviewToFront:cell];
for (UIView *subcell in tableView.visibleCells) {
if (subcell != cell) {
subcell.alpha = 0;
}
}

然后改变选中cell的原点值,并且给cell增加阴影,这里我偷懒没有做按钮的动画,只是用文字表示了按钮的状态

1
2
3
4
5
6
7
8
9
10
11
12
13
[UIView animateWithDuration:0.3 animations:^{
CGRect rect = self.frame;
self.originCellFrame = rect;
CGPoint origin = CGPointMake(0, contentOffsetY + 30);
rect.origin = origin;
self.frame = rect;

// 详情页处理

[self addShadowWithView:self];
[self addShadowWithView:self.detailView];
[self.detailButton setTitle:@"×" forState:UIControlStateNormal];
}];

由于cell的动画处理是放在自定义cell里面做的,所以在点击关闭的时候,需要在动画结束之后回调给视图控制器,所以这里就用了block来回调关闭的状态

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
- (void)deselectCell {
[UIView animateWithDuration:0.3 animations:^{
self.frame = self.originCellFrame;

self.layer.shadowColor = [UIColor clearColor].CGColor;
self.layer.shadowRadius = 0;
self.layer.shadowOpacity = 0.0;

// 详情页处理
[self.detailButton setTitle:@"..." forState:UIControlStateNormal];
} completion:^(BOOL finished) {
[self.helperHideView removeFromSuperview];
[self.detailView removeFromSuperview];
if (_deselectBlock) {
_deselectBlock();
}
}];
}

然后在试图控制器里面,将其他的cell透明度改成1

1
2
3
4
5
6
7
8
9
[cell addDeselectBlock:^() {
for (UIView *subcell in tableView.visibleCells) {
if (subcell != cell) {
subcell.alpha = 1;
}
}
tableView.allowsSelection = YES;
tableView.scrollEnabled = YES;
}];

0x01 详情页

可以看到详情页是类似于从上掉落的感觉,可能会联想到电商的那些筛选栏,但是筛选栏点击的时候,是上部分开始出现一直到下面,展示的顺序是反过来的,后面想了很久,只能想到利用视觉错误的效果,将详情页添加到cell的下一层,然后cell动画时候,详情页也做相应的动画,但是由于详情页的sizecell肯定是要大的,所以如果加到cell下一层时,是遮挡不住的,所以就需要一个遮挡的view来遮住详情页

首先我们需要将cell移到最前

1
[self.tableView bringSubviewToFront:cell];

然后在cell的下一层加上遮挡视图详情页,并根据cell的origin做frame的变化

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
UIView *superview = self.superview;

CGFloat height = CGRectGetMinY(self.frame) - contentOffsetY + 30;
[self.helperHideView setFrame:CGRectMake(0, contentOffsetY, CGRectGetWidth(self.frame), height)];
[superview insertSubview:self.helperHideView belowSubview:self];

[self.detailView setFrame:CGRectMake(0, CGRectGetMaxY(self.frame) - (GCDeviceHeight - 100 - 30*2),
CGRectGetWidth(self.frame), GCDeviceHeight - 100 - 30*2)];
[superview insertSubview:self.detailView belowSubview:self.helperHideView];

[UIView animateWithDuration:0.3 animations:^{
// cell处理

CGRect rect2 = self.helperHideView.frame;
self.originHelperViewFrame = rect2;
CGPoint origin2 = CGPointMake(0, contentOffsetY + 30 - height);
rect2.origin = origin2;
self.helperHideView.frame = rect2;

CGRect rect1 = self.detailView.frame;
self.originDetailViewFrame = rect1;
CGPoint origin1 = CGPointMake(0, 100 + 30 + contentOffsetY);
rect1.origin = origin1;
self.detailView.frame = rect1;

[self addShadowWithView:self];
[self addShadowWithView:self.detailView];
[self.detailButton setTitle:@"×" forState:UIControlStateNormal];
}];

然后在关闭的时候,移除掉

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
- (void)deselectCell {
[UIView animateWithDuration:0.3 animations:^{
// cell处理

self.detailView.layer.shadowColor = [UIColor clearColor].CGColor;
self.detailView.layer.shadowRadius = 0;
self.detailView.layer.shadowOpacity = 0.0;

self.detailView.frame = self.originDetailViewFrame;
self.helperHideView.frame = self.originHelperViewFrame;

[self.detailButton setTitle:@"..." forState:UIControlStateNormal];
} completion:^(BOOL finished) {
[self.helperHideView removeFromSuperview];
[self.detailView removeFromSuperview];
if (_deselectBlock) {
_deselectBlock();
}
}];
}

这样就实现了抽屉式的效果,但是总感觉还有更好的办法实现,如果有思路的,欢迎交流~