viewWillDisappear: Menentukan apakah pengontrol tampilan sedang dimunculkan atau menampilkan pengontrol sub-tampilan

133

Saya berjuang untuk menemukan solusi yang baik untuk masalah ini. Dalam -viewWillDisappear:metode pengontrol tampilan , saya perlu menemukan cara untuk menentukan apakah itu karena pengontrol tampilan didorong ke tumpukan pengontrol navigasi, atau apakah itu karena pengontrol tampilan menghilang karena telah muncul.

Saat ini saya sedang mengatur bendera seperti isShowingChildViewControlleritu tetapi ini menjadi cukup rumit. Satu-satunya cara saya pikir saya bisa mendeteksinya adalah dalam -deallocmetode.

Air Terjun Michael
sumber

Jawaban:

227

Anda dapat menggunakan yang berikut ini.

- (void)viewWillDisappear:(BOOL)animated {
  [super viewWillDisappear:animated];
  NSArray *viewControllers = self.navigationController.viewControllers;
  if (viewControllers.count > 1 && [viewControllers objectAtIndex:viewControllers.count-2] == self) {
    // View is disappearing because a new view controller was pushed onto the stack
    NSLog(@"New view controller was pushed");
  } else if ([viewControllers indexOfObject:self] == NSNotFound) {
    // View is disappearing because it was popped from the stack
    NSLog(@"View controller was popped");
  }
}

Ini, tentu saja, mungkin karena tumpukan pengontrol tampilan UINavigationController (diekspos melalui properti viewControllers) telah diperbarui pada saat viewWillDisappear dipanggil.

Bryan Henry
sumber
2
Sempurna! Saya tidak tahu mengapa saya tidak memikirkan itu! Saya kira saya tidak berpikir tumpukan akan diubah sampai metode penghilangan dipanggil! Terima kasih :-)
Air Terjun Michael
1
Saya baru saja mencoba melakukan hal yang sama tetapi di dalam viewWillAppeardan tampaknya apakah pengontrol tampilan diungkapkan dengan didorong atau sesuatu di atasnya yang muncul, Array viewControllers sama di kedua sisi! Ada ide?
Air Terjun Michael
Saya juga harus mencatat bahwa pengontrol tampilan persisten selama masa pakai aplikasi sehingga saya tidak dapat melakukan tindakan saya viewDidLoadkarena hanya dipanggil sekali! Hmm, yang rumit!
Air Terjun Michael
4
@Sbrocket apakah ada alasan Anda tidak melakukan ![viewControllers containsObject:self]bukan [viewControllers indexOfObject:self] == NSNotFound? Pilihan gaya?
zekel
24
Jawaban ini sudah usang sejak iOS 5. -isMovingFromParentViewControllerMetode yang disebutkan di bawah ini memungkinkan Anda untuk menguji apakah tampilan dimunculkan secara eksplisit.
grahamparks
136

Menurut saya cara termudah adalah:

 - (void)viewWillDisappear:(BOOL)animated
{
    if ([self isMovingFromParentViewController])
    {
        NSLog(@"View controller was popped");
    }
    else
    {
        NSLog(@"New view controller was pushed");
    }
    [super viewWillDisappear:animated];
}

Cepat:

override func viewWillDisappear(animated: Bool)
{
    if isMovingFromParent
    {
        print("View controller was popped")
    }
    else
    {
        print("New view controller was pushed")
    }
    super.viewWillDisappear(animated)
}
RTasche
sumber
Pada iOS 5 ini jawabannya, mungkin juga periksa isBeingDismissed
d370urn3ur
4
Untuk iOS7, saya harus memeriksa [self.navigationController.viewControllers indexOfObject: self] == NSNotFound lagi karena latar belakang aplikasi juga akan lulus tes ini tetapi tidak akan menghapus diri dari tumpukan navigasi.
Eric Chen
3
Apple telah menyediakan cara terdokumentasi untuk melakukan ini - stackoverflow.com/a/33478133/385708
Shyam Bhat
Masalah dengan menggunakan viewWillDisappear adalah ada kemungkinan bahwa pengontrol muncul dari tumpukan saat tampilan sudah menghilang. Misalnya, viewcontroller lain dapat didorong ke atas tumpukan dan kemudian memanggil popToRootViewControllerAnimated bypassing viewWillDisappear di tengah.
John K
Misalkan Anda memiliki dua pengontrol (root vc dan yang lainnya didorong) pada tumpukan navigasi Anda. Saat yang ketiga didorong viewWillDisappear dipanggil pada detik yang pandangannya akan menghilang, bukan? Jadi ketika Anda pop ke root view controller (pop yang ketiga dan kedua) viewWillDisappear dipanggil pada ketiga yaitu vc terakhir di stack karena viewnya ada di atas dan akan menghilang saat ini dan view kedua sudah menghilang. Itulah mengapa metode ini disebut viewWillDisappear dan bukan viewControllerWillBePopped.
RTasche
61

Dari Dokumentasi Apple di UIViewController.h:

"Keempat metode ini dapat digunakan dalam callback tampilan pengontrol tampilan untuk menentukan apakah itu disajikan, ditutup, atau ditambahkan atau dihapus sebagai pengontrol tampilan anak. Misalnya, pengontrol tampilan dapat memeriksa apakah itu menghilang karena ditutup atau muncul dengan menanyakan dirinya sendiri dalam metode viewWillDisappear: dengan memeriksa ekspresi ([self isBeingDismissed] || [self isMovingFromParentViewController]). "

- (BOOL)isBeingPresented NS_AVAILABLE_IOS(5_0);

- (BOOL)isBeingDismissed NS_AVAILABLE_IOS(5_0);

- (BOOL)isMovingToParentViewController NS_AVAILABLE_IOS(5_0);

- (BOOL)isMovingFromParentViewController NS_AVAILABLE_IOS(5_0);

Jadi ya, satu-satunya cara yang didokumentasikan untuk melakukan ini adalah dengan cara berikut:

- (void)viewWillDisappear:(BOOL)animated
{
    [super viewWillDisappear:animated];
    if ([self isBeingDismissed] || [self isMovingFromParentViewController]) {
    }
}

Versi Swift 3:

override func viewWillDisappear(_ animated: Bool) {
    super.viewWillDisappear(animated)
    
    if self.isBeingDismissed || self.isMovingFromParentViewController { 
    }
}
Shyam Bhat
sumber
18

Jika Anda hanya ingin tahu apakah pandangan Anda semakin muncul, saya hanya menemukan bahwa self.navigationControlleradalah nildi viewDidDisappear, ketika dihapus dari tumpukan pengendali. Jadi itulah tes alternatif sederhana.

(Ini saya temukan setelah mencoba semua jenis perubahan lain. Saya terkejut tidak ada protokol pengontrol navigasi untuk mendaftarkan pengontrol tampilan agar diberi tahu saat muncul. Anda tidak dapat menggunakan UINavigationControllerDelegatekarena itu benar-benar berfungsi tampilan nyata.)

dk.
sumber
15

Cepat 4

override func viewWillDisappear(_ animated: Bool)
    {
        super.viewWillDisappear(animated)
        if self.isMovingFromParent
        {
            //View Controller Popped
        }
        else
        {
            //New view controller pushed
        }
    }
Umair
sumber
5

Di Swift:

 override func viewWillDisappear(animated: Bool) {
    if let navigationController = self.navigationController {
        if !contains(navigationController.viewControllers as! Array<UIViewController>, self) {
        }
    }

    super.viewWillDisappear(animated)

}
pengguna754905
sumber
Pastikan untuk digunakan sebagai! alih-alih as
dfmuir
1

Saya menemukan dokumentasi Apple tentang hal ini sulit untuk dipahami. Ekstensi ini membantu melihat status di setiap navigasi.

extension UIViewController {
    public func printTransitionStates() {
        print("isBeingPresented=\(isBeingPresented)")
        print("isBeingDismissed=\(isBeingDismissed)")
        print("isMovingToParentViewController=\(isMovingToParentViewController)")
        print("isMovingFromParentViewController=\(isMovingFromParentViewController)")
    }
}
Norman
sumber
1

Terima kasih @Bryan Henry, Masih bekerja di Swift 5

    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
        if let controllers = navigationController?.children{
            if controllers.count > 1, controllers[controllers.count - 2] == self{
                // View is disappearing because a new view controller was pushed onto the stack
                print("New view controller was pushed")
            }
            else if controllers.firstIndex(of: self) == nil{
                // View is disappearing because it was popped from the stack
                print("View controller was popped")
            }
        }

    }
dengST30
sumber
0

Pertanyaan ini cukup lama tetapi saya melihatnya secara tidak sengaja jadi saya ingin memposting praktik terbaik (afaik)

Anda bisa melakukannya

if([self.navigationController.viewControllers indexOfObject:self]==NSNotFound)
 // view controller popped
}
pengguna1396236
sumber
0

Ini berlaku untuk iOS7 , tidak tahu apakah itu berlaku untuk yang lain. Dari apa yang saya tahu, viewDidDisappeartampilan sudah muncul. Yang berarti ketika Anda melakukan kueri, self.navigationController.viewControllersAnda akan mendapatkan file nil. Jadi periksa saja apakah itu nihil.

TL; DR

 - (void)viewDidDisappear:(BOOL)animated
 {
    [super viewDidDisappear:animated];
    if (self.navigationController.viewControllers == nil) {
        // It has been popped!
        NSLog(@"Popped and Gone");
    }
 }
Byte
sumber
0

Segues bisa menjadi cara yang sangat efektif untuk menangani masalah ini di iOS 6+. Jika Anda telah memberikan pengenal pada segmen tertentu di Interface Builder, Anda dapat memeriksanya di prepareForSegue.

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
    if ([segue.identifier isEqualToString:@"LoginSegue"]) {
       NSLog(@"Push");
       // Do something specific here, or set a BOOL indicating
       // a push has occurred that will be checked later
    }
}
Kyle Clegg
sumber
-1

Saya berasumsi maksud Anda bahwa tampilan Anda sedang dipindahkan ke tumpukan pengontrol navigasi dengan mendorong tampilan baru saat Anda mengatakan didorong ke tumpukan. Saya akan menyarankan menggunakan viewDidUnloadmetode untuk menambahkan NSLogpernyataan untuk menulis sesuatu ke konsol sehingga Anda dapat melihat apa yang sedang terjadi, Anda mungkin ingin menambahkan NSLogke viewWillDissappeer.

Aaron
sumber
-1

Berikut adalah kategori untuk mencapai hal yang sama dengan jawaban sbrocket:

Header:

#import <UIKit/UIKit.h>

@interface UIViewController (isBeingPopped)

- (BOOL) isBeingPopped;

@end

Sumber:

#import "UIViewController+isBeingPopped.h"

@implementation UIViewController (isBeingPopped)

- (BOOL) isBeingPopped {
    NSArray *viewControllers = self.navigationController.viewControllers;
    if (viewControllers.count > 1 && [viewControllers objectAtIndex:viewControllers.count-2] == self) {
        return NO;
    } else if ([viewControllers indexOfObject:self] == NSNotFound) {
        return YES;
    }
    return NO;
}

@end
bbrame
sumber