【iOS】viewControllerの切り替え時に滑らかにアニメーションしたかった

おはこんばんちわ。
ドーモ。窓際エンジニア=デス

## 前振り
島中エンジニア「画面が滑らかにアニメーションしながら切り替わるのいいよね」
ぼく「いいですね」
島エ「この部分もアニメーションで切り替わるようにしようよ」
ぼく「でもやったことないです」
島エ「真似すればできるよ」
ぼく「できるめう！」

※このやりとりはフィクションです

## ミッション
* viewControllerの切り替え時に、viewもアニメーションしながら切り替わる

## 動作例
![output.gif](https://qiita-image-store.s3.amazonaws.com/0/75536/5f4bc707-be24-7325-ee15-d08a1111342d.gif)

## 解決策
切り替え前のviewControllerを親(Parent)、切り替え後のviewControllerを子(Child)とすると、
1. 親のviewの `frame` の情報を子のコントローラに渡す
2. 子のコントローラでアニメーションする

### 解決例のサンプル
buttonやviewの配置はxibで作ってます。
適当に配置しましょう。
環境はXcode6系のiOS8.3です。

#### ParentViewController.h
```objc
#import <UIKit/UIKit.h>

@interface ParentViewController : UIViewController
@property (weak, nonatomic) IBOutlet UIView *parentView;
@property (weak, nonatomic) IBOutlet UIButton *button;

@end
```

#### ParentViewController.m
```objc
#import "ParentViewController.h"
#import "ChildViewController.h"

@interface ParentViewController ()

@end

@implementation ParentViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    self.navigationItem.title = @"Parent";
    
    [self.button addTarget:self action:@selector(touchUpInsideButton:) forControlEvents:UIControlEventTouchUpInside];
}

-(void)touchUpInsideButton:(id)sender
{
    ChildViewController *vc = [ChildViewController new];
    vc.parentViewFrame = self.parentView.frame;
    UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:vc];
    nav.modalPresentationStyle = UIModalPresentationOverCurrentContext;
    [self presentViewController:nav animated:NO completion:nil];
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
}

@end
```

#### ChildViewController.h
```objc
#import <UIKit/UIKit.h>

@interface ChildViewController : UIViewController
@property (weak, nonatomic) IBOutlet UIView *childView;
@property (weak, nonatomic) IBOutlet UIButton *button;

@property CGRect parentViewFrame;

@end
```

#### ChildViewController.m
```objc
#import "ChildViewController.h"

@interface ChildViewController ()
@property CGRect childViewFrame;

@end

@implementation ChildViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    self.navigationItem.title = @"Child";
    
    // viewを透過できるようにする
    self.view.opaque = YES;
    
    // viewのサイズを保存しておく
    self.childViewFrame = self.childView.frame;
    
    [self.button addTarget:self action:@selector(touchUpInsideButton:) forControlEvents:UIControlEventTouchUpInside];
    
    // 開始時のアニメーションを実行
    [self viewDidLoadAnimationView];
}

-(void)viewDidLoadAnimationView
{
    // 開始時は親のviewの大きさからアニメーションをスタートさせる
    self.childView.frame = self.parentViewFrame;
    
    [UIView animateWithDuration:0.4f delay:0.0f options:UIViewAnimationOptionCurveEaseInOut animations:^{
        // 親のviewの大きさへ
        self.childView.frame = self.childViewFrame;
    } completion:nil];
}

-(void)touchUpInsideButton:(id)sender
{
    // 終了時のアニメーション
    [UIView animateWithDuration:0.4f delay:0.0f options:UIViewAnimationOptionCurveEaseInOut  animations:^{
        // 親のviewの大きさへ
        self.childView.frame = self.parentViewFrame;
    } completion:^(BOOL finished) {
        [self dismissViewControllerAnimated:NO completion:nil];
    }];
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
}

@end
```

## 応用例
少し頑張るとイケてるUIに。
親のframeからcenterを一致させて拡大縮小させると綺麗です(Blue)。
tableViewでもcollectionViewを使っても同じことはできるので、あとは想像次第！

![output.gif](https://qiita-image-store.s3.amazonaws.com/0/75536/cbab7d47-e57c-f92f-376d-0d9acab7d0cf.gif)


## まとめ
単一のviewを使ってviewControllerの切り替え時に滑らかにアニメーションするようにした。
応用してtableViewCellとかcollectionViewCellに適用してあげるとかっこいい

## おまけ
応用例のソースも置いておきます。
viewの配置と色はxibで自由に作ってみてください。

#### ParentViewController.h
```objc
@interface ParentViewController : UIViewController
@property (weak, nonatomic) IBOutlet UIView *parentViewRed;
@property (weak, nonatomic) IBOutlet UIView *parentViewBlue;
@property (weak, nonatomic) IBOutlet UIView *parentViewYellow;

@end
```

#### ParentViewController.m
```objc
#import "ParentViewController.h"
#import "ChildViewController.h"

@interface ParentViewController ()

@end

@implementation ParentViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    self.navigationItem.title = @"Parent";
    
    UITapGestureRecognizer *redViewGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(touchView:)];
    UITapGestureRecognizer *blueViewGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(touchView:)];
    UITapGestureRecognizer *yellowViewGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(touchView:)];
    [self.parentViewRed addGestureRecognizer:redViewGesture];
    [self.parentViewBlue addGestureRecognizer:blueViewGesture];
    [self.parentViewYellow addGestureRecognizer:yellowViewGesture];
}

-(void)touchView:(UITapGestureRecognizer *)gesture
{
    ChildViewController *vc = [ChildViewController new];
    vc.parentViewFrame = gesture.view.frame;
    vc.parentViewColor = gesture.view.backgroundColor;
    UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:vc];
    nav.modalPresentationStyle = UIModalPresentationOverCurrentContext;
    [self presentViewController:nav animated:NO completion:nil];
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
}

@end
```

#### ChildViewController.h
```objc
#import <UIKit/UIKit.h>

@interface ChildViewController : UIViewController
@property (weak, nonatomic) IBOutlet UIView *childView;

@property CGRect parentViewFrame;
@property UIColor *parentViewColor;

@end
```

#### ChildViewController.m
```objc
#import "ChildViewController.h"

@interface ChildViewController ()
@property CGRect childViewFrame;

@end

@implementation ChildViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    self.navigationItem.title = @"Child";
    
    self.view.opaque = YES;
    
    self.childViewFrame = self.childView.frame;
    self.childView.backgroundColor = self.parentViewColor;
    
    UITapGestureRecognizer *viewGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(touchView:)];
    [self.childView addGestureRecognizer:viewGesture];
    
    [self viewDidLoadAnimationView];
}

-(void)viewDidLoadAnimationView
{
    self.childView.frame = self.parentViewFrame;
    
    [UIView animateWithDuration:0.4f delay:0.0f options:UIViewAnimationOptionCurveEaseInOut animations:^{
        self.childView.frame = self.childViewFrame;
    } completion:nil];
}

-(void)touchView:(UITapGestureRecognizer *)gesture
{
    [UIView animateWithDuration:0.4f delay:0.0f options:UIViewAnimationOptionCurveEaseInOut  animations:^{
        self.childView.frame = self.parentViewFrame;
        self.view.backgroundColor = [UIColor colorWithWhite:1 alpha:0.0];
    } completion:^(BOOL finished) {
        [self dismissViewControllerAnimated:NO completion:^{
            UINavigationController *nav = self.navigationController;
            UIViewController *parentViewController = [nav.viewControllers objectAtIndex:(nav.viewControllers.count-1)];
            [parentViewController viewWillAppear:NO];
        }];
    }];
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
}

@end
```