web-dev-qa-db-ja.com

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

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

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

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

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

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

近い例ですが、アニメーションはありません: Navigation Controllerなしで複数のiOSカスタムView Controllerを使用する方法

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

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

141
TigerCoding

EDIT:任意の方向で機能する新しい回答。元の回答は、インターフェイスが縦向き。これは、ウィンドウに追加された最初のビュー(たとえば、window.rootViewController.view.anotherView)の下のレベル以上のビューで異なるビューを発生させる必要があるビューを置き換えるb/cビュー遷移アニメーションです。

TransitionControllerという単純なコンテナクラスを実装しました。 https://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;
}

任意のView Controllerから新しいView Controllerに移行するには

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

編集:以下のオリジナルの回答-縦向きの場合にのみ機能します

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

  1. ウィンドウのrootViewControllerとして割り当てられたView Controllerがあります

  2. 新しいビューに切り替えるとき、現在のviewControllerを新しいビューを所有するviewControllerに置き換えます。常に、現在のviewControllerのみが生きています(たとえば、割り当てられています)。

コードは簡単に変更して、異なる動作をさせることができます。キーポイントは、アニメーション化されたトランジションとシングルビューコントローラーです。 window.rootViewControllerに割り当てる以外の場所にView Controllerを保持しないでください。

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

- (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;
                    }];
}

View Controllerでの使用例

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

Appleの新しいviewController包含システムを使用できます。詳細については、WWDC 2011セッションビデオ「Implementing UIViewController Containment」をご覧ください。

IOS5の新機能であるUIViewController Containmentを使用すると、親viewControllerとその中に含まれるいくつかの子viewControllerを使用できます。これがUISplitViewControllerの仕組みです。これを行うと、親でView Controllerをスタックできますが、特定のアプリケーションでは、単に親を使用して、表示されているViewControllerから別のViewControllerへの移行を管理しています。これはApple承認済みの方法であり、1つの子viewControllerからアニメーション化するのは簡単です。さらに、さまざまなUIViewAnimationOptionトランジションをすべて使用できます。

また、UIViewContainmentを使用すると、オリエンテーションイベント中に子viewControllersを管理する面倒さについて心配する必要はありません。次を使用して、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 。この 他のプロジェクト は、画面全体を占有しないさまざまな入力viewControllerタイプでUIViewController Containmentを使用する方法を示しています。幸運を

67
timthetoolman

OK、私は質問がNavigation Controllerを使用せずに言っていることを知っていますが、そうしない理由はありません。 OPは私が眠りにつくのに間に合うようにコメントに応答していませんでした。私に投票しないでください。 :)

Navigation Controllerを使用して現在のView Controllerをポップし、新しいView Controllerに切り替える方法は次のとおりです。

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];
7

私はちょうどこの正確な問題に遭遇し、限られた成功に対する既存のすべての答えのバリエーションを試したので、最終的にどのように解決したかを投稿します:

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

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

私の場合、同様の置換セグエが発生することを望んでいましたが、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

次に、Ctrlキーを押しながらドラッグして他の種類のセグエを設定し、それをカスタムセグエにし、カスタムセグエクラスの名前を入力します。

3
Ryan Artecona

私はこれに長い間苦労しました、そして、私の問題の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;

子View Controller配列は、スタック内のすべてのView Controllerのストアとして機能します。すべての回転とサイズ変更コードを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などを実装できるようになりました。

幸運を祈ります。これがお役に立てば幸いです!

2
aopsfan