24

これは問題であり、部分的な解決策でもあります。


*サンプルプロジェクトはこちら:

https://github.com/JosephLin/TransitionTest


問題 1:

を使用している場合、遷移アニメーションの開始時transitionFromViewController:...に で作成されたレイアウトが表示されません。つまり、アニメーション中にプレレイアウト ビューが表示され、そのコンテンツはアニメーション後にレイアウト後の位置にスナップされます。toViewControllerviewWillAppear:

問題 2:

ナビゲーション バーの背景をカスタマイズするUIBarButtonItemと、問題 1 と同様に、アニメーションの前にバー ボタンが間違ったサイズ/位置で表示され、アニメーションが終了すると正しいサイズ/位置にスナップします。


この問題を実証するために、いくつかのカスタム ビュー遷移を実行するベアボーン カスタム コンテナー コントローラーを作成しました。ビュー間でアニメーションをプッシュするのではなく、クロスディゾルブを行うのは、ほとんど UINavigationController のコピーです。

「プッシュ」メソッドは次のようになります。

- (void)pushController:(UIViewController *)toViewController
{
    UIViewController *fromViewController = [self.childViewControllers lastObject];

    [self addChildViewController:toViewController];
    toViewController.view.frame = self.view.bounds;

    NSLog(@"Before transitionFromViewController:");
    [self transitionFromViewController:fromViewController
                      toViewController:toViewController
                              duration:0.5
                               options:UIViewAnimationOptionTransitionCrossDissolve
                            animations:^{}
                            completion:^(BOOL finished) {
                                [toViewController didMoveToParentViewController:self];
                            }];
}

ここで、DetailViewController(プッシュしているビュー コントローラー) でコンテンツをレイアウトする必要がありますviewWillAppear:viewDidLoadその時点で正しいフレームがないため、それを行うことはできません。

デモンストレーションのために、ラベルを、、およびDetailViewControllerで異なる場所と色に設定します。viewDidLoadviewWillAppearviewDidAppear

- (void)viewDidLoad
{
    [super viewDidLoad];
    NSLog(@"%s", __PRETTY_FUNCTION__);
    CGRect rect = self.descriptionLabel.frame;
    rect.origin.y = 50;
    self.descriptionLabel.frame = rect;
    self.descriptionLabel.text = @"viewDidLoad";
    self.descriptionLabel.backgroundColor = [UIColor redColor];
}

- (void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];
    NSLog(@"%s", __PRETTY_FUNCTION__);
    CGRect rect = self.descriptionLabel.frame;
    rect.origin.y = 200;
    self.descriptionLabel.frame = rect;
    self.descriptionLabel.text = @"viewWillAppear";
    self.descriptionLabel.backgroundColor = [UIColor yellowColor];
}

- (void)viewDidAppear:(BOOL)animated
{
    [super viewDidAppear:animated];
    NSLog(@"%s", __PRETTY_FUNCTION__);
    CGRect rect = self.descriptionLabel.frame;
    rect.origin.y = 350;
    self.descriptionLabel.frame = rect;
    self.descriptionLabel.text = @"viewDidAppear";
    self.descriptionLabel.backgroundColor = [UIColor greenColor];
}

を押すと、アニメーションの開始時DetailViewControllerにラベルが表示さy =200れ (左の画像)、y = 350アニメーションが終了した後にジャンプします (右の画像)。

ここに画像の説明を入力 ここに画像の説明を入力 アニメーション前後の期待されるビュー。


y=50しかし、アニメーションが行われる前にで作成されたレイアウトが作成されviewWillAppearなかったかのように、ラベルは にありました (左の画像)。ただし、ラベルの背景が黄色 ( で指定された色viewWillAppear) に設定されていることに注意してください。

ここに画像の説明を入力 ここに画像の説明を入力 アニメ冒頭のレイアウトがおかしい。バー ボタンも間違った位置/サイズで始まることに注意してください。


コンソール ログ
TransitionTest[49795:c07] -[DetailViewController viewDidLoad]
TransitionTest[49795:c07] transitionFromViewController の前:
TransitionTest[49795:c07] -[DetailViewController viewWillAppear:]
TransitionTest[49795:c07] -[DetailViewController viewWillLayoutSubviews]
TransitionTest[49795:c07 ] -[DetailViewController viewDidLayoutSubviews]
TransitionTest[49795:c07] -[DetailViewController viewDidAppear:] AFTERが呼び出され
たことに注意viewWillAppear: transitionFromViewController:


問題 1 の解決策

よし、ここから部分解の部分に入る。beginAppearanceTransition:endAppearanceTransitionを明示的に呼び出すことによりtoViewController、遷移アニメーションが発生する前に、ビューは正しいレイアウトになります。

- (void)pushController:(UIViewController *)toViewController
{
    UIViewController *fromViewController = [self.childViewControllers lastObject];

    [self addChildViewController:toViewController];
    toViewController.view.frame = self.view.bounds;

    [toViewController beginAppearanceTransition:YES animated:NO];

    NSLog(@"Before transitionFromViewController:");
    [self transitionFromViewController:fromViewController
                      toViewController:toViewController
                              duration:0.5
                               options:UIViewAnimationOptionTransitionCrossDissolve
                            animations:^{}
                            completion:^(BOOL finished) {
                                [toViewController didMoveToParentViewController:self];
                                [toViewController endAppearanceTransition];
                            }];
}

viewWillAppear:BEFORE TransitionTest[ 18398transitionFromViewController: :c07] -[DetailViewController viewDidLoad]
TransitionTest[18398:c07] -[DetailViewController viewWillAppear:]
TransitionTest[18398:c07] TransitionFromViewController の前:
TransitionTest[18398:c07] -[DetailViewController viewWillLayoutSubviews]
TransitionTest [18398:c07] -[DetailViewController viewDidLayoutSubviews]
TransitionTest[18398:c07] -[DetailViewController viewDidAppear:]


しかし、それでは問題 2 は解決しません。

なんらかの理由で、ナビゲーション バーのボタンは、遷移アニメーションの開始時に間違った位置/サイズで開始されます。私は正しい解決策を見つけるために多くの時間を費やしましたが、運がありませんでした. 私はそれがバグだと感じ始めていtransitionFromViewController:ますUIAppearance。この質問に対してあなたが提供できる洞察をいただければ幸いです。ありがとう!


私が試した他の解決策

  • [self.view addSubview:toViewController.view];前に電話するtransitionFromViewController:

実際には、ユーザーに正確な結果を提供し、問題 1 と 2 の両方を修正します。問題は、viewWillAppear両方viewDidAppearが 2 回呼び出されることです! で大規模なアニメーションや計算を行いたい場合は問題がありviewDidAppearます。

  • [toViewController viewWillAppear:YES];前に電話するtransitionFromViewController:

を呼び出すのとほとんど同じだと思いますbeginAppearanceTransition:。問題 1 は修正されますが、問題 2 は修正されません。さらに、ドキュメントには、viewWillAppear直接呼び出してはいけないと書かれています。

  • [UIView animateWithDuration:]の代わりに使用transitionFromViewController:

このように: [self addChildViewController:toViewController]; [self.view addSubview:toViewController.view]; toViewController.view.alpha = 0.0;

[UIView animateWithDuration:0.5 animations:^{
    toViewController.view.alpha = 1.0;
} completion:^(BOOL finished) {
    [toViewController didMoveToParentViewController:self];
}];

問題 2 は修正されますが、ビューはレイアウトviewDidAppear(ラベルは緑、y=350) で開始されます。また、クロスディゾルブは使用するほど良くありませんUIViewAnimationOptionTransitionCrossDissolve

4

2 に答える 2

7

OK、layoutIfNeeded を toViewController.view に追加するとうまくいくようです。これにより、画面に表示される前に (追加/削除なしで) ビューが適切にレイアウトされ、奇妙な二重の viewDidAppear: 呼び出しがなくなります。

- (void)pushController:(UIViewController *)toViewController
{
    UIViewController *fromViewController = [self.childViewControllers lastObject];

    [self addChildViewController:toViewController];

    toViewController.view.frame = self.view.bounds;
    [toViewController.view layoutIfNeeded];

    NSLog(@"Before transitionFromViewController:");
    [self transitionFromViewController:fromViewController
                      toViewController:toViewController
                              duration:0.5
                               options:UIViewAnimationOptionTransitionCrossDissolve
                            animations:^{}
                            completion:^(BOOL finished) {
                            }];

}
于 2013-02-05T17:11:16.033 に答える