ナビゲーションコントローラースタック、サブビュー、モーダルコントローラーを使用せずにビューコントローラーの変化をアニメーション化しますか?


142

NavigationControllerには、管理するViewControllerスタックがあり、アニメーションの遷移が制限されています。

既存のビューコントローラーにビューコントローラーをサブビューとして追加するには、イベントをサブビューコントローラーに渡す必要があります。これは、管理が面倒であり、煩わしさがほとんどなく、実装時に一般に悪いハックのように感じられます(Appleはまた、これを行う)。

モーダルビューコントローラーを再び表示すると、ビューコントローラーが別のビューコントローラーの上に配置され、上記のイベントパッシングの問題はありませんが、ビューコントローラーを実際に「スワップ」するのではなく、スタックします。

ストーリーボードはiOS 5に限定されており、ほぼ理想的ですが、すべてのプロジェクトで使用できるわけではありません。

誰かが上記の制限なしにView Controllerを変更する方法についてソリッドコード例を提示し、それらの間のアニメーション化された遷移を可能にすることができますか?

近い例ですが、アニメーションはありません: ナビゲーションコントローラーなしで複数のiOSカスタムビューコントローラーを使用する方法

編集:ナビゲーションコントローラーの使用は問題ありませんが、アニメーション化されたトランジションスタイル(単なるスライドエフェクトではない)が必要です。表示されているビューコントローラーは完全に(スタックではなく)交換する必要があります。2番目のビューコントローラーがスタックから別のビューコントローラーを削除する必要がある場合、十分にカプセル化されていません。

編集2:iOS 4がこの質問のベースOSである必要があります。ストーリーボード(上記)について言及するときは、それを明確にすべきでした。


1
ナビゲーションコントローラーを使用して、カスタムのアニメーション遷移を行うことができます。これが許容できる場合は、質問からその制約を削除してください。コード例を投稿します。
Richard Brightwell、2011年

@Richardがスタックの管理の手間を省き、View Controller間のさまざまなアニメーション化されたトランジションスタイルに対応している場合、Navigation Controllerの使用は問題ありません。
TigerCoding

ようし。いいぞ。私は焦って、コードを投稿しました。試してみる。私のために働く。
Richard Brightwell、2011年

@RichardBrightwellナビゲーションコントローラーを使用して、ビューコントローラー間でカスタムアニメーションのトランジションを実行できるとここで言いました。例を投稿できますか?ありがとう。
ダック

回答:


108

編集:任意の方向で機能する新しい答え。 元の答えは、インターフェースが縦向きの場合にのみ機能します。これは、別のビューのビューを置き換えるb / cビュー遷移アニメーションであり、少なくともウィンドウに追加された最初のビューよりも下のレベルのビューで発生する必要があります(例:)window.rootViewController.view.anotherView

私が呼び出した単純なコンテナークラスを実装しましたTransitionControllerhttps://gist.github.com/1394947で見つけることができます

余談ですが、別のクラスb / cで実装するほうが、再利用が簡単です。それを望まない場合は、同じロジックをアプリデリゲートに直接実装するだけで、TransitionControllerクラスの必要性を排除できます。ただし、必要なロジックは同じです。

次のように使用します。

アプリのデリゲート内

// add a property for the TransitionController

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
    MyViewController *vc = [[MyViewContoller alloc] init...];
    self.transitionController = [[TransitionController alloc] initWithViewController:vc];
    self.window.rootViewController = self.transitionController;
    [self.window makeKeyAndVisible];
    return YES;
}

任意のビューコントローラーから新しいビューコントローラーに移行するには

- (IBAction)flipToView
{
    anotherViewController *vc = [[AnotherViewController alloc] init...];
    MyAppDelegate *appDelegate = [UIApplication sharedApplication].delegate;
    [appDelegate.transitionController transitionToViewController:vc withOptions:UIViewAnimationOptionTransitionFlipFromRight];
}

編集:以下の元の回答-縦向きでのみ機能します

この例では、次のことを想定しています。

  1. あなたはrootViewControllerあなたのウィンドウのとして割り当てられたビューコントローラを持っています

  2. 新しいビューに切り替えるときは、現在のviewControllerを、新しいビューを所有するviewControllerに置き換えます。いつでも、現在のviewControllerのみが有効です(割り当て済みなど)。

コードは簡単に変更して異なる動作をさせることができます。重要な点は、アニメーション化された遷移と単一のビューコントローラーです。ビューコントローラをに割り当てる以外の場所に保持しないようにしてくださいwindow.rootViewController

アプリのデリゲートで遷移をアニメーション化するコード

- (void)transitionToViewController:(UIViewController *)viewController
                    withTransition:(UIViewAnimationOptions)transition
{
    [UIView transitionFromView:self.window.rootViewController.view
                        toView:viewController.view
                      duration:0.65f
                       options:transition
                    completion:^(BOOL finished){
                        self.window.rootViewController = viewController;
                    }];
}

ビューコントローラーでの使用例

- (IBAction)flipToNextView
{
    AnotherViewController *anotherVC = [[AnotherVC alloc] init...];
    MyAppDelegate *appDelegate = (MyAppDelegate *)[UIApplication sharedApplication].delegate;
    [appDelegate transitionToViewController:anotherVC
                             withTransition:UIViewAnimationOptionTransitionFlipFromRight];
}

1
うん、とってもいい!これはトリックを実行するだけでなく、非常にシンプルでクリーンなコード例です。どうもありがとう!
TigerCoding

彼はあなたのために風景の問題を引き起こしていませんか?また、これにより、viewControllersのwillAppearメソッドとdidAppearメソッドがトリガーされますか?
Felix Lamouroux、2011年

1
ログに記録したため、表示されている通話が行われていることがわかります。これが方向の変更に影響を与える理由もわかりません。なぜそうなると思いますか?
TigerCoding

1
@XJonesの優れたソリューションは、最初の初期化後、私が直面している1つの問題を除いて、iPadアプリでかなりうまく機能しますtransVC = [TransitionController ... initWithViewController:aUINavCtrlObj]; window.rootVC = transVC; aUINavCtrlObjのビューには上から20pxが埋め込まれます(つまり、下の20 pxはすべての方向で画面(UIWindow)の境界から外れます)。TransitionControllerのloadViewでwantsFullScreenLayout = NOを試しましたが、statusBarのすぐ下に20ピクセルの黒い領域が追加されます。
アブドゥリアムレーマニウス2012

1
@AbduliamRehmanius:同じ問題がありました。25行目をに変更して修正しましたTransitionController.mUIView *view = [[UIView alloc] initWithFrame:[UIScreen mainScreen].bounds];、これを使用したのは最新バージョンのiOSだけなので、慎重にテストしてください。
Phil Calvin

67

Appleの新しいviewController封じ込めシステムを使用できます。より詳細な情報については、WWDC 2011のセッションビデオ「Implementing UIViewControllerContainment」をご覧ください。

iOS5の新機能であるUIViewControllerContainmentでは、親のviewControllerと、その中に含まれるいくつかの子のviewControllerを使用できます。これは、UISplitViewControllerが機能する方法です。これを行うと、ビューコントローラーを親にスタックできますが、特定のアプリケーションでは、親を使用して、1つの表示可能なviewControllerから別のビューコントローラーへの遷移を管理します。これはAppleが承認した方法であり、1つの子viewControllerからのアニメーションは簡単です。さらに、さまざまなUIViewAnimationOptionトランジションをすべて使用できます。

また、UIViewContainmentを使用すると、オリエンテーションイベント中に子viewControllerを管理する煩わしさについて、必要でない限り心配する必要はありません。次のコードを使用するだけで、parentViewControllerが回転イベントを確実に子viewControllersに転送できるようになります。

- (BOOL)automaticallyForwardAppearanceAndRotationMethodsToChildViewControllers{
    return YES;
}

親のviewDidLoadメソッドで次のようなことを行うと、最初のchildViewControllerを設定できます。

[self addChildViewController:self.currentViewController];
[self.view addSubview:self.currentViewController.view];
[self.currentViewController didMoveToParentViewController:self];
[self.currentViewController.swapViewControllerButton setTitle:@"Swap" forState:UIControlStateNormal];

次に、子のviewControllerを変更する必要がある場合は、親のviewController内で次の行に沿って何かを呼び出します。

-(void)swapViewControllers:(childViewController *)addChildViewController:aNewViewController{
     [self addChildViewController:aNewViewController];
     __weak __block ViewController *weakSelf=self;
     [self transitionFromViewController:self.currentViewController
                       toViewController:aNewViewController
                               duration:1.0
                                options:UIViewAnimationOptionTransitionCurlUp
                             animations:nil
                             completion:^(BOOL finished) {
                                   [aNewViewController didMoveToParentViewController:weakSelf];

                                   [weakSelf.currentViewController willMoveToParentViewController:nil];
                                   [weakSelf.currentViewController removeFromParentViewController];

                                   weakSelf.currentViewController=[aNewViewController autorelease];
                             }];
 }

ここに完全なサンプルプロジェクトを投稿しました:https : //github.com/toolmanGitHub/stackedViewControllers。この別のプロジェクトUIViewController、画面全体を占めないいくつかのさまざまな入力viewControllerタイプでContainment を使用する方法を示しています。幸運を


1
良い例です。コードを投稿していただきありがとうございます。一方、iOS 5に限定されます。質問で述べたように、「[ストーリーボード]はiOS 5に限定されており、ほぼ理想的ですが、すべてのプロジェクトで使用できるわけではありません。」大部分の割合(約40%?)がまだiOS 4を使用していることを考えると、目標はiOS 4以降で機能するものを提供することです。
TigerCoding、

[self.currentViewController willMoveToParentViewController:nil];移行前に電話をかけるべきではありません か?
Felix Lamouroux

@FelixLam-UIViewControllerコンテインメントのドキュメントに従って、addChildViewControllerメソッドをオーバーライドする場合にのみ、willMoveToParentViewControllerを呼び出します。この例では、私はそれを呼び出していますが、オーバーライドしていません。
timthetoolman

2
WWDCのデモでは、トランジションはcurrentVCがnilに移行することを意味しないため、トランジションを開始する前にコールしたことを覚えているようです。UITabBarControllerの場合、遷移はvcの親を変更しません。親からの削除では、didMoveToParentViewController:nilを呼び出しますが、will ...呼び出しはありません。私見
Felix Lamouroux

7

わかりました。ナビゲーションコントローラーを使用せずに質問のとおりですが、使用しない理由はありません。OPは、私がスリープ状態になるのに間に合うようにコメントに応答していませんでした。投票しないでください。:)

現在のビューコントローラーをポップし、ナビゲーションコントローラーを使用して新しいビューコントローラーに切り替える方法は次のとおりです。

UINavigationController *myNavigationController = self.navigationController;
[[self retain] autorelease];

[myNavigationController popViewControllerAnimated:NO];

PreferencesViewController *controller = [[PreferencesViewController alloc] initWithNibName:nil bundle:nil];

[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration: 0.65];
[UIView setAnimationTransition:UIViewAnimationTransitionFlipFromRight forView:myNavigationController.view cache:YES];
[myNavigationController pushViewController:controller animated:NO];
[UIView commitAnimations];

[controller release];

このスタックビューコントローラーはありませんか?
TigerCoding

1
はい、ナビゲーションコントローラを使用しているためです。しかし、それはあなたがどのようなトランジションを実行できるかという制限を回避します。それはあなたの質問の核心であると私は思いました。
Richard Brightwell

近いですが、大きな問題の1つは、スタック上で管理する複数のView Controllerがあることです。ビューコントローラーを完全に変更する方法を探しています。=)
TigerCoding

ああ。私もそのようなものがあるかもしれません...ちょっと待ってください。そうでない場合は、この回答を削除します。
Richard Brightwell

OK、2つの異なるコードをまとめました。これはあなたがやりたいことをすると思います。
Richard Brightwell、2011年

3

私はこの正確な問題に遭遇し、限られた成功に対する既存のすべての回答のバリエーションを試したので、最終的にそれを解決した方法を投稿します。

カスタムセグエに関するこの投稿で説明したように、カスタムセグエを作成するのは実際には非常に簡単です。また、Interface Builderで接続するのも非常に簡単で、IB内の関係を表示したままにして、セグエのソース/宛先ビューコントローラーによるサポートをあまり必要としません。

上にリンクされた投稿は、navigationControllerスタックの現在のトップビューコントローラーを、トップインスライドアニメーションを使用する新しいものに置き換えるiOS 4コードを提供します。

私の場合、同様の置換セグエが発生することを望みましたが、FlipFromLeft遷移がありました。また、iOS 5以降のサポートのみが必要でした。コード:

RAFlipReplaceSegue.hから:

#import <UIKit/UIKit.h>

@interface RAFlipReplaceSegue : UIStoryboardSegue
@end

RAFlipReplaceSegue.mから:

#import "RAFlipReplaceSegue.h"

@implementation RAFlipReplaceSegue

-(void) perform
{
    UIViewController *destVC = self.destinationViewController;
    UIViewController *sourceVC = self.sourceViewController;
    [destVC viewWillAppear:YES];

    destVC.view.frame = sourceVC.view.frame;

    [UIView transitionFromView:sourceVC.view
                        toView:destVC.view
                      duration:0.7
                       options:UIViewAnimationOptionTransitionFlipFromLeft
                    completion:^(BOOL finished)
                    {
                        [destVC viewDidAppear:YES];

                        UINavigationController *nav = sourceVC.navigationController;
                        [nav popViewControllerAnimated:NO];
                        [nav pushViewController:destVC animated:NO];
                    }
     ];
}

@end

次に、コントロールドラッグして他の種類のセグエを設定し、それをカスタムセグエにして、カスタムセグエクラスの名前を入力します。


ストーリーボードなしでこれをプログラム的に行う方法はありますか?
zakdances 2013年

私の知る限り、セグエを使用するには、それを定義し、ストーリーボードで識別子を指定する必要があります。UIViewControllerの–performSegueWithIdentifier:sender:メソッドを使用して、コードでセグエを呼び出すことができます。
Ryan Artecona 2013年

1
viewDidXXXおよびviewWillXXXを直接呼び出さないでください。
ベンシンクレア

2

私はこれに長い間苦労しており、私の問題の1つがここにリストさています。その問題があったかどうかはわかりません。しかし、iOS 4で動作する必要がある場合は、以下をお勧めします。

まず、新しいNavigationControllerクラスを作成します。ここで、すべてのダーティな作業を行います。他のクラスは、インスタンスメソッドなどを「クリーン」に呼び出すことができますpushViewController:。あなたの.h

@interface NavigationController : UIViewController {
    NSMutableArray *childViewControllers;
    UIViewController *currentViewController;
}

- (void)transitionFromViewController:(UIViewController *)fromViewController toViewController:(UIViewController *)toViewController duration:(NSTimeInterval)duration animations:(void (^)(void))animations completion:(void (^)(BOOL))completion;
- (void)addChildViewController:(UIViewController *)childController;
- (void)removeChildViewController:(UIViewController *)childController;

子ビューコントローラー配列は、スタック内のすべてのビューコントローラーのストアとして機能します。すべてのローテーションおよびサイズ変更コードをNavigationControllerのビューからに自動的に転送しますcurrentController

今、私たちの実装では:

- (void)transitionFromViewController:(UIViewController *)fromViewController toViewController:(UIViewController *)toViewController duration:(NSTimeInterval)duration animations:(void (^)(void))animations completion:(void (^)(BOOL))completion
{
    currentViewController = [toViewController retain];
    // Put any auto- and manual-resizing handling code here

    [UIView animateWithDuration:duration animations:animations completion:completion];

    [fromViewController.view removeFromSuperview];
}

- (void)addChildViewController:(UIViewController *)childController {
    [childViewControllers addObject:childController];
}

- (void)removeChildViewController:(UIViewController *)childController {
    [childViewControllers removeObject:childController];
}

これらのメソッド呼び出しを使用してpushViewController:、独自のカスタムpopViewControllerなどを実装できるようになりました。

がんばってください。これがお役に立てば幸いです。


ここでも、既存のビューコントローラーのサブビューとしてビューコントローラーを追加する必要があります。確かに、これは既存のNavigation Controllerが行うことですが、それは基本的にそれとそのすべてのメソッドを書き換える必要があることを意味します。実際には、viewWillAppearおよび同様のメソッドを配布する必要はありません。私はこれを行うためのきれいな方法はないと思い始めています。しかし、私は時間と労力を割いていただきありがとうございます!
TigerCoding

必要に応じてビューコントローラを追加および削除するこのシステムでは、このソリューションにより、これらのメソッドを転送する必要がなくなると思います。
aopsfan 2011年

本気ですか?以前は同様の方法でView Controllerを交換していましたが、常にメッセージを転送する必要がありました。他に確認できますか?
TigerCoding

いいえ、限り、あなたは彼らが消えるよう、ビューコントローラのビューを削除し、彼らが表示されそうであるようにそれらを追加し、それが自動的にトリガ必要として、私はわからないんだけど、私はそれを引き受けるviewWillAppearviewDidAppearとな。
aopsfan

-1
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"Main" bundle:nil];
UINavigationController *viewController = (UINavigationController *)[storyboard instantiateViewControllerWithIdentifier:@"storyBoardIdentifier"];
viewController.modalTransitionStyle = UIModalTransitionStylePartialCurl;
[self presentViewController:viewController animated:YES completion:nil];

このコードを試してください。


このコードは、ビューコントローラーから、ナビゲーションコントローラーを持つ別のビューコントローラーへの遷移を提供します。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.