วิธีการเปลี่ยนภาพเคลื่อนไหว Push และ Pop ในแอพที่ใช้การนำทาง


221

ฉันมีแอพพลิเคชั่นที่ใช้การนำทางและฉันต้องการเปลี่ยนภาพเคลื่อนไหวของภาพเคลื่อนไหวแบบพุชและป๊อป ฉันจะทำอย่างไร

แก้ไข 2018

มีคำตอบมากมายสำหรับคำถามนี้และขณะนี้ค่อนข้างนานแล้วฉันได้เลือกคำตอบอีกครั้งสำหรับสิ่งที่ฉันเชื่อว่าเกี่ยวข้องที่สุด หากมีใครที่คิดเป็นอย่างอื่นโปรดแจ้งให้เราทราบในความคิดเห็น


25
ตั้งแต่ iOS 7 มี API อย่างเป็นทางการสำหรับสิ่งนี้ ดูการสนับสนุนการเปลี่ยนแปลงภาพเคลื่อนไหวที่กำหนดเองของUINavigationControllerDelegate นอกจากนี้ยังมีวิดีโอเกี่ยวกับเรื่องนี้ของWWDC 2013
Jesse Rusak

ฉันได้เพิ่มคำตอบ (ด้านล่าง) สำหรับการทำเช่นนี้ใน Swift - ฉันเจอคำถามนี้เพื่อถามเกี่ยวกับการใช้งาน Swift ดังนั้นคิดว่าฉันจะพูดสอดกับการใช้งานที่ตามมาของฉัน
djbp

1
สำหรับบทแนะนำที่ดีมากกับ API อย่างเป็นทางการ (iOS 7+) โปรดดูที่: bradbambara.wordpress.com/2014/04/11/…
nikolovski

1
@JesseRusak อัปเดตลิงก์ไปที่ WWDC 2013 วิดีโอ: developer.apple.com/videos/play/wwdc2013-218
Wojciech Rutkowski

1
เปลี่ยนคำตอบที่ฉันตอบรับแล้ว หวังว่านี่จะช่วยได้! GLHF
Jab

คำตอบ:


35

วิธีการเปลี่ยนภาพเคลื่อนไหว Push และ Pop ในแอพที่ใช้การนำทาง ...

สำหรับปี 2562 คำตอบสุดท้าย!

บทนำ:

สมมติว่าคุณยังใหม่กับการพัฒนา iOS แอปเปิ้ลมีการเปลี่ยนสองแบบซึ่งสามารถใช้งานได้ง่าย เหล่านี้คือ "crossfade" และ "flip"

แต่แน่นอนว่า "crossfade" และ "flip" นั้นไร้ประโยชน์ พวกเขาไม่เคยใช้ ไม่มีใครรู้ว่าทำไม Apple ให้การเปลี่ยนแปลงที่ไร้ประโยชน์ทั้งสองครั้งนั้น!

ดังนั้น:

สมมติว่าคุณต้องการเปลี่ยนแบบธรรมดาสามัญเช่น "เลื่อน" ในกรณีนี้คุณต้องทำงานเป็นจำนวนมาก! .

งานนั้นมีการอธิบายในโพสต์นี้

เพียงทำซ้ำ:

น่าแปลกที่: กับ iOS ของคุณ, ถ้าคุณต้องการที่ง่ายที่สุดที่พบมากที่สุดช่วงการเปลี่ยนในชีวิตประจำวัน (เช่นภาพนิ่งธรรมดา) คุณจะต้องทำงานทั้งหมดของการดำเนินการเปลี่ยนแปลงที่กำหนดเองเต็มรูปแบบ

นี่คือวิธีที่จะทำ ...

1. คุณต้องกำหนดเอง UIViewControllerAnimatedTransitioning

  1. popStyleคุณต้องบูลเช่นของคุณเอง (มันโผล่หรือโผล่ออกมาหรือไม่)

  2. คุณจะต้องรวมถึงtransitionDuration(เล็กน้อย) และสายหลักanimateTransition

  3. ในความเป็นจริงคุณจะต้องanimateTransitionเขียนสองการปฏิบัติที่แตกต่างกันสำหรับภายใน หนึ่งรายการสำหรับการผลักดันและอีกอันสำหรับป๊อป น่าจะเป็นชื่อพวกเขาและanimatePush animatePopข้างในanimateTransitionแค่แยกpopStyleไปที่สองรูทีน

  4. ตัวอย่างด้านล่างจะทำการเลื่อน / เลื่อน - ปิดแบบง่าย

  5. ในanimatePushและanimatePopกิจวัตรของคุณ คุณต้องได้รับ "จากการดู" และ "เพื่อดู" (วิธีการดังกล่าวแสดงในตัวอย่างโค้ด)

  6. และคุณต้อง 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)
    }
}

วุ้ย.


3. เพลิดเพลินไปกับคำตอบอื่น ๆ ในหน้านี้ซึ่งอธิบายถึงวิธีการแทนที่ AnimatedTransitioning

เลื่อนไปที่คำตอบ @AlanZeino และ @elias สำหรับการสนทนาเพิ่มเติมเกี่ยวกับวิธีการAnimatedTransitioningในแอพ iOS ในปัจจุบัน!


ยอดเยี่ยม หากฉันต้องการเลื่อนการนำทางด้วยท่าทางย้อนกลับสนับสนุน AnimatedTransitioning เดียวกันด้วย ความคิดใด ๆ
sam chi wen

ขอบคุณ @samchiwen - แน่นอนว่าเป็นสิ่งที่animatePushและanimatePopเป็น .. ทั้งสองทิศทางที่แตกต่างกัน!
Fattie

268

ฉันทำสิ่งต่อไปนี้และใช้งานได้ดี .. และเรียบง่ายและเข้าใจง่าย ..

CATransition* transition = [CATransition animation];
transition.duration = 0.5;
transition.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
transition.type = kCATransitionFade; //kCATransitionMoveIn; //, kCATransitionPush, kCATransitionReveal, kCATransitionFade
//transition.subtype = kCATransitionFromTop; //kCATransitionFromLeft, kCATransitionFromRight, kCATransitionFromTop, kCATransitionFromBottom
[self.navigationController.view.layer addAnimation:transition forKey:nil];
[[self navigationController] popViewControllerAnimated:NO];

และสิ่งเดียวกันสำหรับการผลักดัน ..


เวอร์ชั่น Swift 3.0:

let transition = CATransition()
transition.duration = 0.5
transition.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)
transition.type = kCATransitionFade
self.navigationController?.view.layer.add(transition, forKey: nil)
_ = self.navigationController?.popToRootViewController(animated: false)

34
+1 นี่เป็นทางออกที่มีเหตุผลมากที่สุด เป็นเพียงข้อความเล็กน้อยสำหรับผู้เยี่ยมชมในอนาคต: Animated:NOส่วนนี้มีความสำคัญ หากYESผ่านไปแล้วภาพเคลื่อนไหวจะปะปนและทำให้เอฟเฟกต์ตลก
DarkDust

12
ทางออกที่ดีที่สุดจนถึงตอนนี้และสำหรับผู้เริ่มต้นอย่าลืมรวม QuartCore (#import <QuartzCore / QuartzCore.h>)
nomann

4
ปัญหาเดียวที่ฉันมีกับวิธีแก้ปัญหานี้คือ viewDidAppear ที่ถูกผลักของ viewcontroller ถูกเรียกใช้ทันทีหลังจากฉันกดโดยไม่มีภาพเคลื่อนไหว มีวิธีแก้ไขไหม?
Pedro Mancheno

9
ปัญหาของฉันเกี่ยวกับรหัสนี้คือแต่ละมุมมองดูเหมือนจะกะพริบเป็นสีเทาหรือสีขาวเมื่อเลื่อนเข้าหรือออก
Chris

1
ตรวจสอบบน iOS 7.1.2 และ iOS 8.3 - รหัสนี้ทำงานได้ดีและทำงานได้ดีสำหรับวิธีการsetViewControllers:
proff

256

นี่คือวิธีที่ฉันจัดการเพื่อให้งานนี้สำเร็จ

สำหรับการผลักดัน:

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

3
ขอบคุณสำหรับสิ่งนี้. แต่ป๊อปจะทำโดยอัตโนมัติโดย UINavigationController คุณจะแทนที่พฤติกรรมดังกล่าวเพื่อให้คุณสามารถเรียกตรรกะป๊อปของคุณเองได้อย่างไร
โจชัวแฟรงค์

1
@stuckj ใช้งานได้จริง !!! คุณต้องเปลี่ยนsuperโดยself.navigationController
holierthanthou84

มีวิธีใดบ้างที่จะได้สไลด์จากด้านซ้ายแทนที่จะเป็นสไลด์เริ่มต้นจากด้านขวา?
shim

มุมมองแรกไม่แสดงมุมมองใหม่เลย ที่สองไม่แสดงภาพเคลื่อนไหว คำตอบที่แย่มาก! iOS 7.
มิทรี

2
ทำไมคุณถึงได้UIViewControllersubclass ชื่อโดยไม่มีส่วน "ViewController" ชื่อนี้เหมาะสมกว่าสำหรับ UIView
user2159978

29

สำหรับการผลักดัน

CATransition *transition = [CATransition animation];
transition.duration = 0.3;
transition.type = kCATransitionFade;
//transition.subtype = kCATransitionFromTop;

[self.navigationController.view.layer addAnimation:transition forKey:kCATransition];
[self.navigationController pushViewController:ViewControllerYouWantToPush animated:NO];

สำหรับป๊อป

CATransition *transition = [CATransition animation];
transition.duration = 0.3;
transition.type = kCATransitionFade;
//transition.subtype = kCATransitionFromTop;

[self.navigationController.view.layer addAnimation:transition forKey:kCATransition];
[self.navigationController popViewControllerAnimated:NO];

19

@ Magnagn ตอบแล้วเท่านั้นสำหรับ 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 อย่างไรก็ตามสิ่งนี้จะเก็บแอนิเมชั่นเริ่มต้นไว้ด้วยเช่นกัน ในการแก้ไขปัญหานี้คุณต้องไปที่กระดานเรื่องราวคลิกที่ Segue และยกเลิกการทำเครื่องหมายที่ช่อง 'แอนิเมชั่น' แต่สิ่งนี้จะ จำกัด แอปของคุณสำหรับ IOS 9.0 ขึ้นไป (อย่างน้อยเมื่อฉันทำใน Xcode 7)shouldPerformSegueWithIdentifier

เมื่อทำในการแยกคำสองบรรทัดสุดท้ายควรถูกแทนที่ด้วย:

self.navigationController?.popViewControllerAnimated(false)

แม้ว่าฉันจะตั้งข้อผิดพลาด แต่มันก็ไม่สนใจ


วิธีการลบสีดำเป็นพื้นหลังในตอนท้ายของการเคลื่อนไหว
Madhu

ไม่ทำงานสำหรับแอนนิเมชั่น push view คอนโทรลเลอร์ทำงานได้กับ Pop view controller
Mukul More

16

จำไว้ว่าในสวิฟท์ , ขยายเป็นมั่นเหมาะเพื่อนของคุณ!

public extension UINavigationController {

    /**
     Pop current view controller to previous view controller.

     - parameter type:     transition animation type.
     - parameter duration: transition animation duration.
     */
    func pop(transitionType type: String = kCATransitionFade, duration: CFTimeInterval = 0.3) {
        self.addTransition(transitionType: type, duration: duration)
        self.popViewControllerAnimated(false)
    }

    /**
     Push a new view controller on the view controllers's stack.

     - parameter vc:       view controller to push.
     - parameter type:     transition animation type.
     - parameter duration: transition animation duration.
     */
    func push(viewController vc: UIViewController, transitionType type: String = kCATransitionFade, duration: CFTimeInterval = 0.3) {
        self.addTransition(transitionType: type, duration: duration)
        self.pushViewController(vc, animated: false)
    }

    private func addTransition(transitionType type: String = kCATransitionFade, duration: CFTimeInterval = 0.3) {
        let transition = CATransition()
        transition.duration = duration
        transition.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)
        transition.type = type
        self.view.layer.addAnimation(transition, forKey: nil)
    }

}

11

การใช้การโทรส่วนตัวเป็นความคิดที่ไม่ดีเนื่องจาก Apple ไม่อนุมัติแอพที่ทำเช่นนั้น คุณอาจลองสิ่งนี้:

//Init Animation
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration: 0.50];


[UIView setAnimationTransition:UIViewAnimationTransitionCurlUp forView:self.navigationController.view cache:YES];

//Create ViewController
MyViewController *myVC = [[MyViewController alloc] initWith...];

[self.navigationController pushViewController:myVC animated:NO];
[myVC release];

//Start Animation
[UIView commitAnimations];

ใช้งานได้เพียง "ครึ่ง" เท่านั้น - มันจะไม่แก้ปัญหาที่หนักกว่าของแอนิเมชั่นป๊อป
อดัม

ฉันชอบวิธีนี้ดีกว่าและใช้ได้ดี การใช้วิธีการส่วนตัวจะทำให้คุณถูกปฏิเสธอย่างแน่นอน
Benjamin Intal

@nicktmro ซึ่งเป็นการเรียก api ส่วนตัว ฉันไม่ได้สังเกตอะไรเลย
Franklin

@ Franklin มีการพูดคุยกันซักพักแล้วที่นี่เป็น-pushViewController:transition:forceImmediate:ความคิดที่ไม่ดี
nicktmro

9

เนื่องจากนี่เป็นผลลัพธ์อันดับต้น ๆ ของ Google ฉันจึงคิดว่าฉันจะแบ่งปันสิ่งที่ฉันคิดว่าเป็นวิธีที่มีเหตุผลที่สุด สิ่งที่จะใช้ iOS 7+ การเปลี่ยน API ฉันใช้สิ่งนี้กับ iOS 10 ด้วย Swift 3

มันค่อนข้างง่ายที่จะรวมสิ่งนี้เข้ากับวิธีที่นิเมชั่นUINavigationControllerระหว่างตัวควบคุมมุมมองสองตัวถ้าคุณสร้างคลาสย่อยUINavigationControllerและคืนอินสแตนซ์ของคลาสที่สอดคล้องกับUIViewControllerAnimatedTransitioningโปรโตคอล

ตัวอย่างนี่คือUINavigationControllersubclass ของฉัน:

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ตัวเองและในส่วนขยายในคลาสย่อยของฉันฉันใช้วิธีการUINavigationControllerDelegateที่ช่วยให้คุณส่งคืนตัวควบคุมภาพเคลื่อนไหวที่กำหนดเอง (เช่นNavigationControllerAnimation) คอนโทรลเลอร์ภาพเคลื่อนไหวที่กำหนดเองนี้จะแทนที่ภาพเคลื่อนไหวหุ้นสำหรับคุณ

คุณอาจสงสัยว่าทำไมฉันผ่านการดำเนินการกับNavigationControllerAnimationอินสแตนซ์ผ่าน initializer ฉันทำสิ่งนี้เพื่อที่ว่าในNavigationControllerAnimationการนำUIViewControllerAnimatedTransitioningโพรโทคอลมาใช้ฉันรู้ว่าการดำเนินการคืออะไร (เช่น 'push' หรือ 'pop') สิ่งนี้จะช่วยให้รู้ว่าฉันควรทำอนิเมชั่นประเภทใด ส่วนใหญ่คุณต้องการแสดงภาพเคลื่อนไหวที่แตกต่างกันขึ้นอยู่กับการใช้งาน

ที่เหลือก็ค่อนข้างมาตรฐาน ใช้ฟังก์ชั่นที่จำเป็นสองอย่างในUIViewControllerAnimatedTransitioningโปรโตคอลและสร้างภาพเคลื่อนไหวตามที่คุณต้องการ:

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
        }
    }
}

สิ่งสำคัญคือต้องจำไว้ว่าสำหรับการดำเนินการแต่ละประเภทที่แตกต่างกัน (เช่น 'พุช' หรือ 'ป๊อป) ตัวควบคุมถึงและจากมุมมองจะแตกต่างกัน เมื่อคุณอยู่ในการดำเนินการพุชตัวควบคุมการดูจะเป็นสิ่งที่ถูกผลัก เมื่อคุณอยู่ในการดำเนินการ pop ตัวควบคุมการดูจะเป็นสิ่งที่กำลังถูกเปลี่ยนเป็นและตัวควบคุมจากมุมมองจะเป็นสิ่งที่ถูกเปิดขึ้นมา

นอกจากนี้toต้องเพิ่มตัวควบคุมมุมมองเป็นมุมมองย่อยของcontainerViewในบริบทการเปลี่ยนแปลง

transitionContext.completeTransition(true)เมื่อเสร็จสิ้นการเคลื่อนไหวของคุณคุณต้องเรียก ถ้าคุณกำลังทำการเปลี่ยนแปลงการโต้ตอบและคุณจะต้องแบบไดนามิกกลับBoolไปcompleteTransition(didComplete: Bool)ขึ้นอยู่กับว่าการเปลี่ยนแปลงเสร็จสมบูรณ์ในตอนท้ายของภาพเคลื่อนไหว

ในที่สุด ( การอ่านที่เป็นตัวเลือก ) คุณอาจต้องการดูว่าฉันได้ทำการเปลี่ยนแปลงที่ฉันกำลังทำอย่างไร รหัสนี้ค่อนข้างแฮ็กมากขึ้นและฉันเขียนมันค่อนข้างเร็วดังนั้นฉันจะไม่บอกว่าเป็นรหัสภาพเคลื่อนไหวที่ยอดเยี่ยม แต่มันยังคงแสดงวิธีการทำส่วนของภาพเคลื่อนไหว

ของฉันเป็นการเปลี่ยนแปลงที่ง่ายมาก ๆ ฉันต้องการเลียนแบบอนิเมชั่นแบบเดียวกับที่ UINavigationController ทำแทน แต่แทนที่จะเป็นอนิเมชัน 'หน้าถัดไปที่อยู่เหนือสุด' ฉันต้องการที่จะนำแอนิเมชั่น 1: 1 ของตัวควบคุมมุมมองเก่าออกไปในเวลาเดียวกันกับมุมมองใหม่ ปรากฏขึ้น นี่เป็นผลของการทำให้ตัวควบคุมมุมมองทั้งสองดูเหมือนว่าพวกเขาจะถูกตรึงซึ่งกันและกัน

สำหรับการดำเนินการพุชที่ต้องตั้งค่าtoViewControllerต้นกำเนิดของมุมมองบนหน้าจอปิดแกน x ให้เพิ่มเป็นมุมมองย่อยของcontainerViewภาพเคลื่อนไหวบนหน้าจอโดยตั้งค่าorigin.xเป็นศูนย์ ในขณะเดียวกันฉันทำให้fromViewControllerมุมมองของผู้ใช้เคลื่อนไหวโดยการorigin.xปิดหน้าจอ:

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มองย่อยของcontainerViewและสร้างภาพเคลื่อนไหวไปfromViewControllerทางขวาเมื่อคุณเคลื่อนไหว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)
})

นี่คือส่วนสำคัญของไฟล์ swift ทั้งหมด:

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


ที่ดี !. สิ่งที่ฉันต้องการจะทำคือเพียงแค่เคลื่อนไหวในทิศทางตรงกันข้าม ฉันตรวจสอบโซลูชันอื่น ๆ แต่มีการจัดแสดงทั้งหมดกะพริบที่หน้าจอซ้ายและขวา ดูเหมือนว่าภาพเคลื่อนไหวการเปลี่ยนแปลงอัลฟาโดยนัยไม่สามารถลบออกได้ วิธีแก้ปัญหาเฉพาะนี้แก้ไขปัญหาได้
beshio

ใช่นี่เป็นทางออกที่ถูกต้องเท่านั้น (ฉันไม่รังเกียจ แต่ก็เหมือนกับวิธีที่ฉันพิมพ์ด้านล่าง! :))
Fattie

@AlanZeino จะเกิดอะไรขึ้นถ้าภายใน ViewController เดียวกันคุณต้องมีแอนิเมชั่นต่าง ๆ สำหรับการคลิกปุ่มต่าง ๆ ? ดังนั้นสำหรับ button1 คุณต้องการแอนิเมชั่นการละลายสำหรับ button2 คุณต้องการการเปลี่ยนค่าเริ่มต้น
jzeferino

7

มี UINavigationControllerDelegate และ UIViewControllerAnimatedTransition มีคุณสามารถเปลี่ยนภาพเคลื่อนไหวสำหรับสิ่งที่คุณต้องการ

ตัวอย่างเช่นนี่คือแอนิเมชั่นป๊อปแนวตั้งสำหรับ VC:

@objc class PopAnimator: NSObject, UIViewControllerAnimatedTransitioning {

func transitionDuration(transitionContext: UIViewControllerContextTransitioning?) -> NSTimeInterval {
    return 0.5
}

func animateTransition(transitionContext: UIViewControllerContextTransitioning) {

    let fromViewController = transitionContext.viewControllerForKey(UITransitionContextFromViewControllerKey)!
    let toViewController = transitionContext.viewControllerForKey(UITransitionContextToViewControllerKey)!
    let containerView = transitionContext.containerView()
    let bounds = UIScreen.mainScreen().bounds
    containerView!.insertSubview(toViewController.view, belowSubview: fromViewController.view)
    toViewController.view.alpha = 0.5

    let finalFrameForVC = fromViewController.view.frame

    UIView.animateWithDuration(transitionDuration(transitionContext), animations: {
        fromViewController.view.frame = CGRectOffset(finalFrameForVC, 0, bounds.height)
        toViewController.view.alpha = 1.0
        }, completion: {
            finished in
            transitionContext.completeTransition(!transitionContext.transitionWasCancelled())
    })
}

}

และจากนั้น

func navigationController(navigationController: UINavigationController, animationControllerForOperation operation: UINavigationControllerOperation, fromViewController fromVC: UIViewController, toViewController toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? {
    if operation == .Pop {
        return PopAnimator()
    }
    return nil;
}

บทแนะนำที่เป็นประโยชน์ https://www.objc.io/issues/5-ios7/view-controller-transitions/


6

อ้างอิงจากคำตอบที่ได้ รับการอัปเดตสำหรับ swift 4jordanperry

สำหรับการผลัก UIViewController

let yourVC = self.storyboard?.instantiateViewController(withIdentifier: "yourViewController") as! yourViewController
    UIView.animate(withDuration: 0.75, animations: {() -> Void in
    UIView.setAnimationCurve(.easeInOut)
    self.navigationController?.pushViewController(terms, animated: true)
    UIView.setAnimationTransition(.flipFromRight, for: (self.navigationController?.view)!, cache: false)
})

สำหรับป๊อป

UIView.animate(withDuration: 0.75, animations: {() -> Void in
    UIView.setAnimationCurve(.easeInOut)
    UIView.setAnimationTransition(.flipFromLeft, for: (self.navigationController?.view)!, cache: false)
})
navigationController?.popViewController(animated: false)

5

นี่คือวิธีที่ฉันทำแบบเดียวกันใน Swift:

สำหรับการผลักดัน:

    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)

5

คำตอบของ @Luca Davanzo ในSwift 4.2

public extension UINavigationController {

    /**
     Pop current view controller to previous view controller.

     - parameter type:     transition animation type.
     - parameter duration: transition animation duration.
     */
    func pop(transitionType type: CATransitionType = .fade, duration: CFTimeInterval = 0.3) {
        self.addTransition(transitionType: type, duration: duration)
        self.popViewController(animated: false)
    }

    /**
     Push a new view controller on the view controllers's stack.

     - parameter vc:       view controller to push.
     - parameter type:     transition animation type.
     - parameter duration: transition animation duration.
     */
    func push(viewController vc: UIViewController, transitionType type: CATransitionType = .fade, duration: CFTimeInterval = 0.3) {
        self.addTransition(transitionType: type, duration: duration)
        self.pushViewController(vc, animated: false)
    }

    private func addTransition(transitionType type: CATransitionType = .fade, duration: CFTimeInterval = 0.3) {
        let transition = CATransition()
        transition.duration = duration
        transition.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeInEaseOut)
        transition.type = type
        self.view.layer.add(transition, forKey: nil)
    }

}

4

ฉันเพิ่งพยายามทำสิ่งที่คล้ายกัน ฉันตัดสินใจว่าฉันไม่ชอบภาพเคลื่อนไหวแบบเลื่อนของ UINavigationController แต่ฉันก็ไม่ต้องการที่จะทำแอนิเมชั่นที่ UIView มอบให้คุณเหมือนขดตัวหรืออะไรทำนองนั้น ฉันต้องการข้ามสีจางระหว่างมุมมองเมื่อฉันกดหรือป๊อป

ปัญหาที่เกี่ยวข้องกับความจริงที่ว่ามุมมองที่แท้จริงคือการลบมุมมองหรือ popping หนึ่งที่ด้านบนของมุมมองปัจจุบันดังนั้นจางหายไปไม่ทำงาน วิธีแก้ไขที่ฉันเข้ามาเกี่ยวข้องกับการรับมุมมองใหม่ของฉันและเพิ่มเป็นมุมมองย่อยไปยังมุมมองด้านบนปัจจุบันในสแต็กของ UIViewController ฉันจะเพิ่มมันด้วยอัลฟาของ 0 จากนั้นทำ crossfade เมื่อลำดับแอนิเมชันเสร็จสิ้นฉันดันมุมมองไปที่สแต็กโดยไม่ทำให้เคลื่อนไหว จากนั้นฉันกลับไปที่ topView แบบเก่าและล้างข้อมูลที่ฉันเปลี่ยนไป

มันซับซ้อนกว่านั้นเล็กน้อยเนื่องจากคุณมีระบบนำทางคุณต้องปรับเพื่อให้การเปลี่ยนแปลงมีลักษณะที่ถูกต้อง นอกจากนี้หากคุณหมุนภาพใด ๆ คุณต้องปรับขนาดเฟรมเมื่อคุณเพิ่มมุมมองเป็นภาพย่อยเพื่อให้แสดงอย่างถูกต้องบนหน้าจอ นี่คือรหัสบางส่วนที่ฉันใช้ ฉันแบ่งคลาส UINavigationController และลบล้างการพุชและเมธอด pop

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

 }

อย่างที่ฉันพูดถึงในรหัสฉันมีรายการปุ่มปุ่มซ้ายอยู่เสมอดังนั้นฉันจึงไม่ตรวจสอบเพื่อดูว่ามันเป็นศูนย์หรือไม่ก่อนที่จะใส่ลงในอาร์เรย์ที่ฉันผ่านเป็นบริบทสำหรับตัวแทนแอนิเมชั่น หากคุณทำเช่นนี้คุณอาจต้องการตรวจสอบนั้น

ปัญหาที่ฉันพบคือถ้าคุณผิดพลาดในวิธีการมอบหมายมันจะไม่ผิดพลาดของโปรแกรม มันแค่หยุดการมอบหมายให้เสร็จ แต่คุณไม่ได้รับคำเตือนใด ๆ
ดังนั้นเมื่อฉันทำการล้างข้อมูลในรูทีนตัวแทนนั้นมันทำให้เกิดพฤติกรรมการมองเห็นที่แปลกประหลาดเพราะมันไม่เสร็จสิ้นการล้างข้อมูล

ปุ่มย้อนกลับที่ฉันสร้างเรียกวิธีการ "goBack" และวิธีการที่เรียกเพียงแค่รูทีนป๊อป

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


 }
}

นั่นมันสวยมาก คุณจะต้องมีรหัสเพิ่มเติมถ้าคุณต้องการใช้การหมุน คุณจะต้องกำหนดขนาดเฟรมของมุมมองที่คุณเพิ่มเป็นมุมมองย่อยก่อนที่จะแสดงมิฉะนั้นคุณจะพบปัญหาเกี่ยวกับการวางแนวคือแนวนอน แต่ครั้งสุดท้ายที่คุณเห็นมุมมองก่อนหน้านี้เป็นแนวตั้ง ดังนั้นจากนั้นคุณเพิ่มมันเป็นมุมมองย่อยและจางหายไปใน แต่มันจะปรากฏเป็นแนวตั้งแล้วเมื่อเราป๊อปโดยไม่มีภาพเคลื่อนไหวมุมมองเดียวกัน แต่มุมมองที่อยู่ในกอง สิ่งทั้งหมดดูเล็กน้อยขี้ขลาด การใช้การหมุนของทุกคนแตกต่างกันเล็กน้อยดังนั้นฉันจึงไม่ได้รวมรหัสของฉันไว้ที่นี่

หวังว่ามันจะช่วยให้บางคน ฉันได้มองหาบางสิ่งเช่นนี้และไม่พบอะไรเลย ฉันไม่คิดว่านี่เป็นคำตอบที่สมบูรณ์แบบ แต่มันก็ใช้ได้ดีสำหรับฉันในตอนนี้


ในขณะที่น่าชื่นชมนี่ไม่ใช่วิธีแก้ปัญหาอย่างแท้จริงในตอนนี้ 7 ปีต่อมา!
Fattie

คุณพูดถูก คำตอบนี้มาจาก 2011 มันใช้งานได้แล้ว แต่สิ่งต่าง ๆ มีการเปลี่ยนแปลงมากมายตั้งแต่นั้นมา =)
georryan

4

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)
}

1
@Fattie วิธีการนี้โดยเฉพาะอย่างยิ่งงานได้เฉพาะกับใด ๆ ของภาพเคลื่อนไหวมาตรฐานเช่นพลิกและหยิกที่ระบุไว้ในdeveloper.apple.com/documentation/uikit/uiviewanimationoptions
ปีเตอร์ DeWeese

3

ใช้คำตอบ iJordan เป็นแรงบันดาลใจทำไมไม่เพียงแค่สร้างหมวดหมู่บน UINavigationController เพื่อใช้งานทั่วทั้งแอพของคุณแทนที่จะคัดลอก / วางรหัสภาพเคลื่อนไหวนี้ทั่วทุกแห่ง

UINavigationController + Animation.h

@interface UINavigationController (Animation)

- (void) pushViewControllerWithFlip:(UIViewController*) controller;

- (void) popViewControllerWithFlip;

@end

UINavigationController + Animation.m

@implementation UINavigationController (Animation)

- (void) pushViewControllerWithFlip:(UIViewController *) controller
{
    [UIView animateWithDuration:0.50
                     animations:^{
                         [UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
                         [self pushViewController:controller animated:NO];
                         [UIView setAnimationTransition:UIViewAnimationTransitionFlipFromRight forView:self.view cache:NO];
                     }];
}

- (void) popViewControllerWithFlip
{
    [UIView animateWithDuration:0.5
                     animations:^{
                         [UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
                         [UIView setAnimationTransition:UIViewAnimationTransitionFlipFromRight forView:self.view cache:NO];
                     }];

    [self popViewControllerAnimated:NO];
}

@end

จากนั้นเพียงนำเข้าไฟล์ UINavigationController + Animation.h และเรียกมันตามปกติ:

[self.navigationController pushViewControllerWithFlip:[[NewViewController alloc] init]];

[self.navigationController popViewControllerWithFlip];

ฉลาด. แต่ทำไมไม่เพิ่มวิธีพุช / ป๊อปซึ่งใช้อาร์กิวเมนต์ของ UIViewAnimationTransition แทนที่จะเป็น hardcode ใน flipFromRight
Jef

@Jef นี่คือวิธีการอำนวยความสะดวก - ด้วยวิธีนี้ผู้ดำเนินการไม่จำเป็นต้องจำว่า UIViewAnimationTransition ค่าใดที่จะส่งผ่านสำหรับแอนิเมชั่นแต่ละประเภทพวกเขาเพียงเรียกเมธอดด้วยชื่อ "english" ของสิ่งที่พวกเขาต้องการทำ
DiscDev

@Jef ยังข้อเสนอแนะของคุณถูกต้องแน่นอน - ถ้าฉันยังคงใช้วัตถุประสงค์ -c และจำเป็นต้องสนับสนุนรูปแบบการเปลี่ยนแปลงจำนวนมาก (ไม่แนะนำอย่างแน่นอนเนื่องจากรูปแบบการเปลี่ยนแปลงที่แตกต่างกันจะทำให้ผู้ใช้สับสน) ฉันจะมี 1 วิธีที่ใช้ประเภท UIViewAnimation วิธีการอำนวยความสะดวกหลายอย่างเพื่อให้การพัฒนาง่ายขึ้น
DiscDev

3

มันง่ายมาก

self.navigationController?.view.semanticContentAttribute = .forceRightToLeft

ยินดีต้อนรับสู่ StackOverflow: หากคุณโพสต์โค้ด XML หรือตัวอย่างข้อมูลโปรดเน้นบรรทัดเหล่านั้นในตัวแก้ไขข้อความและคลิกที่ปุ่ม "ตัวอย่างโค้ด" ({}) บนแถบเครื่องมือตัวแก้ไขหรือใช้ Ctrl + K บนแป้นพิมพ์เพื่อจัดรูปแบบ และไวยากรณ์เน้นมัน!
WhatsThePoint

2

ดูที่ADTransitionControllerลดลงแทน UINavigationController ด้วยภาพเคลื่อนไหวการเปลี่ยนแปลงที่กำหนดเอง (API ตรงกับ API ของ UINavigationController) ที่เราสร้างขึ้นที่ Applidium

คุณสามารถใช้ภาพเคลื่อนไหวที่กำหนดไว้ล่วงหน้าที่แตกต่างกันสำหรับการกระทำแบบพุชและป๊อปเช่นSwipe , Fade , Cube , Carrousel , Zoomและอื่น ๆ


2

ในขณะที่คำตอบทั้งหมดที่นี่ยอดเยี่ยมและทำงานได้ดีที่สุด แต่ก็มีวิธีที่ง่ายกว่าเล็กน้อยซึ่งได้ผลเหมือนกัน ...

สำหรับการผลักดัน:

  NextViewController *nextViewController = [[NextViewController alloc] init];

  // Shift the view to take the status bar into account 
  CGRect frame = nextViewController.view.frame;
  frame.origin.y -= 20;
  frame.size.height += 20;
  nextViewController.view.frame = frame;

  [UIView transitionFromView:self.navigationController.topViewController.view toView:nextViewController.view duration:0.5 options:UIViewAnimationOptionTransitionFlipFromRight completion:^(BOOL finished) {
    [self.navigationController pushViewController:nextViewController animated:NO];
  }];

สำหรับป๊อป:

  int numViewControllers = self.navigationController.viewControllers.count;
  UIView *nextView = [[self.navigationController.viewControllers objectAtIndex:numViewControllers - 2] view];

  [UIView transitionFromView:self.navigationController.topViewController.view toView:nextView duration:0.5 options:UIViewAnimationOptionTransitionFlipFromLeft completion:^(BOOL finished) {
    [self.navigationController popViewControllerAnimated:NO];
  }];}

สิ่งนี้จะล้มเหลวเมื่อป๊อปอัปไปยังตัวควบคุมมุมมองรูท
Abdullah Umer

1

ฉันไม่ทราบวิธีที่คุณสามารถเปลี่ยนภาพเคลื่อนไหวการเปลี่ยนแปลงต่อสาธารณะ

หากไม่จำเป็นต้องใช้ปุ่ม "ย้อนกลับ" คุณควรใช้ตัวควบคุมมุมมองแบบโมดัลเพื่อเปลี่ยน "การเลื่อนจากด้านล่าง" / "พลิก" / "จาง" / (≥3.2) "หน้าม้วน"


ในด้านส่วนตัวเมธอดจะ-pushViewController:animated:เรียกเมธอดที่ไม่มีเอกสาร-pushViewController:transition:forceImmediate:ดังนั้นเช่นหากคุณต้องการการเปลี่ยนแบบพลิกจากซ้ายไปขวาคุณสามารถใช้

[navCtrler pushViewController:ctrler transition:10 forceImmediate:NO];

อย่างไรก็ตามคุณไม่สามารถเปลี่ยนการเปลี่ยนแปลง "ป๊อป" ได้ด้วยวิธีนี้


1

ดูคำตอบของฉันสำหรับคำถามนี้เพื่อหาวิธีที่จะทำได้โดยใช้โค้ดน้อยกว่ามาก วิธีนี้ช่วยให้คุณสามารถสร้างภาพเคลื่อนไหว - "พุช" ของคอนโทรลเลอร์มุมมองใหม่ในแบบที่คุณต้องการและเมื่อภาพเคลื่อนไหวเสร็จสิ้นจะทำการตั้งค่าคอนโทรลเลอร์นำทางเช่นเดียวกับที่คุณใช้วิธีพุชแบบมาตรฐาน ตัวอย่างของฉันให้คุณเคลื่อนไหวแบบสไลด์อินจากซ้ายหรือขวา รหัสซ้ำที่นี่เพื่อความสะดวก:

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

0

จากแอปตัวอย่างตรวจสอบชุดรูปแบบนี้ 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;
}

0

เพียงใช้:

ViewController *viewController = [[ViewController alloc] init];

UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:viewController];
navController.navigationBarHidden = YES;

[self presentViewController:navController animated:YES completion: nil];
[viewController release];
[navController release];

0

การตระหนักว่านี่เป็นคำถามเก่า ฉันยังต้องการโพสต์คำตอบนี้เนื่องจากฉันมีปัญหาบางอย่างเกิดขึ้นviewControllersกับคำตอบที่เสนอ ทางออกของฉันคือ subclass UINavigationControllerและแทนที่วิธี pop และ push ทั้งหมด

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

0

ฉันรู้ว่าหัวข้อนี้เก่า แต่ฉันคิดว่าฉันจะใส่สองเซ็นต์ของฉัน คุณไม่จำเป็นต้องทำแอนิเมชั่นที่กำหนดเองมันมีวิธีทำอย่างง่าย ๆ แทนที่จะใช้การผลักดันให้สร้างตัวควบคุมการนำทางใหม่สร้างตัวควบคุมมุมมองใหม่เป็นตัวควบคุมรูทมุมมองของตัวควบคุม nav นั้นแล้วนำเสนอตัวควบคุมทิศทางจากตัวควบคุมทิศทางเดิม ปัจจุบันสามารถปรับแต่งได้อย่างง่ายดายด้วยหลายสไตล์และไม่จำเป็นต้องสร้างภาพเคลื่อนไหว

ตัวอย่างเช่น:

UIViewcontroller viewControllerYouWantToPush = UIViewController()
UINavigationController newNavController = UINavigationController(root: viewControllerYouWantToView)
newNavController.navBarHidden = YES;
self.navigationController.present(newNavController)

และคุณสามารถเปลี่ยนสไตล์การนำเสนอได้ตามต้องการ


-1

ฉันพบวิธีแบบเรียกซ้ำอย่างอ่อนโยนเพื่อทำสิ่งนี้ได้ผลสำหรับวัตถุประสงค์ของฉัน ฉันมีตัวแปร BOOL อินสแตนซ์ที่ฉันใช้เพื่อบล็อกภาพเคลื่อนไหว popping ปกติและแทนที่ข้อความป๊อปที่ไม่ใช่ภาพเคลื่อนไหวของฉันเอง ตัวแปรถูกตั้งค่าเริ่มต้นเป็น NO เมื่อกดปุ่มย้อนกลับวิธีการมอบหมายจะตั้งค่าเป็น YES และส่งข้อความป๊อปที่ไม่ใช่ภาพเคลื่อนไหวใหม่ไปยังแถบนำทางดังนั้นจึงเรียกวิธีการมอบหมายซ้ำอีกครั้งคราวนี้ด้วยตัวแปรที่ตั้งค่าเป็น YES ด้วยการตั้งค่าตัวแปรเป็น YES วิธีการมอบหมายจะตั้งค่าเป็นไม่ใช่และส่งคืน YES เพื่อให้ป๊อปที่ไม่มีภาพเคลื่อนไหวเกิดขึ้น หลังจากการโทรของผู้ร่วมประชุมคนที่สองกลับมาเราก็กลับมาเป็นคนแรกโดยที่ไม่มีการส่งคืนจะบล็อกป๊อปแบบภาพเคลื่อนไหวดั้งเดิม! จริง ๆ แล้วมันไม่ยุ่งอย่างที่มันฟัง เมธอด shouldPopItem ของฉันมีลักษณะเช่นนี้:

- (BOOL)navigationBar:(UINavigationBar *)navigationBar shouldPopItem:(UINavigationItem *)item 
{
    if ([[navigationBar items] indexOfObject:item] == 1) 
    {
        [expandedStack restack];    
    }

    if (!progPop) 
    {
        progPop = YES;
        [navBar popNavigationItemAnimated:NO];
        return NO;
    }
    else 
    {
        progPop = NO;
        return YES;
    }
}

ได้ผลสำหรับฉัน

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.