viewWillDisappear: ตรวจสอบว่าตัวควบคุมมุมมองกำลังถูกเปิดหรือกำลังแสดงตัวควบคุมมุมมองย่อย


133

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

ในขณะนี้ฉันกำลังตั้งค่าสถานะเช่นisShowingChildViewControllerแต่มันค่อนข้างซับซ้อน วิธีเดียวที่ฉันคิดว่าฉันสามารถตรวจจับได้คือ-deallocวิธีการ

คำตอบ:


228

คุณสามารถใช้สิ่งต่อไปนี้

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

แน่นอนว่าเป็นไปได้เนื่องจากสแต็กคอนโทรลเลอร์มุมมองของ UINavigationController (เปิดเผยผ่านคุณสมบัติ viewControllers) ได้รับการอัพเดตตามเวลาที่เรียก viewWillDisappear


2
สมบูรณ์แบบ! ไม่รู้ทำไมถึงไม่คิดแบบนั้น! ฉันเดาว่าฉันไม่คิดว่าสแต็กจะถูกเปลี่ยนแปลงจนกว่าจะมีการเรียกวิธีการหายตัวไป! ขอบคุณ :-)
Michael Waterfall

1
ฉันเพิ่งพยายามทำสิ่งเดียวกัน แต่ในviewWillAppearและดูเหมือนว่าไม่ว่าตัวควบคุมมุมมองจะถูกเปิดเผยโดยการถูกผลักหรือสิ่งที่อยู่เหนือมันถูกโผล่ออกมาอาร์เรย์ viewControllers ก็เหมือนกันทั้งสองวิธี! ความคิดใด ๆ ?
Michael Waterfall

ฉันควรทราบด้วยว่าตัวควบคุมมุมมองนั้นคงอยู่ตลอดอายุการใช้งานของแอปดังนั้นฉันจึงไม่สามารถดำเนินการได้viewDidLoadเนื่องจากมีการเรียกเพียงครั้งเดียว! อืมมีเล่ห์เหลี่ยม!
Michael Waterfall

4
@Sbrocket มีเหตุผลที่คุณไม่ทำ![viewControllers containsObject:self]แทน[viewControllers indexOfObject:self] == NSNotFoundหรือไม่? เลือกสไตล์?
zekel

24
คำตอบนี้ล้าสมัยไปแล้วตั้งแต่ iOS 5 -isMovingFromParentViewControllerวิธีการที่กล่าวถึงด้านล่างช่วยให้คุณสามารถทดสอบได้ว่ามีการเปิดมุมมองอย่างชัดเจนหรือไม่
grahamparks

136

ฉันคิดว่าวิธีที่ง่ายที่สุดคือ:

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

รวดเร็ว:

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

สำหรับ iOS 5 นี่คือคำตอบอาจตรวจสอบ isBeingDismissed
d370urn3ur

4
สำหรับ iOS7 ฉันต้องตรวจสอบ [self.navigationController.viewControllers indexOfObject: self] == NSNotFound อีกครั้งเนื่องจากการทำพื้นหลังแอปจะผ่านการทดสอบนี้ด้วย แต่จะไม่ลบตัวเองออกจากสแต็กการนำทาง
Eric Chen

3
Apple ได้จัดเตรียมเอกสารวิธีการไว้ให้แล้ว - stackoverflow.com/a/33478133/385708
Shyam Bhat

ปัญหาในการใช้ viewWillDisappear คือเป็นไปได้ว่าตัวควบคุมถูกดึงออกมาจากสแต็กในขณะที่มุมมองหายไปแล้ว ตัวอย่างเช่นตัวควบคุมมุมมองอื่นอาจถูกผลักไปที่ด้านบนของสแต็กแล้วเรียกใช้ popToRootViewControllerAnimate บายพาส viewWillDisappear ที่อยู่ตรงกลาง
John K

สมมติว่าคุณมีคอนโทรลเลอร์สองตัว (root vc และอีกตัวที่พุช) บนสแต็กการนำทางของคุณ เมื่ออันที่สามถูกผลัก viewWillDisappear ถูกเรียกในวินาทีที่มุมมองกำลังจะหายไปใช่ไหม? ดังนั้นเมื่อคุณเปิดไปที่ตัวควบคุมมุมมองรูท (ป๊อปที่สามและที่สอง) viewWillDisappear จะถูกเรียกบน vc ตัวที่สามในสแต็กเนื่องจากมุมมองอยู่ด้านบนและจะหายไปในเวลานี้และมุมมองของวินาทีก็หายไปแล้ว นั่นเป็นเหตุผลที่เรียกวิธีนี้ว่า viewWillDisappear และไม่ใช่ viewControllerWillBePopped
RTasche

61

จากเอกสารของ Apple ใน UIViewController.h:

"วิธีการทั้งสี่นี้สามารถใช้ในการเรียกกลับลักษณะของตัวควบคุมมุมมองเพื่อตรวจสอบว่ากำลังนำเสนอปิดหรือเพิ่มหรือลบออกเป็นตัวควบคุมมุมมองเด็กตัวอย่างเช่นตัวควบคุมมุมมองสามารถตรวจสอบได้ว่ากำลังหายไปเนื่องจากถูกปิดหรือ โผล่มาโดยถามตัวเองใน viewWillDisappear: method โดยการตรวจสอบนิพจน์ ([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);

ใช่วิธีเดียวที่เป็นเอกสารในการดำเนินการนี้คือวิธีต่อไปนี้:

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

เวอร์ชัน Swift 3:

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

18

ถ้าคุณแค่อยากรู้ว่ามุมมองของคุณโผล่ขึ้นมาหรือไม่ฉันเพิ่งค้นพบว่าself.navigationControllerอยู่nilในviewDidDisappearนั้นเมื่อถูกลบออกจากสแต็กของคอนโทรลเลอร์ นั่นเป็นการทดสอบทางเลือกง่ายๆ

(สิ่งนี้ฉันค้นพบหลังจากลองใช้รูปแบบอื่น ๆ ทุกประเภทฉันแปลกใจที่ไม่มีโปรโตคอลตัวควบคุมการนำทางในการลงทะเบียนตัวควบคุมมุมมองเพื่อรับการแจ้งเตือนในป๊อปคุณใช้ไม่ได้UINavigationControllerDelegateเพราะนั่นใช้งานได้จริง)


15

สวิฟต์ 4

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

5

ใน Swift:

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

    super.viewWillDisappear(animated)

}

อย่าลืมใช้เป็น! แทนที่จะเป็น
dfmuir

1

ฉันพบว่าเอกสารของ Apple เกี่ยวกับเรื่องนี้ยากที่จะเข้าใจ ส่วนขยายนี้ช่วยดูสถานะในการนำทางแต่ละครั้ง

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

1

ขอบคุณ @Bryan Henry ยังคงใช้งานได้ใน 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")
            }
        }

    }

0

คำถามนี้ค่อนข้างเก่า แต่ฉันเห็นมันโดยบังเอิญฉันจึงอยากโพสต์แนวปฏิบัติที่ดีที่สุด (afaik)

คุณก็ทำได้

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

0

สิ่งนี้ใช้ได้กับiOS7โดยไม่รู้ว่าจะใช้กับiOS7อื่น ๆ ได้หรือไม่ จากสิ่งที่ฉันรู้ในviewDidDisappearมุมมองได้ถูกโผล่ขึ้นมา ซึ่งหมายความว่าเมื่อคุณสอบถามself.navigationController.viewControllersคุณจะได้รับไฟล์nil. ดังนั้นตรวจสอบว่าเป็นศูนย์หรือไม่

TL; ดร

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

0

Segues เป็นวิธีที่มีประสิทธิภาพมากในการจัดการปัญหานี้ใน iOS 6+ ถ้าคุณได้ให้ทำต่อโดยเฉพาะอย่างยิ่งตัวระบุใน Interface Builder 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

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


-1

นี่คือหมวดหมู่เพื่อให้บรรลุสิ่งเดียวกันกับคำตอบของ sbrocket:

หัวข้อ:

#import <UIKit/UIKit.h>

@interface UIViewController (isBeingPopped)

- (BOOL) isBeingPopped;

@end

ที่มา:

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