【Xcode】なるべくコードを書かずにAutoLayoutを使ってアニメーションする
AutoLayoutを使ってアニメーションする方法を調べたところ、VisualFormatを使ってコード上でAutoLayoutの制約を書く方法が多く見つかりました。
しかし、VisualFormatで制約を書くのはなかなか複雑ですので、そこはIBで済ませてなるべくコードを書かないでやってみました。
今回作るもの
Moveボタンを押すと緑色の四角が上下に動くアニメーションを作ります。
作成手順
画面を作る
storyboardでView(緑色の四角)やButtonを配置していきます。
まず、View(緑色の四角)に下記の制約を追加します。
- 幅・高さ 200
- 垂直方向の制限:superviewから40の位置
- 水平方向の制限:superviewに対し中央
「垂直方向の制限:superviewから40の位置」はpriorityがデフォルトの1000になっています。
次に、View(緑色の四角)に「垂直方向の制限:superviewから200の位置」を追加します。追加後、この制約のpriorityを900(先ほどの制約より小さい値)に設定します。これによりAutoLayoutの警告が発生しなくなります。
Buttonについては割愛します。適当にお願いします。
変数、メソッドと結びつける
View(緑色の四角)やButtonを変数、メソッドと結びつけていきます。
moveViewはView(緑色の四角)自身と、upperConstraintは「垂直方向の制限:superviewから40の位置」と、underConstraintは「垂直方向の制限:superviewから200の位置」と結びつけてください。
@property (weak, nonatomic) IBOutlet UIView *moveView; // 動かすビュー @property (strong, nonatomic) IBOutlet NSLayoutConstraint *upperConstraint; // 上位置 @property (strong, nonatomic) IBOutlet NSLayoutConstraint *underConstraint; // 下位置
また、Buttonをタップしたときのメソッドを設定しておきます。
- (IBAction)onMoveButton:(id)sender { }
コードを書く
viewDidLoadで初期化をしておきます。
ビューを上下どちらに動かすかを判定するtoBottomプロパティを追加し、最初は下に動かしたいのでYESにしておきます。
また、コード上でAutoLayoutを設定するために、動かすビューのtranslatesAutoresizingMaskIntoConstraintsプロパティをNOにします。
@property BOOL toBottom; // 移動方向 (中略) - (void)viewDidLoad { [super viewDidLoad]; self.toBottom = YES; self.moveView.translatesAutoresizingMaskIntoConstraints = NO; }
次に、Buttonを押したときに呼ばれるメソッドを下記のように実装します。
- (IBAction)onMoveButton:(id)sender { if (self.toBottom) { // 上から下へ動かす self.upperConstraint.active = NO; self.underConstraint.active = YES; [UIView animateWithDuration:0.3f delay:0.f options:UIViewAnimationOptionCurveEaseInOut animations:^{ [self.view layoutIfNeeded]; } completion:nil]; } else { // 下から上へ動かす self.upperConstraint.active = YES; self.underConstraint.active = NO; [UIView animateWithDuration:0.3f delay:0.f options:UIViewAnimationOptionCurveEaseInOut animations:^{ [self.view layoutIfNeeded]; } completion:nil]; } self.toBottom = !self.toBottom; }
やっていることは、Buttonを押すたびにアニメーションをつけて垂直方向の制約を切り替えているだけです。
ポイントは
- [UIView animateWithDuration:] のanimationsブロック内で [self.view layoutIfNeeded]; を呼ぶこと(呼ばないとアニメーションしません)
- NSLayoutConstraintのプロパティは strong にしておくこと(通常IBOutletはweakにしますが、strongでないと active = NO を呼んだ後解放されてしまいます)
です。