viewWillDisappear: Xác định xem bộ điều khiển xem đang được bật hay đang hiển thị bộ điều khiển xem phụ


134

Tôi đang đấu tranh để tìm một giải pháp tốt cho vấn đề này. Trong -viewWillDisappear:phương thức của bộ điều khiển chế độ xem , tôi cần tìm cách xác định xem đó có phải là do bộ điều khiển chế độ xem đang được đẩy lên ngăn xếp của bộ điều khiển điều hướng hay là do bộ điều khiển chế độ xem biến mất vì nó đã bị bật.

Hiện tại tôi đang cài đặt các cờ như vậy isShowingChildViewControllernhưng nó trở nên khá phức tạp. Cách duy nhất tôi nghĩ rằng tôi có thể phát hiện ra nó là trong -deallocphương thức.

Câu trả lời:


228

Bạn có thể sử dụng như sau.

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

Tất nhiên, điều này là có thể bởi vì ngăn xếp trình điều khiển khung nhìn của UINavestionContaptor (được hiển thị thông qua thuộc tính viewControllers) đã được cập nhật theo thời gian mà viewWillDisappear được gọi.


2
Hoàn hảo! Tôi không biết tại sao tôi không nghĩ về điều đó! Tôi đoán tôi đã không nghĩ rằng ngăn xếp sẽ bị thay đổi cho đến khi các phương thức biến mất được gọi! Cảm ơn :-)
Thác Michael

1
Tôi vừa mới cố gắng thực hiện điều tương tự nhưng trong viewWillAppearđó và dường như liệu trình điều khiển chế độ xem có bị tiết lộ khi bị đẩy hay thứ gì đó phía trên nó được bật lên hay không, mảng viewControllers đều giống nhau! Có ý kiến ​​gì không?
Thác Michael

Tôi cũng nên lưu ý rằng trình điều khiển chế độ xem vẫn tồn tại trong suốt vòng đời của ứng dụng vì vậy tôi không thể thực hiện hành động của mình viewDidLoadvì nó chỉ được gọi một lần! Hmm, khó khăn một!
Thác nước Michael

4
@Sbrocket có lý do gì bạn không làm ![viewControllers containsObject:self]thay vì [viewControllers indexOfObject:self] == NSNotFound? Lựa chọn phong cách nào?
zekel

24
Câu trả lời này đã lỗi thời kể từ iOS 5. -isMovingFromParentViewControllerPhương pháp được đề cập dưới đây cho phép bạn kiểm tra xem chế độ xem có được hiển thị rõ ràng hay không.
grahamparks

136

Tôi nghĩ cách dễ nhất là:

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

Nhanh:

override func viewWillDisappear(animated: Bool)
{
    if isMovingFromParent
    {
        print("View controller was popped")
    }
    else
    {
        print("New view controller was pushed")
    }
    super.viewWillDisappear(animated)
}

Kể từ iOS 5, đây là câu trả lời, cũng có thể kiểm tra isByingDismissed
d370urn3ur

4
Đối với iOS7, tôi phải kiểm tra [self.navlationControll.viewControllers indexOfObject: self] == NSNotFound một lần nữa vì nền tảng ứng dụng cũng sẽ vượt qua bài kiểm tra này nhưng sẽ không tự xóa khỏi ngăn xếp điều hướng.
Eric Chen

3
Apple đã cung cấp một cách được ghi lại để làm điều này - stackoverflow.com/a/33478133/385708
Shyam Bhat

Vấn đề với việc sử dụng viewWillDisappear là có thể bộ điều khiển được bật ra khỏi ngăn xếp trong khi chế độ xem đã biến mất. Ví dụ, một trình điều khiển khung nhìn khác có thể được đẩy lên trên cùng của ngăn xếp và sau đó gọi popToRootViewControllAnimated bỏ qua viewWillDisappear trên những cái ở giữa.
John K

Giả sử bạn có hai bộ điều khiển (root vc và bộ đẩy khác) trên ngăn xếp điều hướng của bạn. Khi cái thứ ba đang được đẩy viewWillDisappear được gọi vào cái thứ hai mà chế độ xem sẽ biến mất, phải không? Vì vậy, khi bạn bật bộ điều khiển chế độ xem gốc (bật thứ ba và thứ hai) viewWillDisappear được gọi vào thứ ba tức là vc cuối cùng trên ngăn xếp vì chế độ xem của nó ở trên cùng và sẽ biến mất vào lúc này và chế độ xem thứ hai đã biến mất. Đó là lý do tại sao phương thức này được gọi là viewWillDisappear chứ không phải viewControllWillBePopped.
RTasche

61

Từ Tài liệu của Apple trong UIViewControll.h:

"Bốn phương thức này có thể được sử dụng trong các cuộc gọi lại xuất hiện của bộ điều khiển xem để xác định xem nó có được trình bày, loại bỏ hoặc thêm hoặc xóa như một bộ điều khiển xem con không. Ví dụ, bộ điều khiển xem có thể kiểm tra xem nó có biến mất không vì nó bị loại bỏ hoặc xuất hiện bằng cách tự hỏi trong phương thức viewWillDisappear: của nó bằng cách kiểm tra biểu thức ([self isByingDismissed] || [self isMovingFromParentViewCont điều khiển]). "

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

Vì vậy, có, cách duy nhất được ghi lại để làm điều này là cách sau:

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

Phiên bản Swift 3:

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

18

Nếu bạn chỉ muốn biết liệu xem bạn là nhận được popped, tôi chỉ phát hiện ra rằng self.navigationControllerniltrong viewDidDisappear, khi nó được lấy ra từ ngăn xếp của bộ điều khiển. Vì vậy, đó là một thử nghiệm thay thế đơn giản.

(Điều này tôi phát hiện ra sau khi thử tất cả các loại mâu thuẫn khác. Tôi ngạc nhiên không có giao thức điều khiển điều hướng để đăng ký bộ điều khiển xem được thông báo trên cửa sổ bật lên. Bạn không thể sử dụng UINavigationControllerDelegatevì điều đó thực sự hoạt động trên màn hình thực sự.)


16

Swift 4

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

6

Trong Swift:

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

    super.viewWillDisappear(animated)

}

Hãy chắc chắn để sử dụng như! thay vì như
dfmuir

2

Tôi thấy tài liệu của Apple về điều này thật khó hiểu. Phần mở rộng này giúp xem các trạng thái tại mỗi điều hướng.

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

1

Câu hỏi này khá cũ nhưng tôi đã thấy nó một cách tình cờ nên tôi muốn đăng bài thực hành tốt nhất (afaik)

bạn chỉ có thể làm

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

1

Điều này áp dụng cho iOS7 , không biết nó có áp dụng cho bất kỳ ứng dụng nào khác không. Từ những gì tôi biết, trong viewDidDisappearchế độ xem đã được bật lên. Có nghĩa là khi bạn truy vấn, self.navigationController.viewControllersbạn sẽ nhận được một nil. Vì vậy, chỉ cần kiểm tra nếu đó là con số không.

TL; DR

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

1

Phân đoạn có thể là một cách rất hiệu quả để xử lý vấn đề này trong iOS 6+. Nếu bạn đã cung cấp cho biệt danh cụ thể một mã định danh trong Trình tạo giao diện, bạn có thể kiểm tra nó trong 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
    }
}

1

Cảm ơn @Bryan Henry, Vẫn hoạt động trong 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")
            }
        }

    }

-1

Tôi giả sử bạn có nghĩa là chế độ xem của bạn đang được chuyển xuống ngăn xếp của bộ điều khiển điều hướng bằng cách đẩy một chế độ xem mới khi bạn nói được đẩy lên ngăn xếp. Tôi sẽ đề nghị sử dụng các viewDidUnloadphương pháp để thêm một NSLogtuyên bố để ghi cái gì đó để giao diện điều khiển, do đó bạn có thể xem những gì đang xảy ra, bạn có thể muốn thêm một NSLogtới viewWillDissappeer.


-1

Đây là một danh mục để thực hiện điều tương tự như câu trả lời của sbrocket:

Tiêu đề:

#import <UIKit/UIKit.h>

@interface UIViewController (isBeingPopped)

- (BOOL) isBeingPopped;

@end

Nguồn:

#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
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.