RootViewController Beralih Animasi Transisi

125

Apakah ada cara untuk memiliki efek Transisi / animasi sambil mengganti viewcontroller yang ada sebagai rootviewcontroller dengan yang baru di appDelegate?

Jefferson
sumber

Jawaban:

272

Anda dapat membungkus pengalihan rootViewControllerdalam blok animasi transisi:

[UIView transitionWithView:self.window
                  duration:0.5
                   options:UIViewAnimationOptionTransitionFlipFromLeft
                animations:^{ self.window.rootViewController = newViewController; }
                completion:nil];
Ole Begemann
sumber
5
hei Ole, saya mencoba pendekatan ini, itu bekerja sebagian, masalahnya adalah aplikasi saya hanya akan tetap dalam mode lansekap, tetapi dengan melakukan transisi rootviewcontroller, pengendali tampilan yang baru disajikan dimuat dalam potret di awal, dan dengan cepat memutar ke mode lansekap , bagaimana cara mengatasinya?
Chris Chen 8/11
4
Saya menjawab pertanyaan Chris Chen (semoga! Mungkin?) Dalam pertanyaannya yang terpisah di sini: stackoverflow.com/questions/8053832/…
Kalle
1
hei saya ingin transisi push dalam animasi yang sama dapatkah saya mencapai itu?
Pengguna 1531343
14
Saya perhatikan beberapa masalah dengan ini, yaitu elemen yang salah tempat / elemen yang dimuat dengan malas. Misalnya, jika Anda tidak memiliki bilah navigasi di root vc yang ada, maka hidupkan ke vc baru yang memiliki itu, animasi selesai DAN MAKA nav bar ditambahkan. Tampaknya agak konyol - ada pemikiran mengapa ini mungkin, dan apa yang bisa dilakukan?
anon_dev1234
1
Saya telah menemukan bahwa memanggil newViewController.view.layoutIfNeeded()sebelum blok animasi memperbaiki masalah dengan elemen-elemen malas.
Whoa
66

Saya menemukan ini dan bekerja dengan sempurna:

di appDelegate Anda:

- (void)changeRootViewController:(UIViewController*)viewController {

    if (!self.window.rootViewController) {
        self.window.rootViewController = viewController;
        return;
    }

    UIView *snapShot = [self.window snapshotViewAfterScreenUpdates:YES];

    [viewController.view addSubview:snapShot];

    self.window.rootViewController = viewController;

    [UIView animateWithDuration:0.5 animations:^{
        snapShot.layer.opacity = 0;
        snapShot.layer.transform = CATransform3DMakeScale(1.5, 1.5, 1.5);
    } completion:^(BOOL finished) {
        [snapShot removeFromSuperview];
    }];
}

di aplikasi Anda

 if (!app) { app = (AppDelegate *)[[UIApplication sharedApplication] delegate]; }
        [app changeRootViewController:newViewController];

kredit:

https://gist.github.com/gimenete/53704124583b5df3b407

Yesus
sumber
Apakah ini mendukung layar otomatis memutar?
Wingzero
1
Solusi ini bekerja lebih baik dalam kasus saya. Saat menggunakan transisiWithView pengontrol tampilan root baru ditata dengan benar sampai setelah transisi selesai. Pendekatan ini memungkinkan pengontrol tampilan root baru ditambahkan ke jendela, ditata dan kemudian ditransisikan.
Fostah
@ Winzero sedikit terlambat, tetapi, ini akan memungkinkan segala jenis transisi, baik melalui UIView.animations (katakanlah, CGAffineTransform dengan rotate) atau CAAnimation khusus.
Bisa
41

Saya memposting jawaban Yesus diimplementasikan dengan cepat. Dibutuhkan pengidentifikasi viewcontroller sebagai argumen, memuat dari storyboard yang diinginkanViewController dan mengubah rootViewController dengan animasi.

Pembaruan Swift 3.0:

  func changeRootViewController(with identifier:String!) {
    let storyboard = self.window?.rootViewController?.storyboard
    let desiredViewController = storyboard?.instantiateViewController(withIdentifier: identifier);

    let snapshot:UIView = (self.window?.snapshotView(afterScreenUpdates: true))!
    desiredViewController?.view.addSubview(snapshot);

    self.window?.rootViewController = desiredViewController;

    UIView.animate(withDuration: 0.3, animations: {() in
      snapshot.layer.opacity = 0;
      snapshot.layer.transform = CATransform3DMakeScale(1.5, 1.5, 1.5);
      }, completion: {
        (value: Bool) in
        snapshot.removeFromSuperview();
    });
  }

Pembaruan Swift 2.2:

  func changeRootViewControllerWithIdentifier(identifier:String!) {
    let storyboard = self.window?.rootViewController?.storyboard
    let desiredViewController = storyboard?.instantiateViewControllerWithIdentifier(identifier);

    let snapshot:UIView = (self.window?.snapshotViewAfterScreenUpdates(true))!
    desiredViewController?.view.addSubview(snapshot);

    self.window?.rootViewController = desiredViewController;

    UIView.animateWithDuration(0.3, animations: {() in
      snapshot.layer.opacity = 0;
      snapshot.layer.transform = CATransform3DMakeScale(1.5, 1.5, 1.5);
      }, completion: {
        (value: Bool) in
        snapshot.removeFromSuperview();
    });
  }

  class func sharedAppDelegate() -> AppDelegate? {
    return UIApplication.sharedApplication().delegate as? AppDelegate;
  }

Setelah itu, Anda memiliki penggunaan yang sangat sederhana dari mana saja:

let appDelegate = AppDelegate.sharedAppDelegate()
appDelegate?.changeRootViewControllerWithIdentifier("YourViewControllerID")

Pembaruan Swift 3.0

let appDelegate = UIApplication.shared.delegate as! AppDelegate
appDelegate.changeRootViewController(with: "listenViewController")
Neil Galiaskarov
sumber
25

Cepat 2

UIView.transitionWithView(self.window!, duration: 0.5, options: UIViewAnimationOptions.TransitionFlipFromLeft, animations: {
  self.window?.rootViewController = anyViewController
}, completion: nil)

Swift 3, 4, 5

UIView.transition(with: self.window!, duration: 0.5, options: UIView.AnimationOptions.transitionFlipFromLeft, animations: {
  self.window?.rootViewController = anyViewController
}, completion: nil)
Chandrashekhar HM
sumber
XCode memperbaiki kode saya seperti ini: `` `UIView.transition (dengan: self.view.window !, durasi: 0,5, opsi: UIViewAnimationOptions.transitionFlipFromTop, animasi: {appDelegate.window? .RootViewController = myViewController}, selesai: nil) `` `
scaryguy
10

coba saja ini. Bekerja dengan baik untuk saya.

BOOL oldState = [UIView areAnimationsEnabled];
[UIView setAnimationsEnabled:NO];
self.window.rootViewController = viewController;
[UIView transitionWithView:self.window duration:0.5 options:transition animations:^{
    //
} completion:^(BOOL finished) {
    [UIView setAnimationsEnabled:oldState];
}];

EDIT:

Yang ini lebih baik.

- (void)setRootViewController:(UIViewController *)viewController
               withTransition:(UIViewAnimationOptions)transition
                   completion:(void (^)(BOOL finished))completion {
    UIViewController *oldViewController = self.window.rootViewController;
    [UIView transitionFromView:oldViewController.view 
                        toView:viewController.view
                      duration:0.5f
                       options:(UIViewAnimationOptions)(transition|UIViewAnimationOptionAllowAnimatedContent|UIViewAnimationOptionLayoutSubviews)
                    completion:^(BOOL finished) {
        self.window.rootViewController = viewController;
        if (completion) {
            completion(finished);
        }
    }];
}
Dmitry Coolerov
sumber
Saya memiliki animasi default yang aneh ketika hanya beralih VC root. Versi pertama menyingkirkan itu untuk saya.
juhan_h
Versi kedua akan menghidupkan tata letak subview, seperti yang disebutkan oleh juhan_h. Jika ini tidak diperlukan, bereksperimen dengan menghapus UIViewAnimationOptionAllowAnimatedContent|UIViewAnimationOptionLayoutSubviews, atau menggunakan versi pertama, atau metode lain.
ftvs
3

Agar tidak memiliki masalah dengan flip transisi nanti di aplikasi ini baik untuk menghapus tampilan lama dari tumpukan juga

UIViewController *oldController=self.window.rootViewController;

[UIView transitionWithView:self.window
                  duration:0.5
                   options:UIViewAnimationOptionTransitionCrossDissolve
                animations:^{ self.window.rootViewController = nav; }
                completion:^(BOOL finished) {
                    if(oldController!=nil)
                        [oldController.view removeFromSuperview];
                }];
Catalin
sumber
2

Jawaban yang benar adalah Anda tidak perlu mengganti rootViewControllerdi jendela Anda. Alih-alih, buat custom UIViewController, tetapkan sekali dan biarkan menampilkan satu pengontrol anak sekaligus dan ganti dengan animasi jika diperlukan. Anda dapat menggunakan potongan kode berikut sebagai titik awal:

Swift 3.0

import Foundation
import UIKit

/// Displays a single child controller at a time.
/// Replaces the current child controller optionally with animation.
class FrameViewController: UIViewController {

    private(set) var displayedViewController: UIViewController?

    func display(_ viewController: UIViewController, animated: Bool = false) {

        addChildViewController(viewController)

        let oldViewController = displayedViewController

        view.addSubview(viewController.view)
        viewController.view.layoutIfNeeded()

        let finishDisplay: (Bool) -> Void = {
            [weak self] finished in
            if !finished { return }
            oldViewController?.view.removeFromSuperview()
            oldViewController?.removeFromParentViewController()
            viewController.didMove(toParentViewController: self)
        }

        if (animated) {
            viewController.view.alpha = 0
            UIView.animate(
                withDuration: 0.5,
                animations: { viewController.view.alpha = 1; oldViewController?.view.alpha = 0 },
                completion: finishDisplay
            )
        }
        else {
            finishDisplay(true)
        }

        displayedViewController = viewController
    }

    override var preferredStatusBarStyle: UIStatusBarStyle {
        return displayedViewController?.preferredStatusBarStyle ?? .default
    }
}

Dan cara Anda menggunakannya adalah:

...
let rootController = FrameViewController()
rootController.display(UINavigationController(rootViewController: MyController()))
window.rootViewController = rootController
window.makeKeyAndVisible()
...

Contoh di atas menunjukkan bahwa Anda dapat bersarang UINavigationControllerdi dalam FrameViewControllerdan berfungsi dengan baik. Pendekatan ini memberi Anda tingkat kustomisasi dan kontrol yang tinggi. Hubungi FrameViewController.display(_)saja kapan saja Anda ingin mengganti pengendali root di jendela Anda, dan itu akan melakukan pekerjaan itu untuk Anda.

Aleks N.
sumber
2

Ini adalah pembaruan untuk swift 3, metode ini harus ada di delegasi aplikasi Anda, dan Anda menyebutnya dari pengontrol tampilan apa pun, melalui instance bersama dari delegasi aplikasi

func logOutAnimation() {
    let storyBoard = UIStoryboard.init(name: "SignIn", bundle: nil)
    let viewController = storyBoard.instantiateViewController(withIdentifier: "signInVC")
    UIView.transition(with: self.window!, duration: 0.5, options: UIViewAnimationOptions.transitionFlipFromLeft, animations: {
        self.window?.rootViewController = viewController
        self.window?.makeKeyAndVisible()
    }, completion: nil)
}

Bagian yang hilang dari berbagai pertanyaan di atas, adalah

    self.window?.makeKeyAndVisible()

Semoga ini bisa membantu seseorang.

Giovanny Piñeros
sumber
1

di AppDelegate.h:

#define ApplicationDelegate ((AppDelegate *)[UIApplication sharedApplication].delegate)]

di Controller Anda:

[UIView transitionWithView:self.window
                  duration:0.5
                   options:UIViewAnimationOptionTransitionFlipFromLeft
                animations:^{
    ApplicationDelegate.window.rootViewController = newViewController;
    }
                completion:nil];
Bob
sumber
6
Ini sama dengan jawaban yang diterima, kecuali formatnya salah. Mengapa mengganggu?
jrturton
1
Yang ini tidak tergantung pada Anda berada di View atau ViewController. Perbedaan terbesar adalah lebih filosofis dalam hal seberapa tebal atau tipis Anda menyukai Tampilan dan ViewControllers Anda.
Max
0

Saya mengusulkan cara saya yang berfungsi dengan baik dalam proyek saya, dan itu menawarkan saya animasi yang baik. Saya telah menguji proposal lain yang ditemukan di pos ini, tetapi beberapa di antaranya tidak berfungsi seperti yang diharapkan.

- (void)transitionToViewController:(UIViewController *)viewController withTransition:(UIViewAnimationOptions)transition completion:(void (^)(BOOL finished))completion {
// Reset new RootViewController to be sure that it have not presented any controllers
[viewController dismissViewControllerAnimated:NO completion:nil];

[UIView transitionWithView:self.window
                  duration:0.5f
                   options:transition
                animations:^{
                    for (UIView *view in self.window.subviews) {
                        [view removeFromSuperview];
                    }
                    [self.window addSubview:viewController.view];

                    self.window.rootViewController = viewController;
                } completion:completion];
}
93sauu
sumber
0

Animasi manis yang bagus (diuji dengan Swift 4.x):

extension AppDelegate {
   public func present(viewController: UIViewController) {
        guard let window = window else { return }
        UIView.transition(with: window, duration: 0.5, options: .transitionFlipFromLeft, animations: {
            window.rootViewController = viewController
        }, completion: nil)
    }
}

Telepon dengan

guard let delegate = UIApplication.shared.delegate as? AppDelegate else { return }
delegate.present(viewController: UIViewController())
Cesare
sumber