Farlanki.

YALContextMenu

字数统计: 830阅读时长: 4 min
2016/06/06 Share

YALContextMenu

作为菜单使用的YALContextMenu其实是一个UITableView.

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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
- (void)showInView:(UIView *)superview withEdgeInsets:(UIEdgeInsets)edgeInsets animated:(BOOL)animated {

if (self.animatingState!=Stable) {
return;
}

for (UITableViewCell<YALContextMenuCell> *aCell in [self visibleCells]) {
aCell.contentView.hidden = YES;
}

self.dismissalIndexpath = nil;

[superview addSubViewiew:self withSidesConstrainsInsets:edgeInsets];

if (animated) {
self.animatingState = Showing;
self.alpha = 0;

[self setUserInteractionEnabled:NO];

[UIView animateWithDuration:self.animationDuration animations:^{
self.alpha = 1;
} completion:^(BOOL finished) {
[self show:YES visibleCellsAnimated:YES];
[self setUserInteractionEnabled:YES];
}];

} else {
[self show:YES visibleCellsAnimated:NO];
}
}

- (void)addSubViewiew:(UIView *)view withSidesConstrainsInsets:(UIEdgeInsets)insets {
NSParameterAssert(view);

[view setTranslatesAutoresizingMaskIntoConstraints:NO];

[self addSubview:view];

[self addConstraint:[NSLayoutConstraint constraintWithItem:view
attribute:NSLayoutAttributeTop
relatedBy:NSLayoutRelationEqual
toItem:self
attribute:NSLayoutAttributeTop
multiplier:1.0
constant:insets.top]];

[self addConstraint:[NSLayoutConstraint constraintWithItem:view
attribute:NSLayoutAttributeLeading
relatedBy:NSLayoutRelationEqual
toItem:self
attribute:NSLayoutAttributeLeading
multiplier:1.0
constant:insets.left]];

[self addConstraint:[NSLayoutConstraint constraintWithItem:view
attribute:NSLayoutAttributeBottom
relatedBy:NSLayoutRelationEqual
toItem:self
attribute:NSLayoutAttributeBottom
multiplier:1.0
constant:insets.bottom]];

[self addConstraint:[NSLayoutConstraint constraintWithItem:view
attribute:NSLayoutAttributeTrailing
relatedBy:NSLayoutRelationEqual
toItem:self
attribute:NSLayoutAttributeTrailing
multiplier:1.0
constant:insets.right]];
}

在UIView的一个extension中添加了加入subview并且设置constraint的方法.
先显示UITableView的背景,再通过[self show:YES visibleCellsAnimated:YES];显示各个cell.

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
- (void)show:(BOOL)show visibleCellsAnimated:(BOOL)animated {
NSArray *visibleCellsIndexPahts = [self indexPathsForVisibleRows];
NSInteger firstVisibleRowIndex = [(NSIndexPath *)visibleCellsIndexPahts.firstObject row];
NSInteger lastVisibleRowIndex = [(NSIndexPath *)visibleCellsIndexPahts.lastObject row];

if (visibleCellsIndexPahts.count == 0 || self.animatingIndex > lastVisibleRowIndex) {
self.animatingIndex = 0;
[self setUserInteractionEnabled:YES];
[self reloadData];
self.animatingState = Stable;
return;
}

NSIndexPath *animatingIndexPath = [NSIndexPath indexPathForRow:self.animatingIndex inSection:0];
UITableViewCell<YALContextMenuCell> *visibleCell = (UITableViewCell<YALContextMenuCell> *)[self cellForRowAtIndexPath:animatingIndexPath];
if (visibleCell) {
[self prepareCellForShowAnimation:visibleCell];
[visibleCell contentView].hidden = NO;
Direction direction;
if (self.animatingIndex == firstVisibleRowIndex) {
direction = self.menuItemsSide == Right ? right : left;
} else {
direction = self.menuItemsAppearanceDirection == FromBottomToTop ? bottom : top;
}

[self show:show cell:visibleCell animated:animated direction:direction clockwise:NO completion:^(BOOL completed) {
// ignore flag 'completed', cause if user scroll out animating cell, it will be false and menu will be empty(
self.animatingIndex++;
[self show:show visibleCellsAnimated:animated];
//loop
}];

} else {
// user scroolled animatingCell, so animate from first visible again
self.animatingIndex = firstVisibleRowIndex;
[self show:show visibleCellsAnimated:animated];
}
}

在动画的completion block中再次调用这个方法,直到所有cell被显示.
注意这里调用了[self prepareCellForShowAnimation:visibleCell]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
- (void)prepareCellForShowAnimation:(UITableViewCell<YALContextMenuCell> *)cell {

[self resetAnimatedIconForCell:cell];

Direction direction;
BOOL clockwise;
if ([self indexPathForCell:cell].row == 0) {
direction = self.menuItemsSide == Right ? right : left;
clockwise = self.menuItemsSide == Right ? NO : YES;
} else {
direction = self.menuItemsAppearanceDirection == FromBottomToTop ? bottom : top;
clockwise = self.menuItemsAppearanceDirection == FromBottomToTop ? YES : NO;
}

[self show:NO cell:cell animated:NO direction:direction clockwise:clockwise completion:nil];
}

这个方法是通过设置clockwise,先将各个cell翻转到动画翻转的反方向,之后进行一次动画翻转的时候就能让cell回复正常.除此之外还重设了icon.layer的锚点,transform.

准备工作完成后,调用

1
2
3
4
5
6
[self show:show cell:visibleCell animated:animated direction:direction clockwise:NO completion:^(BOOL completed) {
// ignore flag 'completed', cause if user scroll out animating cell, it will be false and menu will be empty(
self.animatingIndex++;
[self show:show visibleCellsAnimated:animated];
//loop
}];

具体如下:

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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
- (void)show:(BOOL)show
cell:(UITableViewCell<YALContextMenuCell> *)cell
animated:(BOOL)animated
direction:(Direction)direction
clockwise:(BOOL)clockwise
completion:(completionBlock)completed {

UIView *icon = [cell animatedIcon];
UIView *content = [cell animatedContent];


CATransform3D rotationAndPerspectiveTransform = CATransform3DIdentity;
rotationAndPerspectiveTransform.m34 = 1.0/200; //Add the perspective

CGFloat rotation = 90;
if (clockwise) {
rotation = -rotation;
}

if (show) {
rotation = 0;
}

switch (direction) {
case left: {
[self setAnchorPoint:CGPointMake(0, 0.5) forView:icon];
rotationAndPerspectiveTransform = CATransform3DRotate(rotationAndPerspectiveTransform, DEGREES_TO_RADIANS(rotation), 0.0f, 1.0f, 0.0f);
break;
}
case right: {
[self setAnchorPoint:CGPointMake(1, 0.5) forView:icon];
rotationAndPerspectiveTransform = CATransform3DRotate(rotationAndPerspectiveTransform, DEGREES_TO_RADIANS(rotation), 0.0f, 1.0f, 0.0f);
break;
}
case top: {
[self setAnchorPoint:CGPointMake(0.5, 0.0) forView:icon];
rotationAndPerspectiveTransform = CATransform3DRotate(rotationAndPerspectiveTransform, DEGREES_TO_RADIANS(rotation), 1.0f, 0.0f, 0.0f);
break;
}
case bottom: {
[self setAnchorPoint:CGPointMake(0.5, 1) forView:icon];
rotationAndPerspectiveTransform = CATransform3DRotate(rotationAndPerspectiveTransform, DEGREES_TO_RADIANS(rotation), 1.0f, 0.0f, 0.0f);
break;
}

default:
break;
}

if (animated) {
[UIView animateWithDuration:self.animationDuration animations:^{
icon.layer.transform = rotationAndPerspectiveTransform;
content.alpha = show;
} completion:^(BOOL finished) {
if (completed) {
completed(finished);
}
}];

} else {
icon.layer.transform = rotationAndPerspectiveTransform;
content.alpha = show;
if (completed) {
completed(YES);
}
}
}

使用了CATransform3DIdentity对icon.layer进行转换.

CATALOG