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) { self .animatingIndex++; [self show:show visibleCellsAnimated:animated]; }]; } else { 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) { self .animatingIndex++; [self show:show visibleCellsAnimated:animated]; }];
具体如下:
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 ; 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.0 f, 1.0 f, 0.0 f); break ; } case right: { [self setAnchorPoint:CGPointMake (1 , 0.5 ) forView:icon]; rotationAndPerspectiveTransform = CATransform3DRotate (rotationAndPerspectiveTransform, DEGREES_TO_RADIANS(rotation), 0.0 f, 1.0 f, 0.0 f); break ; } case top: { [self setAnchorPoint:CGPointMake (0.5 , 0.0 ) forView:icon]; rotationAndPerspectiveTransform = CATransform3DRotate (rotationAndPerspectiveTransform, DEGREES_TO_RADIANS(rotation), 1.0 f, 0.0 f, 0.0 f); break ; } case bottom: { [self setAnchorPoint:CGPointMake (0.5 , 1 ) forView:icon]; rotationAndPerspectiveTransform = CATransform3DRotate (rotationAndPerspectiveTransform, DEGREES_TO_RADIANS(rotation), 1.0 f, 0.0 f, 0.0 f); 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进行转换.