ios - pushviewcontroller ナビゲーションベースのアプリでプッシュとポップのアニメーションを変更する方法




11 Answers

これは私がいつもこの作業を完了した方法です。

プッシュの場合:

MainView *nextView=[[MainView alloc] init];
[UIView  beginAnimations:nil context:NULL];
[UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
[UIView setAnimationDuration:0.75];
[self.navigationController pushViewController:nextView animated:NO];
[UIView setAnimationTransition:UIViewAnimationTransitionFlipFromRight forView:self.navigationController.view cache:NO];
[UIView commitAnimations];
[nextView release];

ポップスの場合:

[UIView  beginAnimations:nil context:NULL];
[UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
[UIView setAnimationDuration:0.75];
[UIView setAnimationTransition:UIViewAnimationTransitionFlipFromLeft forView:self.navigationController.view cache:NO];
[UIView commitAnimations];

[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDelay:0.375];
[self.navigationController popViewControllerAnimated:NO];
[UIView commitAnimations];


私はまだこの点から多くのフィードバックを得ていますので、私は先に進んで、アニメーションブロックを使用するようにアップデートします。これはAppleがアニメーションをやり遂げるために推奨する方法です。

プッシュの場合:

MainView *nextView = [[MainView alloc] init];
[UIView animateWithDuration:0.75
                         animations:^{
                             [UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
                             [self.navigationController pushViewController:nextView animated:NO];
                             [UIView setAnimationTransition:UIViewAnimationTransitionFlipFromRight forView:self.navigationController.view cache:NO];
                         }];

ポップスの場合:

[UIView animateWithDuration:0.75
                         animations:^{
                             [UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
                             [UIView setAnimationTransition:UIViewAnimationTransitionFlipFromLeft forView:self.navigationController.view cache:NO];
                         }];
[self.navigationController popViewControllerAnimated:NO];
uinavigationcontroller custom transition

ナビゲーションベースのアプリケーションがあり、プッシュアニメーションとポップアニメーションのアニメーションを変更したいと考えています。 どうすればいい?




@Magnusの答えはSwift(2.0)のためだけです。

    let transition = CATransition()
    transition.duration = 0.5
    transition.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)
    transition.type = kCATransitionPush
    transition.subtype = kCATransitionFromTop
    self.navigationController!.view.layer.addAnimation(transition, forKey: nil)
    let writeView : WriteViewController = self.storyboard?.instantiateViewControllerWithIdentifier("WriteView") as! WriteViewController
    self.navigationController?.pushViewController(writeView, animated: false)

いくつかのsidenotes:

これはSegueでも同様に行うことができます。これをprepareForSegueまたはshouldPerformSegueWithIdentifier実装するだけshouldPerformSegueWithIdentifierただし 、これによってデフォルトのアニメーションも保持されます。 これを修正するには、ストーリーボードに移動してSegueをクリックし、 'Animates'のチェックを外します。 しかし、これはあなたのアプリをIOS 9.0以上(Xcode 7でやった時代以上)に制限します。

セグの場合、最後の2行を次のように置き換えます。

self.navigationController?.popViewControllerAnimated(false)

私が偽を設定しても、それは無視されます。




ナビゲーションベースのアプリケーションでプッシュとポップのアニメーションを変更する方法...

2018年、「最終回答!」

はじめに あなたはおそらくAndroidから、iOS開発の初心者です。 非常に混乱して、Appleは簡単に使用できる2つのトランジションを提供しています。 これらは "クロスフェード"と "フリップ"です。 現在、誰も使用していない "クロスフェード"と "フリップ"は、すべてのコンピューティングで最も無駄な2つのトランジションです。 「滑り落ちる」などの最も一般的なトランジションを実行したい場合は、大量の作業を行う必要があります。 その仕事は、この記事で説明されています!

  • まず、アップルが提供する2つのアニメの1つ(クロスフェード、フリップ) を使用したい場合は、単純です。上記の@PeterDeWeeseのソリューションを使用します。

  • 第二に、古いすばやく汚れたCATransitionクイックフィックスがあります。 これについてはここで詳しく説明します 。 実際には機能しませんし、現実的な解決策ではありません。

それ以外の場合は、驚くべきことに、 完全なカスタムトランジションを実行するように努力する必要があります。

繰り返す:

たとえ最も簡単で最も一般的なムーブオーバー/ムーブオフトランジションが必要な場合でも、完全なカスタムトランジションを実装する必要があります。

ここでそれを行う方法です...

1.カスタムUIViewControllerAnimatedTransitioningが必要です。

  1. popStyleのような自分のブールが必要popStyle 。 (それが飛び出しているのか、それとも飛び出していますか?)

  2. あなたは、 transitionDuration (簡単な)とメイン呼び出しanimateTransition

  3. 実際には、 animateTransition内部に2つの異なるルーチンを記述する必要があります。 1つはプッシュ、もう1つはポップです。 おそらく、 animatePushanimatePop名前を付けます。 animateTransition内部で、 popStyleを2つのルーチンに分岐するだけです

  4. 以下の例では単純な移動/移動を行います

  5. あなたのanimatePushanimatePopルーチンで。 あなたは "見るから"と "見る"必要があります。 (How-toはこの例に示されています)。

  6. あなたがしなければならない主なことは、実際には、新しい "to"ビューのためのaddSubviewを追加することです。

  7. あなたはあなたのアニメの終わりにcompleteTransitionを呼び出さなければなりません

そう ..

  class SimpleOver: NSObject, UIViewControllerAnimatedTransitioning {

        var popStyle: Bool = false

        func transitionDuration(
            using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
            return 0.20
        }

        func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {

            if popStyle {

                animatePop(using: transitionContext)
                return
            }

            let fz = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.from)!
            let tz = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.to)!

            let f = transitionContext.finalFrame(for: tz)

            let fOff = f.offsetBy(dx: f.width, dy: 55)
            tz.view.frame = fOff

            transitionContext.containerView.insertSubview(tz.view, aboveSubview: fz.view)

            UIView.animate(
                withDuration: transitionDuration(using: transitionContext),
                animations: {
                    tz.view.frame = f
            }, completion: {_ in 
                    transitionContext.completeTransition(true)
            })
        }

        func animatePop(using transitionContext: UIViewControllerContextTransitioning) {

            let fz = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.from)!
            let tz = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.to)!

            let f = transitionContext.initialFrame(for: fz)
            let fOffPop = f.offsetBy(dx: f.width, dy: 55)

            transitionContext.containerView.insertSubview(tz.view, belowSubview: fz.view)

            UIView.animate(
                withDuration: transitionDuration(using: transitionContext),
                animations: {
                    fz.view.frame = fOffPop
            }, completion: {_ in 
                    transitionContext.completeTransition(true)
            })
        }
    }

その後 ...

2.ビューコントローラで使用します。

「最初の」ビューコントローラでこれを行うだけでよいことに注意してください。

一番上にポップするものは何もしません。 簡単です。

あなたのクラス...

class SomeScreen: UIViewController {
}

になる...

class FrontScreen: UIViewController,
        UIViewControllerTransitioningDelegate, UINavigationControllerDelegate {

    let simpleOver = SimpleOver()


    override func viewDidLoad() {

        super.viewDidLoad()
        navigationController?.delegate = self
    }

    func navigationController(
        _ navigationController: UINavigationController,
        animationControllerFor operation: UINavigationControllerOperation,
        from fromVC: UIViewController,
        to toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? {

        simpleOver.popStyle = (operation == .pop)
        return simpleOver
    }
}

それでおしまい。

押してポップアップを通常のように正確に行います。 プッシュする ...

let n = UIStoryboard(name: "nextScreenStoryboardName", bundle: nil)
          .instantiateViewController(withIdentifier: "nextScreenStoryboardID")
          as! NextScreen
navigationController?.pushViewController(n, animated: true)

それをポップするには、次の画面でそれをしたい場合は、

class NextScreen: TotallyOrdinaryUIViewController {

    @IBAction func userClickedBackOrDismissOrSomethingLikeThat() {

        navigationController?.popViewController(animated: true)
    }
}

それは "それほど単純な"ものです:O




これがGoogleのトップの結果であることから、私は自分が思っていることを最も純然たる方法で共有すると思っていました。 これはiOS 7+移行APIを使用することです。 私はSwift 3でiOS 10のためにこれを実装しました。

UINavigationControllerのサブクラスを作成し、 UIViewControllerAnimatedTransitioningプロトコルに準拠したクラスのインスタンスを返す場合は、これを2つのView Controller間でアニメーション化する方法と組み合わせるのはUIViewControllerAnimatedTransitioningです。

例えば、ここに私のUINavigationControllerサブクラスがあります:

class NavigationController: UINavigationController {
    init() {
        super.init(nibName: nil, bundle: nil)

        delegate = self
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

extension NavigationController: UINavigationControllerDelegate {

    public func navigationController(_ navigationController: UINavigationController, animationControllerFor operation: UINavigationControllerOperation, from fromVC: UIViewController, to toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? {
        return NavigationControllerAnimation(operation: operation)
    }

}

UINavigationControllerDelegateを自分自身に設定し、サブクラスの拡張UINavigationControllerDelegateUINavigationControllerDelegateのメソッドを実装することで、カスタムアニメーションコントローラ( NavigationControllerAnimation )を返すことができます。 このカスタムアニメーションコントローラは、ストックアニメーションを置き換えます。

あなたはおそらく、私がNavigationControllerAnimationインスタンスにその初期化子を介して操作を渡す理由を疑問に思っています。 私はNavigationControllerAnimationUIViewControllerAnimatedTransitioningプロトコルの実装で、操作が何であるかを知っています(つまり、 'push'または 'pop')。 これは、どのようなアニメーションをすべきかを知るのに役立ちます。 ほとんどの場合、操作によって異なるアニメーションを実行する必要があります。

残りはかなり標準的です。 UIViewControllerAnimatedTransitioningプロトコルで2つの必要な機能を実装し、好きなようにアニメーション化します。

class NavigationControllerAnimation: NSObject, UIViewControllerAnimatedTransitioning {

    let operation: UINavigationControllerOperation

    init(operation: UINavigationControllerOperation) {
        self.operation = operation

        super.init()
    }

    func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
        return 0.3
    }

    public func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
        guard let fromViewController = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.from),
            let toViewController = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.to) else { return }
        let containerView = transitionContext.containerView

        if operation == .push {
            // do your animation for push
        } else if operation == .pop {
            // do your animation for pop
        }
    }
}

異なるタイプの操作(つまり、「プッシュ」または「ポップ」)ごとに、表示コントローラーと表示コントローラーが異なることに注意してください。 プッシュ操作中は、表示するコントローラーはプッシュされたコントローラーになります。 ポップ操作では、to viewコントローラーは、移行先のコントローラーになり、from viewコントローラーはポップされたコントローラーになります。

また、 to viewコントローラは、遷移コンテキストでcontainerViewサブビューとして追加する必要があります。

アニメーションが完了したら、 transitionContext.completeTransition(true)呼び出す必要があります。 インタラクティブなトランジションを行っている場合は、アニメーションの最後にトランジションが完了しているかどうかによって、 BoolcompleteTransition(didComplete: Bool)に動的に戻す必要があります。

最後に( オプションの読書 )、私はどのように私が取り組んでいた移行をしたかを見たいかもしれません。 このコードはもう少しハッキリですが、私はそれをかなり素早く書きました。素晴らしいアニメーションコードだとは言いませんが、アニメーションパーツのやり方はまだ分かっています。

私のことは本当に単純な移​​行でした。 私はUINavigationControllerと同じアニメーションを模倣したいと思っていましたが、 '上のアニメーションの次のページ'の代わりに、新しいビューと同時に古いビューコントローラの1:1アニメーションを実装したかったのですコントローラーが表示されます。 これは、2つのView Controllerを互いに固定されているかのように見せるという効果があります。

プッシュ操作では、最初にtoViewControllerのビュー原点を画面外のx軸に設定し、それをcontainerViewのサブビューとして追加し、 origin.xをゼロに設定して画面にアニメーションをorigin.xするorigin.xがあります。 同時に、 origin.xを画面外に設定することによって、 fromViewControllerのビューをアニメーション化します。

toViewController.view.frame = containerView.bounds.offsetBy(dx: containerView.frame.size.width, dy: 0.0)

containerView.addSubview(toViewController.view)

UIView.animate(withDuration: transitionDuration(using: transitionContext),
               delay: 0,
               options: [ UIViewAnimationOptions.curveEaseOut ],
               animations: {
                toViewController.view.frame = containerView.bounds
                fromViewController.view.frame = containerView.bounds.offsetBy(dx: -containerView.frame.size.width, dy: 0)
},
               completion: { (finished) in
                transitionContext.completeTransition(true)
})

ポップ操作は基本的に逆です。 toViewControllerのサブビューとして追加し、左からtoViewControllerアニメーション化するときにtoViewController右にアニメーション化します。

containerView.addSubview(toViewController.view)

UIView.animate(withDuration: transitionDuration(using: transitionContext),
               delay: 0,
               options: [ UIViewAnimationOptions.curveEaseOut ],
               animations: {
                fromViewController.view.frame = containerView.bounds.offsetBy(dx: containerView.frame.width, dy: 0)
                toViewController.view.frame = containerView.bounds
},
               completion: { (finished) in
                transitionContext.completeTransition(true)
})

迅速なファイル全体の要点は次のとおりです。

https://gist.github.com/alanzeino/603293f9da5cd0b7f6b60dc20bc766be




ここで私はスイフトで同じことをしています:

プッシュの場合:

    UIView.animateWithDuration(0.75, animations: { () -> Void in
        UIView.setAnimationCurve(UIViewAnimationCurve.EaseInOut)
        self.navigationController!.pushViewController(nextView, animated: false)
        UIView.setAnimationTransition(UIViewAnimationTransition.FlipFromRight, forView: self.navigationController!.view!, cache: false)
    })

ポップスの場合:

私は実際にこれを上記の反応のいくつかと少し違って行いました - しかし私はSwift開発には新しいので、正しくないかもしれません。 私はviewWillDisappear:animated:をオーバーライドし、そこにポップコードを追加しました:

    UIView.animateWithDuration(0.75, animations: { () -> Void in
        UIView.setAnimationCurve(UIViewAnimationCurve.EaseInOut)
        UIView.setAnimationTransition(UIViewAnimationTransition.FlipFromLeft, forView: self.navigationController!.view, cache: false)
    })

    super.viewWillDisappear(animated)



私は最近、似たようなことをしようとしていました。 私はUINavigationControllerのスライディングアニメーションが気に入らないと決めたのですが、UIViewがあなたにカールやそのようなものを与えるアニメーションもしたくありませんでした。 私はプッシュまたはポップ時にビュー間でクロスフェードを行いたいと思っていました。

そこでの問題は、ビューが文字通り現在のビューの上にビューを表示したりポップしたりするため、フェードが機能しないという事実があります。 私の新しいビューを取得し、それをUIViewControllerのスタック上の現在のトップビューにサブビューとして追加することになった解決策。 私は0のアルファでそれを加えてから、クロスフェードします。 アニメーションシーケンスが終了すると、ビューをアニメーション化せずにスタックにプッシュします。 私は古いTopViewに戻り、私が変更したものをクリーンアップします。

その複雑さは、それよりも少し複雑です。なぜなら、遷移を正しく見せるために調整しなければならないnavigationItemがあるからです。 また、回転を行う場合は、ビューをサブビューとして追加するときにフレームサイズを調整して、画面に正しく表示されるようにする必要があります。 ここで私が使ったコードのいくつかがあります。 私はUINavigationControllerをサブクラス化し、プッシュメソッドとポップメソッドをオーバーライドしました。

-(void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated
{
      UIViewController *currentViewController = [self.viewControllers lastObject];
      //if we don't have a current controller, we just do a normal push
      if(currentViewController == nil)
      {
         [super pushViewController:viewController animated:animated];
         return;
      }
      //if no animation was requested, we can skip the cross fade
      if(!animation)
      {
         [super pushViewController:viewController animated:NO];
         return;
      }
      //start the cross fade.  This is a tricky thing.  We basically add the new view
//as a subview of the current view, and do a cross fade through alpha values.
//then we push the new view on the stack without animating it, so it seemlessly is there.
//Finally we remove the new view that was added as a subview to the current view.

viewController.view.alpha = 0.0;
//we need to hold onto this value, we'll be releasing it later
    NSString *title = [currentViewController.title retain];

//add the view as a subview of the current view
[currentViewController.view addSubview:viewController.view];
[currentViewController.view bringSubviewToFront:viewController.view];
UIBarButtonItem *rButtonItem = currentViewController.navigationItem.rightBarButtonItem;
UIBarButtonItem *lButtonItem = currentViewController.navigationItem.leftBarButtonItem;

NSArray *array = nil;

//if we have a right bar button, we need to add it to the array, if not, we will crash when we try and assign it
//so leave it out of the array we are creating to pass as the context.  I always have a left bar button, so I'm not checking to see if it is nil. Its a little sloppy, but you may want to be checking for the left BarButtonItem as well.
if(rButtonItem != nil)
    array = [[NSArray alloc] initWithObjects:currentViewController,viewController,title,lButtonItem,rButtonItem,nil];
else {
    array = [[NSArray alloc] initWithObjects:currentViewController,viewController,title,lButtonItem,nil];
}

//remove the right bar button for our transition
[currentViewController.navigationItem setRightBarButtonItem:nil animated:YES];
//remove the left bar button and create a backbarbutton looking item
//[currentViewController.navigationItem setLeftBarButtonItem:nil animated:NO];

//set the back button
UIBarButtonItem *backButton = [[UIBarButtonItem alloc] initWithTitle:title style:kButtonStyle target:self action:@selector(goBack)];
[currentViewController.navigationItem setLeftBarButtonItem:backButton animated:YES];
[viewController.navigationItem setLeftBarButtonItem:backButton animated:NO];
[backButton release];

[currentViewController setTitle:viewController.title];

[UIView beginAnimations:@"push view" context:array];
[UIView setAnimationDidStopSelector:@selector(animationForCrossFadePushDidStop:finished:context:)];
[UIView setAnimationDelegate:self];
[UIView setAnimationDuration:0.80];
[viewController.view setAlpha: 1.0];
[UIView commitAnimations];
}

-(void)animationForCrossFadePushDidStop:(NSString *)animationID finished:(NSNumber *)finished context:(void *)context
{

UIViewController *c = [(NSArray*)context objectAtIndex:0];
UIViewController *n = [(NSArray*)context objectAtIndex:1];
NSString *title     = [(NSArray *)context objectAtIndex:2];
UIBarButtonItem *l = [(NSArray *)context objectAtIndex:3];
UIBarButtonItem *r = nil;
//not all views have a right bar button, if we look for it and it isn't in the context,
//we'll crash out and not complete the method, but the program won't crash.
//So, we need to check if it is there and skip it if it isn't.
if([(NSArray *)context count] == 5)
    r = [(NSArray *)context objectAtIndex:4];

//Take the new view away from being a subview of the current view so when we go back to it
//it won't be there anymore.
[[[c.view subviews] lastObject] removeFromSuperview];
[c setTitle:title];
[title release];
//set the search button
[c.navigationItem setLeftBarButtonItem:l animated:NO];
//set the next button
if(r != nil)
    [c.navigationItem setRightBarButtonItem:r animated:NO];


[super pushViewController:n animated:NO];

 }

私がコードで言及したように、私は常に左のボタンの項目を持っているので、アニメーションデリゲートのコンテキストとして渡す配列に配置する前にそれがnilかどうかをチェックしません。 これを行う場合は、そのチェックをしたいかもしれません。

私が見つけた問題は、デリゲートメソッドでクラッシュすると、プログラムがクラッシュしないということでした。 デリゲートが完了するのを止めるだけですが、警告は表示されません。
そのデリゲートルーチンで私のクリーンアップを行っていたので、それはクリーンアップを終了していないので、奇妙な視覚的な振る舞いを引き起こしていました。

私が作成する戻るボタンは "goBack"メソッドを呼び出し、そのメソッドは単にpopルーチンを呼び出します。

-(void)goBack
{ 
     [self popViewControllerAnimated:YES];
}

また、ここに私のポップのルーチンです。

-(UIViewController *)popViewControllerAnimated:(BOOL)animated
{
    //get the count for the number of viewControllers on the stack
int viewCount = [[self viewControllers] count];
//get the top view controller on the stack
UIViewController *topViewController = [self.viewControllers objectAtIndex:viewCount - 1];
//get the next viewController after the top one (this will be the new top one)
UIViewController *newTopViewController = [self.viewControllers objectAtIndex:viewCount - 2];

//if no animation was requested, we can skip the cross fade
if(!animated)
{
    [super popViewControllerAnimated:NO];
            return topViewController;
}



//start of the cross fade pop.  A bit tricky.  We need to add the new top controller
//as a subview of the curent view controler with an alpha of 0.  We then do a cross fade.
//After that we pop the view controller off the stack without animating it.
//Then the cleanup happens: if the view that was popped is not released, then we
//need to remove the subview we added and change some titles back.
newTopViewController.view.alpha = 0.0;
[topViewController.view addSubview:newTopViewController.view];
[topViewController.view bringSubviewToFront:newTopViewController.view];
NSString *title = [topViewController.title retain];
UIBarButtonItem *lButtonItem = topViewController.navigationItem.leftBarButtonItem;
UIBarButtonItem *rButtonItem = topViewController.navigationItem.rightBarButtonItem;

//set the new buttons on top of the current controller from the new top controller
if(newTopViewController.navigationItem.leftBarButtonItem != nil)
{
    [topViewController.navigationItem setLeftBarButtonItem:newTopViewController.navigationItem.leftBarButtonItem animated:YES];
}
if(newTopViewController.navigationItem.rightBarButtonItem != nil)
{
    [topViewController.navigationItem setRightBarButtonItem:newTopViewController.navigationItem.rightBarButtonItem animated:YES];
}

[topViewController setTitle:newTopViewController.title];
//[topViewController.navigationItem.leftBarButtonItem setTitle:newTopViewController.navigationItem.leftBarButtonItem.title];

NSArray *array = nil;
if(rButtonItem != nil)
    array = [[NSArray alloc] initWithObjects:topViewController,title,lButtonItem,rButtonItem,nil];
else {
    array = [[NSArray alloc] initWithObjects:topViewController,title,lButtonItem,nil];
}


[UIView beginAnimations:@"pop view" context:array];
[UIView setAnimationDidStopSelector:@selector(animationForCrossFadePopDidStop:finished:context:)];
[UIView setAnimationDelegate:self];
[UIView setAnimationDuration:0.80];
[newTopViewController.view setAlpha: 1.0];
[UIView commitAnimations];
return topViewController;

 }

 -(void)animationForCrossFadePopDidStop:(NSString *)animationID finished:(NSNumber *)finished context:(void *)context
 {

UIViewController *c = [(NSArray *)context objectAtIndex:0];
//UIViewController *n = [(NSArray *)context objectAtIndex:1];
NSString *title = [(NSArray *)context objectAtIndex:1];
UIBarButtonItem *l = [(NSArray *)context objectAtIndex:2];
UIBarButtonItem *r = nil;



//Not all views have a right bar button.  If we look for one that isn't there
// we'll crash out and not complete this method, but the program will continue.
//So we need to check if it is therea nd skip it if it isn't.
if([(NSArray *)context count] == 4)
    r = [(NSArray *)context objectAtIndex:3];

//pop the current view from the stack without animation
[super popViewControllerAnimated:NO];

//if what was the current veiw controller is not nil, then lets correct the changes
//we made to it.
if(c != nil)
{
    //remove the subview we added for the transition
    [[c.view.subviews lastObject] removeFromSuperview];
    //reset the title we changed
    c.title = title;
    [title release];
    //replace the left bar button that we changed
    [c.navigationItem setLeftBarButtonItem:l animated:NO];
    //if we were passed a right bar button item, replace that one as well
    if(r != nil)
        [c.navigationItem setRightBarButtonItem:r animated:NO];
    else {
        [c.navigationItem setRightBarButtonItem:nil animated:NO];
    }


 }
}

それはかなりです。ローテーションを実装する場合は、いくつかの追加コードが必要になります。表示する前にサブビューとして追加するビューのフレームサイズを設定する必要があります。そうしないと、方向が横向きですが、直前のビューを見たのはそのポートレイトです。それで、それをサブビューとして追加してフェードインしますが、それはポートレートとして表示されます。アニメーションなしでポップすると、同じビューがスタックにあるものが現在は横長です。全体がちょっとファンキーに見えます。誰もがローテーションを実装しているのは少し違っているので、ここに私のコードは含めていません。

それが人々に役立つことを願っています。私はこのようなことを何度も見てきて何も見つけられませんでした。私はこれが完全な答えだとは思わないが、現時点では本当にうまくいっている。




ADTransitionController見てみましょう。これは、カスタム遷移アニメーション(そのAPIはUINavigationControllerのAPIと一致します)を持つUINavigationControllerの代わりに、Applidiumで作成したものです。

スウィープフェードキューブカルーセルズームなどのプッシュおよびポップアクションには、あらかじめ定義されたさまざまなアニメーションを使用できます。




今すぐ使用できますUIView.transition。注意してくださいanimated:false。これは、任意の遷移オプション、ポップ、プッシュ、またはスタック置換で機能します。

if let nav = self.navigationController
{
    UIView.transition(with:nav.view, duration:0.3, options:.transitionCrossDissolve, animations: {
        _ = nav.popViewController(animated:false)
    }, completion:nil)
}



はるかに少ないコード行でこれを行う方法については、この質問への私の答え参照してください。このメソッドを使用すると、新しいView Controllerの擬似「プッシュ」を任意の方法でアニメートできます。アニメーションが完了すると、標準のPushメソッドを使用したかのようにNavigation Controllerが設定されます。私の例では、スライドインを左から、または右から動かすことができます。便宜のためにコードをここに繰り返します。

-(void) showVC:(UIViewController *) nextVC rightToLeft:(BOOL) rightToLeft {
    [self addChildViewController:neighbor];
    CGRect offscreenFrame = self.view.frame;
    if(rightToLeft) {
        offscreenFrame.origin.x = offscreenFrame.size.width * -1.0;
    } else if(direction == MyClimbDirectionRight) {
        offscreenFrame.origin.x = offscreenFrame.size.width;
    }
    [[neighbor view] setFrame:offscreenFrame];
    [self.view addSubview:[neighbor view]];
    [neighbor didMoveToParentViewController:self];
    [UIView animateWithDuration:0.5 animations:^{
        [[neighbor view] setFrame:self.view.frame];
    } completion:^(BOOL finished){
        [neighbor willMoveToParentViewController:nil];
        [neighbor.view removeFromSuperview];
        [neighbor removeFromParentViewController];
        [[self navigationController] pushViewController:neighbor animated:NO];
        NSMutableArray *newStack = [[[self navigationController] viewControllers] mutableCopy];
        [newStack removeObjectAtIndex:1]; //self, just below top
        [[self navigationController] setViewControllers:newStack];
    }];
}



サンプルアプリから、このバリエーションを確認してください。https://github.com/mpospese/MPFoldTransition/

#pragma mark - UINavigationController(MPFoldTransition)

@implementation UINavigationController(MPFoldTransition)

//- (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated
- (void)pushViewController:(UIViewController *)viewController foldStyle:(MPFoldStyle)style
{
    [MPFoldTransition transitionFromViewController:[self visibleViewController] 
                                  toViewController:viewController 
                                          duration:[MPFoldTransition defaultDuration]  
                                             style:style 
                                        completion:^(BOOL finished) {
                                            [self pushViewController:viewController animated:NO];
                                        }
     ];
}

- (UIViewController *)popViewControllerWithFoldStyle:(MPFoldStyle)style
{
    UIViewController *toController = [[self viewControllers] objectAtIndex:[[self viewControllers] count] - 2];

    [MPFoldTransition transitionFromViewController:[self visibleViewController] 
                                  toViewController:toController 
                                          duration:[MPFoldTransition defaultDuration] 
                                             style:style
                                        completion:^(BOOL finished) {
                                            [self popViewControllerAnimated:NO];
                                        }
     ];

    return toController;
}



これを実現することは古い問題です。viewControllers提案された回答でいくつかの問題が発生したので、私はまだこの回答を投稿したいと思います。私の解決策は、UINavigationControllerすべてのポップとプッシュメソッドをサブクラス化してオーバーライドすることです。

FlippingNavigationController.h

@interface FlippingNavigationController : UINavigationController

@end

FlippingNavigationController.m:

#import "FlippingNavigationController.h"

#define FLIP_DURATION 0.5

@implementation FlippingNavigationController

- (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated
{
    [UIView transitionWithView:self.view
                      duration:animated?FLIP_DURATION:0
                       options:UIViewAnimationOptionCurveEaseInOut | UIViewAnimationOptionTransitionFlipFromRight
                    animations:^{ [super pushViewController:viewController
                                                   animated:NO]; }
                    completion:nil];
}

- (UIViewController *)popViewControllerAnimated:(BOOL)animated
{
    return [[self popToViewController:[self.viewControllers[self.viewControllers.count - 2]]
                             animated:animated] lastObject];
}

- (NSArray *)popToRootViewControllerAnimated:(BOOL)animated
{
    return [self popToViewController:[self.viewControllers firstObject]
                            animated:animated];
}

- (NSArray *)popToViewController:(UIViewController *)viewController animated:(BOOL)animated
{
    __block NSArray* viewControllers = nil;

    [UIView transitionWithView:self.view
                      duration:animated?FLIP_DURATION:0
                       options:UIViewAnimationOptionCurveEaseInOut | UIViewAnimationOptionTransitionFlipFromLeft
                    animations:^{ viewControllers = [super popToViewController:viewController animated:NO]; }
                    completion:nil];

    return viewControllers;
}

@end



Related