Cara mengubah animasi Push dan Pop di aplikasi berbasis navigasi

221

Saya memiliki aplikasi berbasis navigasi dan saya ingin mengubah animasi animasi push dan pop. Bagaimana saya melakukannya?

Edit 2018

Ada banyak jawaban untuk pertanyaan ini dan sudah cukup lama sekarang, saya telah memilih kembali jawaban untuk apa yang saya yakini sebagai yang paling relevan saat ini. Jika ada orang yang berpikir sebaliknya, beri tahu saya dalam komentar

Tusukan
sumber
25
Pada iOS 7, ada API resmi untuk ini; lihat dukungan animasi transisi kustom UINavigationControllerDelegate . Ada juga Video WWDC 2013 tentang ini.
Jesse Rusak
Saya telah menambahkan jawaban (di bawah) untuk melakukan ini di Swift - saya menemukan pertanyaan ini menanyakan tentang implementasi Swift jadi saya pikir saya akan berpadu dengan implementasi selanjutnya.
djbp
1
Untuk tutorial yang sangat baik dengan API resmi (iOS 7+), lihat: bradbambara.wordpress.com/2014/04/11/…
nikolovski
1
@JesseRusak memperbarui tautan ke Video WWDC 2013: developer.apple.com/videos/play/wwdc2013-218
Wojciech Rutkowski
1
Mengubah jawaban saya yang diterima teman dan teman. Semoga ini membantu! GLHF
Jab

Jawaban:

35

Bagaimana cara mengubah animasi Push dan Pop di aplikasi berbasis navigasi ...

Untuk 2019, "jawaban akhir!"

Pembukaan:

Katakanlah Anda baru dalam pengembangan iOS. Yang membingungkan, Apple menyediakan dua transisi yang dapat digunakan dengan mudah. Ini adalah: "crossfade" dan "flip".

Tapi tentu saja, "crossfade" dan "flip" tidak berguna. Mereka tidak pernah digunakan. Tidak ada yang tahu mengapa Apple menyediakan dua transisi yang tidak berguna itu!

Begitu:

Katakanlah Anda ingin melakukan transisi yang biasa, umum, seperti "slide". Dalam hal ini, Anda harus melakukan sejumlah besar pekerjaan! .

Pekerjaan itu, dijelaskan dalam posting ini.

Hanya mengulangi:

Anehnya: dengan iOS, jika Anda menginginkan transisi sehari-hari yang paling sederhana, paling umum, (seperti slide biasa), Anda harus melakukan semua pekerjaan menerapkan transisi kustom penuh .

Inilah cara melakukannya ...

1. Anda perlu kebiasaan UIViewControllerAnimatedTransitioning

  1. Anda membutuhkan bool seperti Anda sendiri popStyle. (Apakah muncul, atau muncul?)

  2. Anda harus memasukkan transitionDuration(sepele) dan panggilan utama,animateTransition

  3. Bahkan Anda harus menulis dua rutinitas yang berbeda untuk di dalamnya animateTransition. Satu untuk push, dan satu untuk pop. Mungkin nama mereka animatePushdan animatePop. Di dalam animateTransition, cabang saja popStyleke dua rutinitas

  4. Contoh di bawah ini melakukan langkah sederhana / pindah

  5. Dalam rutinitas animatePushdan Anda animatePop. Anda harus mendapatkan "dari tampilan" dan "untuk melihat". (Cara melakukannya, ditunjukkan dalam contoh kode.)

  6. dan Anda harus addSubview melihat "untuk" baru.

  7. dan Anda harus menelepon completeTransitiondi akhir anime Anda

Jadi ..

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

Lalu ...

2. Gunakan di pengontrol tampilan Anda.

Catatan: anehnya, Anda hanya perlu melakukan ini di pengontrol tampilan "pertama". (Yang "di bawah".)

Dengan yang Anda pop di atas , jangan lakukan apa pun . Mudah.

Jadi kelasmu ...

class SomeScreen: UIViewController {
}

menjadi...

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

Itu dia.

Dorong dan pop persis seperti biasa, tidak ada perubahan. Untuk mendorong ...

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

dan untuk membuatnya, Anda dapat melakukannya jika ingin melakukannya di layar berikutnya:

class NextScreen: TotallyOrdinaryUIViewController {
    
    @IBAction func userClickedBackOrDismissOrSomethingLikeThat() {
        
        navigationController?.popViewController(animated: true)
    }
}

Fiuh.


3. Juga nikmati jawaban lain di halaman ini yang menjelaskan cara mengganti AnimatedTransitioning

Gulir ke @AlanZeino dan @elias jawab untuk diskusi lebih lanjut tentang cara masuk AnimatedTransitioningdi aplikasi iOS hari ini!

Fattie
sumber
Luar biasa! Jika saya ingin navigasi swipe back gesture mendukung AnimatedTransitioning yang sama juga. Ada ide?
sam chi wen
terima kasih @samchiwen - memang itulah yang ada animatePushdan animatePop.. dua arah yang berbeda!
Fattie
268

Saya melakukan yang berikut dan berfungsi dengan baik .. dan sederhana dan mudah dimengerti ..

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

Dan hal yang sama untuk mendorong ..


Versi 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)
Magnus
sumber
34
+1, ini benar-benar solusi paling waras. Hanya catatan kecil untuk pengunjung masa depan: Animated:NObagian ini sangat penting. Jika YESdilewatkan, animasi akan bercampur dan menimbulkan efek lucu.
DarkDust
12
Solusi terbaik sejauh ini .. Dan untuk pemula, jangan lupa untuk menyertakan QuartCore (#import <QuartzCore / QuartzCore.h>)
nomann
4
Satu-satunya masalah yang saya miliki dengan solusi ini yang mendorong viewDidAppear viewcontroller dipanggil segera setelah saya mendorongnya tanpa animasi. Apakah ada jalan lain?
Pedro Mancheno
9
Masalah saya dengan kode ini adalah setiap tampilan tampak berkedip ke abu-abu atau putih saat slide masuk atau keluar.
Chris
1
diperiksa di iOS 7.1.2 dan iOS 8.3 - kode ini berfungsi dengan baik juga berfungsi dengan baik untuk metodesetViewControllers:
proff
256

Beginilah cara saya selalu berhasil menyelesaikan tugas ini.

Untuk Push:

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

Untuk Pop:

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


Saya masih mendapatkan banyak umpan balik dari ini jadi saya akan melanjutkan dan memperbaruinya untuk menggunakan blok animasi yang merupakan cara yang disarankan Apple untuk melakukan animasi.

Untuk Push:

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

Untuk Pop:

[UIView animateWithDuration:0.75
                         animations:^{
                             [UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
                             [UIView setAnimationTransition:UIViewAnimationTransitionFlipFromLeft forView:self.navigationController.view cache:NO];
                         }];
[self.navigationController popViewControllerAnimated:NO];
jordanperry
sumber
3
Terima kasih untuk ini. Tetapi pop dilakukan secara otomatis oleh UINavigationController. Bagaimana Anda mengesampingkan perilaku itu sehingga Anda dapat memanggil logika pop kustom Anda?
Joshua Frank
1
@stuckj sebenarnya tidak berfungsi !!! Anda hanya perlu mengganti superdenganself.navigationController
holierthanthou84
Apakah ada cara untuk mendapatkan slide dari kiri daripada slide default dari kanan?
shim
Yang pertama tidak menunjukkan tampilan baru sama sekali. Yang kedua tidak menunjukkan animasi. Jawaban yang sangat buruk! iOS 7.
Dmitry
2
mengapa Anda memberi UIViewControllersubclass nama tanpa bagian "ViewController". Nama ini lebih cocok untuk UIView.
user2159978
29

untuk mendorong

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

untuk pop

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];
Ted
sumber
19

@Magnus menjawab, hanya untuk 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)

Beberapa sidenotes:

Anda dapat melakukan ini juga dengan Segue, cukup laksanakan ini di prepareForSegueatau shouldPerformSegueWithIdentifier. Namun , ini juga akan menyimpan animasi default di dalamnya. Untuk memperbaiki ini, Anda harus pergi ke storyboard, klik Segue, dan hapus centang pada kotak 'Animates'. Tapi ini akan membatasi aplikasi Anda untuk iOS 9.0 dan lebih tinggi (minimal ketika saya melakukannya di Xcode 7).

Saat melakukan dalam segue, dua baris terakhir harus diganti dengan:

self.navigationController?.popViewControllerAnimated(false)

Meskipun saya salah, itu agak mengabaikannya.

CularBytes
sumber
Cara menghapus warna hitam di latar belakang di akhir animasi.
Madhu
Tidak berfungsi untuk animasi pengontrol tampilan push bekerja untuk pengontrol tampilan Pop
Mukul More
16

Ingatlah bahwa di Swift , ekstensi pasti adalah teman Anda!

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

}
Luca Davanzo
sumber
11

Menggunakan panggilan pribadi adalah ide yang buruk karena Apple tidak lagi menyetujui aplikasi yang melakukan itu. Mungkin Anda bisa mencoba ini:

//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];
nicktmro
sumber
Ini hanya "setengah" berfungsi - tidak akan menyelesaikan masalah animasi pop yang lebih sulit.
Adam
Saya suka solusi ini lebih baik, dan ya itu berhasil. Menggunakan metode pribadi akan membuat Anda ditolak dengan pasti.
Benjamin Intal
@nicktmro yang merupakan panggilan api pribadi. Saya tidak memperhatikan.
Franklin
@ Franklin ada diskusi di sini beberapa waktu lalu sekitar menggunakan -pushViewController:transition:forceImmediate:yang akan menjadi ide yang buruk.
nicktmro
9

Karena ini adalah hasil teratas di Google, saya pikir saya akan membagikan apa yang menurut saya cara paling waras; yang menggunakan API transisi iOS 7+. Saya menerapkan ini untuk iOS 10 dengan Swift 3.

Sangat mudah untuk menggabungkan ini dengan bagaimana UINavigationControllermenjiwai antara dua pengontrol tampilan jika Anda membuat subkelas UINavigationControllerdan mengembalikan turunan kelas yang sesuai dengan UIViewControllerAnimatedTransitioningprotokol.

Sebagai contoh di sini adalah UINavigationControllersubclass saya :

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

}

Anda dapat melihat bahwa saya mengaturnya UINavigationControllerDelegatesendiri, dan dalam ekstensi pada subkelas saya, saya menerapkan metode UINavigationControllerDelegateyang memungkinkan Anda mengembalikan pengontrol animasi khusus (yaitu, NavigationControllerAnimation). Pengontrol animasi khusus ini akan menggantikan animasi stok untuk Anda.

Anda mungkin bertanya-tanya mengapa saya melewatkan operasi ke NavigationControllerAnimationinstance melalui inisialisasi. Saya melakukan ini sehingga dalam NavigationControllerAnimationimplementasi UIViewControllerAnimatedTransitioningprotokol saya tahu apa operasi itu (yaitu, 'push' atau 'pop'). Ini membantu untuk mengetahui jenis animasi apa yang harus saya lakukan. Sebagian besar waktu, Anda ingin melakukan animasi yang berbeda tergantung pada operasinya.

Sisanya cukup standar. Terapkan dua fungsi yang diperlukan dalam UIViewControllerAnimatedTransitioningprotokol dan hidupkan sesuka Anda:

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

Penting untuk diingat, bahwa untuk setiap jenis operasi yang berbeda (yaitu, 'push' atau 'pop), pengontrol dari dan ke tampilan akan berbeda. Ketika Anda berada dalam operasi push, controller untuk melihat akan menjadi yang didorong. Saat Anda berada dalam operasi pop, to view controller akan menjadi salah satu yang sedang ditransisikan ke, dan controller dari view akan menjadi yang muncul.

Juga, toview controller harus ditambahkan sebagai subview dari containerViewdalam konteks transisi.

Ketika animasi Anda selesai, Anda harus menelepon transitionContext.completeTransition(true) . Jika Anda melakukan transisi interaktif, Anda harus mengembalikan Boolke completeTransition(didComplete: Bool), secara dinamis , tergantung pada apakah transisi selesai pada akhir animasi.

Akhirnya ( bacaan opsional ), Anda mungkin ingin melihat bagaimana saya melakukan transisi yang sedang saya kerjakan. Kode ini sedikit lebih peretasan dan saya menulisnya dengan cepat sehingga saya tidak akan mengatakan itu kode animasi yang bagus tetapi masih menunjukkan bagaimana melakukan bagian animasi.

Milik saya adalah transisi yang sangat sederhana; Saya ingin meniru animasi yang sama dengan yang biasanya dilakukan UINavigationController, tetapi alih-alih animasi 'halaman berikutnya di atas', saya ingin menerapkan animasi 1: 1 dari pengontrol tampilan lama pada saat yang sama dengan tampilan baru pengontrol muncul. Ini memiliki efek membuat dua pengendali tampilan tampak seolah-olah mereka disematkan satu sama lain.

Untuk operasi push, yang pertama-tama mengharuskan pengaturan toViewControllertampilan asal pada layar x-axis off, menambahkannya sebagai subview dari containerView, menggerakkannya ke layar dengan mengatur origin.xke nol. Pada saat yang sama, saya menghidupkan fromViewControllertampilan itu dengan origin.xmematikannya:

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

Operasi pop pada dasarnya adalah kebalikannya. Tambahkan toViewControllersebagai subview dari containerView, dan hidupkan fromViewControllerke kanan saat Anda menghidupkan toViewControllerdari kiri:

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

Inilah inti dengan seluruh file cepat:

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

Alan Zeino
sumber
Bagus!. Yang ingin saya lakukan adalah HANYA untuk menghidupkan di arah yang berlawanan. Saya memeriksa beberapa solusi lain, tetapi semua pameran berkedip di layar kiri dan kanan. Sepertinya animasi perubahan alpha implisit tidak dapat dihapus dengan mereka. Hanya solusi ini yang memperbaiki masalah ini.
beshio
ya, ini adalah satu-satunya solusi modern yang benar. (Saya tidak keberatan, tapi persis sama dengan solusi yang saya ketikkan di bawah ini :) :))
Fattie
@AlanZeino Bagaimana jika di dalam ViewController yang sama Anda harus memiliki Animasi diferente untuk klik tombol yang berbeda? Jadi, untuk button1 Anda memerlukan animasi yang larut, untuk button2 Anda memerlukan transisi default.
jzeferino
7

Ada UINavigationControllerDelegate dan UIViewControllerAnimatedTransitioning di sana Anda dapat mengubah animasi untuk apa pun yang Anda inginkan.

Misalnya ini adalah animasi pop vertikal untuk 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())
    })
}

}

Lalu

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

Tutorial yang berguna https://www.objc.io/issues/5-ios7/view-controller-transitions/

Eilas
sumber
6

Berdasarkan jawaban yang diperbarui untuk swift 4jordanperry

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

Untuk Pop

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

Inilah cara saya melakukan hal yang sama di Swift:

Untuk Push:

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

Untuk Pop:

Saya sebenarnya melakukan ini sedikit berbeda dengan beberapa tanggapan di atas - tetapi karena saya baru dalam pengembangan Swift, itu mungkin tidak benar. Saya telah mengganti viewWillDisappear:animated:dan menambahkan kode pop di sana:

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

    super.viewWillDisappear(animated)
djbp
sumber
5

@Luca Davanzo jawaban di 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)
    }

}
Serj Rubens
sumber
4

Baru-baru ini saya mencoba melakukan hal serupa. Saya memutuskan saya tidak suka animasi geser dari UINavigationController, tetapi saya juga tidak ingin melakukan animasi yang UIView berikan kepada Anda seperti ikal atau semacamnya. Saya ingin melakukan cross fade antara pandangan ketika saya mendorong atau pop.

Masalah di sana melibatkan fakta bahwa tampilan secara harfiah menghapus tampilan atau muncul satu di atas yang sekarang, sehingga fade tidak berfungsi. Solusi yang saya datangi dengan melibatkan mengambil tampilan baru saya dan menambahkannya sebagai subview ke tampilan teratas saat ini pada tumpukan UIViewController's. Saya menambahkannya dengan alpha 0, lalu melakukan crossfade. Ketika urutan animasi selesai, saya mendorong tampilan ke tumpukan tanpa menggerakkannya. Saya kemudian kembali ke topView lama dan membersihkan hal-hal yang telah saya ubah.

Ini sedikit lebih rumit dari itu, karena Anda memiliki item navigasi Anda harus menyesuaikan untuk membuat transisi terlihat benar. Juga, jika Anda melakukan rotasi apa pun, Anda kemudian harus menyesuaikan ukuran bingkai saat Anda menambahkan tampilan sebagai subview sehingga mereka muncul dengan benar di layar. Ini beberapa kode yang saya gunakan. Saya subclassed UINavigationController dan mengalahkan metode push dan 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];

 }

Seperti yang saya sebutkan dalam kode, saya selalu memiliki item tombol bilah kiri, jadi saya tidak memeriksa untuk melihat apakah nil sebelum memasukkannya ke dalam array yang saya berikan sebagai konteks untuk delegasi animasi. Jika Anda melakukan ini, Anda mungkin ingin melakukan pemeriksaan itu.

Masalah yang saya temukan adalah bahwa jika Anda crash sama sekali dalam metode delegasi, itu tidak akan crash program. Itu hanya menghentikan delegasi dari menyelesaikan tetapi Anda tidak mendapatkan peringatan apa pun.
Jadi karena saya melakukan pembersihan dalam rutinitas delegasi itu, itu menyebabkan beberapa perilaku visual yang aneh karena tidak menyelesaikan pembersihan.

Tombol kembali yang saya buat menyebut metode "goBack", dan metode itu hanya memanggil pop rutin.

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

Juga, inilah rutinitas pop saya.

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


 }
}

Cukup banyak. Anda akan memerlukan beberapa kode tambahan jika Anda ingin menerapkan rotasi. Anda harus mengatur ukuran bingkai dari pandangan Anda yang Anda tambahkan sebagai subview sebelum Anda tunjukkan jika tidak Anda akan mengalami masalah orientasi adalah lansekap, tetapi terakhir kali Anda melihat tampilan sebelumnya itu potret. Jadi, kemudian Anda menambahkannya sebagai tampilan sub dan memudar di dalamnya tetapi muncul sebagai potret, lalu ketika kami muncul tanpa animasi, tampilan yang sama, tetapi yang ada di tumpukan, sekarang adalah lanskap. Semuanya terlihat sedikit funky. Implementasi rotasi setiap orang sedikit berbeda jadi saya tidak memasukkan kode saya untuk itu di sini.

Semoga ini bisa membantu beberapa orang. Saya sudah mencari-cari sesuatu seperti ini dan tidak dapat menemukan apa pun. Saya tidak berpikir ini adalah jawaban yang sempurna, tetapi ini bekerja sangat baik untuk saya pada saat ini.

georryan
sumber
Sementara mengagumkan, ini jujur ​​bukan solusinya sekarang 7 tahun kemudian!
Fattie
Kamu benar. Jawaban ini berasal dari tahun 2011. Saat itu berhasil, tetapi banyak hal telah berubah sejak saat itu. =)
georryan
4

Anda sekarang dapat menggunakan UIView.transition. Catat itu animated:false. Ini berfungsi dengan semua opsi transisi, pop, push, atau stack replace.

if let nav = self.navigationController
{
    UIView.transition(with:nav.view, duration:0.3, options:.transitionCrossDissolve, animations: {
        _ = nav.popViewController(animated:false)
    }, completion:nil)
}
Peter DeWeese
sumber
1
@Fattie, metode khusus ini hanya berfungsi dengan animasi standar seperti flips dan curls yang terdaftar di developer.apple.com/documentation/uikit/uiviewanimationoptions
Peter DeWeese
3

Menggunakan jawaban iJordan sebagai inspirasi, mengapa tidak sekadar membuat Kategori di UINavigationController untuk digunakan di seluruh aplikasi Anda alih-alih menyalin / menempelkan kode animasi ini di semua tempat?

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

Kemudian cukup impor file UINavigationController + Animation.h dan panggil secara normal:

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

[self.navigationController popViewControllerWithFlip];
DiscDev
sumber
Pintar. Tetapi mengapa tidak menambahkan metode push / pop yang mengambil argumen UIViewAnimationTransition daripada hardcode ke flipFromRight?
Jef
@Jef ini adalah metode kenyamanan - dengan cara ini, implementor tidak perlu mengingat nilai UIViewAnimationTransition mana yang harus dilewati untuk setiap jenis animasi tertentu, mereka hanya memanggil metode dengan nama "bahasa Inggris" dari apa yang ingin mereka capai.
DiscDev
@Jef juga, saran Anda pasti valid - jika saya masih menggunakan objektif-c dan diperlukan untuk mendukung banyak gaya transisi (pasti tidak disarankan karena banyak gaya transisi yang berbeda akan membingungkan pengguna) Saya akan memiliki 1 metode yang menggunakan jenis UIViewAnimationTransition, kemudian beberapa metode kenyamanan untuk mempermudah pengembangan.
DiscDev
3

Ini sangat sederhana

self.navigationController?.view.semanticContentAttribute = .forceRightToLeft
pengguna2501116
sumber
Selamat datang di StackOverflow: jika Anda memposting kode, XML, atau sampel data, sorot baris-baris itu di editor teks dan klik tombol "kode sampel" ({}) pada bilah alat editor atau gunakan Ctrl + K pada keyboard Anda untuk memformat dengan baik dan sintaks sorotnya!
WhatsThePoint
2

Lihat ADTransitionController , setetes pengganti untuk UINavigationController dengan animasi transisi khusus (API-nya cocok dengan API UINavigationController) yang kami buat di Applidium.

Anda dapat menggunakan berbagai animasi yang telah ditentukan sebelumnya untuk aksi push dan pop seperti Swipe , Fade , Cube , Carrousel , Zoom dan sebagainya.

felginep
sumber
2

Walaupun semua jawaban di sini bagus dan sebagian besar berfungsi dengan sangat baik, ada metode yang sedikit lebih sederhana yang mencapai efek yang sama ...

Untuk Push:

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

Untuk Pop:

  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];
  }];}
kepala dalam kode
sumber
Ini akan macet saat poping ke root view controller.
Abdullah Umer
1

Saya tidak mengetahui cara apa pun untuk mengubah animasi transisi secara publik.

Jika tombol "kembali" tidak diperlukan, Anda harus menggunakan pengontrol tampilan modal untuk mendapatkan transisi "push from bottom" / "flip" / "fade" / (≥3.2) "page curl".


Di sisi pribadi , metode ini -pushViewController:animated:memanggil metode tidak berdokumen -pushViewController:transition:forceImmediate:, jadi mis. Jika Anda menginginkan transisi flip-dari-kiri-ke-kanan, Anda dapat menggunakan

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

Anda tidak dapat mengubah transisi "pop" dengan cara ini.

kennytm
sumber
1

Lihat jawaban saya untuk pertanyaan ini untuk cara melakukannya dalam baris kode yang jauh lebih sedikit. Metode ini memungkinkan Anda untuk menghidupkan "Push" pseudo- dari pengontrol tampilan baru dengan cara apa pun yang Anda suka, dan ketika animasi selesai ia mengatur Pengontrol Navigasi seperti jika Anda telah menggunakan metode Push standar. Contoh saya memungkinkan Anda menggerakkan slide-in dari kiri atau dari kanan. Kode diulangi di sini untuk kenyamanan:

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

Dari aplikasi sampel, periksa variasi ini. 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;
}
johndpope
sumber
0

Cukup gunakan:

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];
Sergio Andreotti
sumber
0

Menyadari ini adalah pertanyaan lama. Saya masih ingin memposting jawaban ini, karena saya memiliki beberapa masalah muncul viewControllersdengan jawaban yang diajukan. Solusi saya adalah dengan subkelas UINavigationControllerdan mengganti semua metode pop dan 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
Christian Otkjær
sumber
0

Saya tahu utas ini sudah tua, tetapi saya pikir saya akan memasukkan dua sen saya. Anda tidak perlu membuat animasi khusus, ada cara sederhana (mungkin gila) untuk melakukannya. Alih-alih menggunakan push, buat pengontrol navigasi baru, buat pengontrol tampilan baru pengontrol tampilan akar pengontrol nav itu, dan kemudian sajikan pengontrol nav dari pengontrol nav asli. Present mudah disesuaikan dengan banyak gaya, dan tidak perlu membuat animasi kustom.

Sebagai contoh:

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

Dan Anda dapat mengubah gaya presentasi sesuai keinginan Anda.

Ethan Zhao
sumber
-1

Saya menemukan cara rekursif ringan untuk melakukan ini yang berfungsi untuk tujuan saya. Saya memiliki variabel instan BOOL yang saya gunakan untuk memblokir animasi popping normal dan mengganti pesan pop non-animasi saya sendiri. Variabel awalnya diatur ke NO. Ketika tombol kembali ditekan, metode delegasi menetapkannya ke YES dan mengirim pesan pop non-animasi baru ke bilah navigasi, sehingga memanggil metode delegasi yang sama lagi, kali ini dengan variabel yang disetel ke YES. Dengan variabel disetel ke YA, metode delegasi menetapkannya ke TIDAK dan mengembalikan YA untuk memungkinkan pop non-animasi terjadi. Setelah panggilan delegasi kedua kembali, kami kembali ke yang pertama, di mana NO dikembalikan, memblokir pop animasi asli! Ini sebenarnya tidak berantakan seperti kedengarannya. Metode shouldPopItem saya terlihat seperti ini:

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

Bekerja untukku.

CharlieMezak
sumber