diff --git a/MGTileMenu/MGTileMenuController.m b/MGTileMenu/MGTileMenuController.m index bd2bbc5..0cfd97a 100644 --- a/MGTileMenu/MGTileMenuController.m +++ b/MGTileMenu/MGTileMenuController.m @@ -22,7 +22,7 @@ #define MG_TILES_PER_PAGE 5 // not including paging tile (or Close button) #define MG_DISABLED_TILE_OPACITY 0.65 // from 0.0 (fully transparent/hidden) to 1.0 (fully opaque/visible) // Timing. -#define MG_ANIMATION_DURATION 0.15 // seconds +#define MG_ANIMATION_DURATION 0.2 // seconds #define MG_ACTIVATION_DISMISS_DELAY 0.25 // seconds; delay between activating a tile and auto-dismissing the menu (if appropriate) @@ -384,6 +384,22 @@ - (UIBezierPath *)_bezelPath } +- (CGPoint)_centreForExtendedTileWithCentre:(CGPoint)tileCentre +{ + // Used to calculate the extreme point of the tile centre when animating in and out. Extend the line going from our view centre to the given centre by a certain factor. + + static float const kTileExtensionFactor = 1.2; // 20 pct + + CGPoint menuCentre = MGCenterPoint([self frameForCenteredTile]); + CGPoint extendedCentre; + + extendedCentre.x = menuCentre.x + (tileCentre.x - menuCentre.x) * kTileExtensionFactor; + extendedCentre.y = menuCentre.y + (tileCentre.y - menuCentre.y) * kTileExtensionFactor; + + return extendedCentre; +} + + // Returns the appropriate frame (within bezel view) for a tile at the given zero-based index. // There are: // three tiles (0-2) on the top row @@ -871,6 +887,16 @@ - (void)animationDidStop:(CAAnimation *)animation finished:(BOOL)finished } [layer removeAnimationForKey:path]; } + + else if ([animation isKindOfClass:[CAKeyframeAnimation class]]) { + path = [(CAKeyframeAnimation *)animation keyPath]; + if (cleanUp) { + fromValue = [[(CAKeyframeAnimation *)animation values] objectAtIndex:0]; + } else { + [layer setValue:[[(CAKeyframeAnimation *)animation values] lastObject] forKeyPath:path]; + } + [layer removeAnimationForKey:path]; + } } if (cleanUp) { @@ -908,13 +934,13 @@ - (void)animateTilesForCurrentPage { // Determine which animation (to or from centre) to perform. CGPoint centrePoint = MGCenterPoint([self frameForCenteredTile]); - CGPoint tileCentre; - CABasicAnimation *tileAnimation; + CGPoint tileCentre, extendedCentre; + CAKeyframeAnimation *tileAnimation; UIButton *tile; CFTimeInterval baseTime = CACurrentMediaTime(); CFTimeInterval delay = (_appeared) ? 0 : MG_ANIMATION_DURATION; // allow for menu appearance animation - CFTimeInterval duration = (_tilesArranged) ? MG_ANIMATION_DURATION - 0.05 : MG_ANIMATION_DURATION; // go to centre quicker than going back out - CFTimeInterval offset = (_tilesArranged) ? 0.05 : 0.1; // go to centre quicker than going back out + CFTimeInterval duration = 2.0 * (_tilesArranged) ? MG_ANIMATION_DURATION - 0.05 : MG_ANIMATION_DURATION; // go to centre quicker than going back out + CFTimeInterval offset = (_tilesArranged) ? 0.025 : 0.05; // go to centre quicker than going back out // Use delegate methods to configure tiles, showing/hiding as required, if we're at the centre. @@ -1013,7 +1039,7 @@ - (void)animateTilesForCurrentPage break; } tile = [_tileButtons objectAtIndex:j]; - tileAnimation = [CABasicAnimation animationWithKeyPath:@"position"]; + tileAnimation = [CAKeyframeAnimation animationWithKeyPath:@"position"]; tileAnimation.beginTime = baseTime + delay + ((CGFloat)i * offset); [tileAnimation setValue:MG_ANIMATION_TILES forKey:@"name"]; [tileAnimation setRemovedOnCompletion:NO]; @@ -1021,18 +1047,17 @@ - (void)animateTilesForCurrentPage [tileAnimation setFillMode:kCAFillModeForwards]; [tileAnimation setTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut]]; tileCentre = MGCenterPoint([self frameForTileAtIndex:j]); + extendedCentre = [self _centreForExtendedTileWithCentre:tileCentre]; // the extreme centre point of the tile animation CALayer *presentationLayer = (CALayer *)[tile.layer presentationLayer]; CGPoint currentPosition = presentationLayer.position; if (!_tilesArranged) { // We're going from the centre out towards the final position. - tileAnimation.fromValue = [NSValue valueWithCGPoint:(_tileAnimationInterrupted) ? currentPosition : centrePoint]; - tileAnimation.toValue = [NSValue valueWithCGPoint:tileCentre]; + tileAnimation.values = [NSArray arrayWithObjects:[NSValue valueWithCGPoint:(_tileAnimationInterrupted) ? currentPosition : centrePoint], [NSValue valueWithCGPoint:extendedCentre], [NSValue valueWithCGPoint:tileCentre], nil]; } else { // We're going from the initial position in towards the centre. - tileAnimation.fromValue = [NSValue valueWithCGPoint:(_tileAnimationInterrupted) ? currentPosition : tileCentre]; - tileAnimation.toValue = [NSValue valueWithCGPoint:centrePoint]; + tileAnimation.values = [NSArray arrayWithObjects:[NSValue valueWithCGPoint:(_tileAnimationInterrupted) ? currentPosition : tileCentre], [NSValue valueWithCGPoint:extendedCentre], [NSValue valueWithCGPoint:centrePoint], nil]; } [tileAnimation setDelegate:self]; [tileAnimation setValue:tile.layer forKey:MG_ANIMATION_TILE_LAYER];