I am willing to create a segue animation similar to opening an app in iOS 7. Now I consider this approach: Non-segue implementation, but encountered an issue: if being implemented in custom segue, the code cannot resize viewcontroller's frame, which is principal part of the animation. As I can see, it applies viewcontroller's position, but cannot change it's size. Am I correct, that property viewcontroller.view.frame cannot be animated and has to remain constant and equal to size of the device's screen? I already implemented something similar with CGAffineTransformMakeScale
, but its a bit weird, so, I am looking for better solution. Below I paste the code I use for the segue.
#import "ZoomSegue.h"
#import "MiniMapViewController.h"
#import "InteractiveMapViewController.h"
@implementation ZoomSegue
- (void)perform {
InteractiveMapViewController *sourceViewController = self.sourceViewController;
MiniMapViewController *destinationViewController = self.destinationViewController;
destinationViewController.view.alpha = 0.0f;
destinationViewController.view.hidden = NO;
//Just to check, lets make view really small. Position it to the center we get from the segue.
destinationViewController.view.frame = CGRectMake(_originatingPoint.x, _originatingPoint.y, 60.0f, 60.0f);
[sourceViewController.view addSubview:destinationViewController.view];
float animationDuration = 1.5f;
[UIView animateWithDuration:animationDuration/2.0f animations:^{
destinationViewController.view.alpha = 1.0f;
}];
[UIView animateWithDuration:animationDuration animations:^{
//Just to check, lets make view very big. It does not work, although, the view goes to the upper left corner, so position works, but not the size
CGRect x = CGRectMake(0.0f, 0.0f, 9590.0f, 91366.0f);
destinationViewController.view.frame = x;
}];
}
@end
I don't know if you're correct, but a better way to do it anyway is to use a snapshot view. This code puts a small snapshot in the center of the screen then expands it to full size,
- (void)perform {
UIView *sourceView = ((UIViewController *)self.sourceViewController).view;
UIView *destinationView = [((UIViewController *)self.destinationViewController).view snapshotViewAfterScreenUpdates:YES];
destinationView.frame = CGRectMake(0, 0, 5, 5);
destinationView.center = CGPointMake(sourceView.center.x, sourceView.center.y);
[sourceView addSubview:destinationView];
[UIView animateWithDuration:1
animations:^{
destinationView.frame = sourceView.frame;
}
completion:^(BOOL finished) {
[[self sourceViewController] presentViewController:[self destinationViewController] animated:NO completion:nil];
}];
}
After Edit:
Here is a translation of the code in the link you provided that converts that animation into a segue,
-(void)perform {
CGFloat duration = .25;
UIViewController *source = self.sourceViewController;
UIView *bg = [source.view snapshotViewAfterScreenUpdates:YES];
bg.frame = UIScreen.mainScreen.bounds;
UIViewController *dest = self.destinationViewController;
UIView *icon = [dest.view snapshotViewAfterScreenUpdates:YES];
icon.frame = self.iconFrame;
icon.alpha = 0.0f;
[source.view addSubview:bg];
[source.view addSubview:icon];
[UIView animateWithDuration:duration animations:^{
icon.alpha = 1.0f;
}];
[UIView animateWithDuration:duration*2 animations:^{
icon.frame = UIScreen.mainScreen.bounds;
bg.frame = [self zoomedRect];
} completion:^(BOOL finished) {
[source presentViewController:dest animated:NO completion:nil];
[bg removeFromSuperview];
[icon removeFromSuperview];
}];
}
- (CGRect)zoomedRect {
float screenWidth = UIScreen.mainScreen.bounds.size.width;
float screenHeight = UIScreen.mainScreen.bounds.size.height;
float size = screenWidth / self.iconFrame.size.width;
float x = screenWidth/2 - (CGRectGetMidX(self.iconFrame) * size);
float y = screenHeight/2 - (CGRectGetMidY(self.iconFrame) * size);
return CGRectMake(x, y, screenWidth * size, screenHeight * size);
}
I have a CGRect property, iconFrame in the .h file of the segue. I performed the segue from a tap gesture recognizer that was attached to the icon that will be expanded. You need to pass the frame of that icon to the segue, which I do in the source view controller's prepareForSegue method,
-(void)prepareForSegue:(SpringboardSegue *)segue sender:(UITapGestureRecognizer *)sender {
segue.iconFrame = sender.view.frame;
}
I've translated the same code in SWIFT.
class CustomOpenSegue: UIStoryboardSegue {
var iconFrame = CGRect()
override func perform(){
var sourceViewController: UIViewController = self.sourceViewController as UIViewController
var bg:UIView = sourceViewController.view.snapshotViewAfterScreenUpdates(true)
bg.frame = UIScreen.mainScreen().bounds
var destinationViewController: UIViewController = self.destinationViewController as UIViewController
var icon:UIView = destinationViewController.view.snapshotViewAfterScreenUpdates(true)
icon.frame = self.iconFrame
icon.alpha = 0.0
sourceViewController.view.addSubview(icon)
UIView.animateWithDuration(0.25, delay: 0.0, options: UIViewAnimationOptions.CurveEaseInOut, animations: {
icon.alpha = 1.0
}, completion: nil )
UIView.animateWithDuration(0.5, delay: 0.0, options: UIViewAnimationOptions.CurveEaseInOut, animations: {
() -> Void in
icon.frame = UIScreen.mainScreen().bounds
bg.frame = self.zoomedRect
}) { (finished) -> Void in
sourceViewController.navigationController?.pushViewController(destinationViewController, animated: false)
icon.removeFromSuperview()
bg.removeFromSuperview()
}
}
var zoomedRect:CGRect{
var screenWidth = UIScreen.mainScreen().bounds.size.width
var screenHeight = UIScreen.mainScreen().bounds.size.height
var size = screenWidth / self.iconFrame.size.width
var x = screenWidth / 2 - CGRectGetMidX(self.iconFrame) * size
var y = screenHeight / 2 - CGRectGetMidY(self.iconFrame) * size
return CGRectMake(x, y, screenWidth * size, screenHeight * size)
}
}
And this is how you call it:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue is CustomOpenSegue{
let customOpenSegue = segue as CustomOpenSegue
customOpenSegue.iconFrame = self.mapButtonFrame!
}
}