จะตรวจสอบได้อย่างไรว่าตัวควบคุมมุมมองถูกนำเสนอแบบโมฆะหรือถูกผลักบนสแต็กการนำทาง?


126

ฉันจะแยกความแตกต่างระหว่าง:

  • นำเสนออย่างสุภาพ
  • ผลักบนสแตกการนำทาง

ทั้งสองอย่างpresentingViewControllerและisMovingToParentViewControllerอยู่YESในทั้งสองกรณีจึงไม่เป็นประโยชน์มากนัก

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

ปรากฎว่าปัญหาของฉันคือฉันฝังลงHtmlViewControllerในสิ่งUINavigationControllerที่จะนำเสนอแล้ว นั่นเป็นเหตุผลที่ความพยายามของฉันเองและคำตอบที่ดีด้านล่างไม่ได้ผล

HtmlViewController*     termsViewController = [[HtmlViewController alloc] initWithDictionary:dictionary];
UINavigationController* modalViewController;

modalViewController = [[UINavigationController alloc] initWithRootViewController:termsViewController];
modalViewController.modalTransitionStyle = UIModalTransitionStyleCoverVertical;
[self presentViewController:modalViewController
                   animated:YES
                 completion:nil];

ฉันเดาว่าฉันควรบอกตัวควบคุมมุมมองของฉันดีกว่าเมื่อมันเป็นกิริยาแทนที่จะพยายามกำหนด

คำตอบ:


125

ใช้เกลือเม็ดไม่ได้ทดสอบ

- (BOOL)isModal {
     if([self presentingViewController])
         return YES;
     if([[[self navigationController] presentingViewController] presentedViewController] == [self navigationController])
         return YES;
     if([[[self tabBarController] presentingViewController] isKindOfClass:[UITabBarController class]])
         return YES;

    return NO;
 }

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

2
อย่างที่ฉันเขียนpresentingViewControllerอยู่YESในกรณีของฉันเสมอ ไม่ช่วย
เรื่องความหมาย

3
presentingViewControllerผลตอบแทนYESสำหรับ VC แบบพุชเมื่อมีUITabBarControllerการตั้งค่าเป็นรูท ดังนั้นไม่เหมาะในกรณีของฉัน
Yevhen Dubinin

5
สิ่งนี้จะใช้ไม่ได้หากคุณนำเสนอตัวควบคุมมุมมองจากนั้นมันจะดันอีกตัวหนึ่ง
Lee

3
"สิ่งนี้ใช้ไม่ได้หากคุณนำเสนอตัวควบคุมมุมมองจากนั้นมันจะผลักอีกอันหนึ่ง" นั่นไม่ใช่จุดประสงค์ของสิ่งนี้ตัวควบคุมมุมมองแบบพุชจะไม่ถูกนำเสนอ
Colin Swelin

87

ในSwift :

เพิ่มแฟล็กเพื่อทดสอบว่าเป็นโมดอลตามประเภทคลาสหรือไม่:

// MARK: - UIViewController implementation

extension UIViewController {

    var isModal: Bool {

        let presentingIsModal = presentingViewController != nil
        let presentingIsNavigation = navigationController?.presentingViewController?.presentedViewController == navigationController
        let presentingIsTabBar = tabBarController?.presentingViewController is UITabBarController

        return presentingIsModal || presentingIsNavigation || presentingIsTabBar
    }
}

4
ควรจะดีกว่าใน var เช่นvar isModal: Bool {}
malinois

1
@malinois มีการเปลี่ยนแปลง
YannSteph

falseพารามิเตอร์สุดท้ายในreturnคำสั่งทำอะไร?
damjandd

คุณต้องเปลี่ยนเพื่อให้ presentationIsNavigation = navigationController? .presentingViewController? .presentedViewController == navigationController && navigationController! = nil
famfamfam

78

คุณมองข้ามวิธีการหนึ่ง: isBeingPresented.

isBeingPresented เป็นจริงเมื่อมีการนำเสนอตัวควบคุมมุมมองและเป็นเท็จเมื่อถูกผลัก

- (void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];

    if ([self isBeingPresented]) {
        // being presented
    } else if ([self isMovingToParentViewController]) {
        // being pushed
    } else {
        // simply showing again because another VC was dismissed
    }
}

2
ฉันพยายามนี้เกินไปก่อนที่จะโพสต์และก็ไม่ได้ทำงานเป็นisBeingPresented NOแต่ฉันเห็นเหตุผลตอนนี้ฉันกำลังฝังตัวควบคุมมุมมองที่นำเสนอไว้ใน a UINavigationControllerและนั่นคือสิ่งที่ฉันกำลังผลักดัน
ความหมาย - เรื่อง

1
คุณไม่สามารถผลักดันตัวควบคุมการนำทาง บางทีคุณอาจหมายความว่าคุณกำลังนำเสนอตัวควบคุมการนำทาง
rmaddy

3
@jowie ใช้pไม่ใช่poเมื่อพิมพ์ค่าดั้งเดิม poใช้สำหรับพิมพ์วัตถุ
rmaddy

37
เอกสารประกอบสำหรับisBeingPresented- วิธีนี้จะส่งคืน YES เฉพาะเมื่อเรียกจากภายในเมธอด viewWillAppear: และ viewDidAppear:
funct7

4
@Terrence ดูเหมือนว่าเอกสารล่าสุดจะไม่แสดงข้อมูลนั้น แต่เคยมีอยู่ isBeingPresented, isBeingDismissed, isMovingFromParentViewControllerและisMovingToParentViewControllerเท่านั้นที่ถูกต้องภายใน 4 view[Will|Did][Disa|A]ppearวิธี
rmaddy

29

Swift 5
นี่คือโซลูชันที่แก้ไขปัญหาที่กล่าวถึงพร้อมกับคำตอบก่อนหน้านี้เมื่อisModal()ส่งคืนtrueหากผลักUIViewControllerอยู่ในUINavigationControllerสแต็กที่นำเสนอ

extension UIViewController {
    var isModal: Bool {
        if let index = navigationController?.viewControllers.firstIndex(of: self), index > 0 {
            return false
        } else if presentingViewController != nil {
            return true
        } else if navigationController?.presentingViewController?.presentedViewController == navigationController {
            return true
        } else if tabBarController?.presentingViewController is UITabBarController {
            return true
        } else {
            return false
        }
    }
}

มันใช้งานได้สำหรับฉันจนถึงตอนนี้ หากมีการเพิ่มประสิทธิภาพโปรดแบ่งปัน


ทำไมถึงต้องตรวจtabBarController?.presentingViewController is UITabBarController ? มันสำคัญหรือไม่ว่านั่นpresentingViewControllerเป็น UITabBarController หรือไม่?
Hlung

และถ้า navigationController เป็นศูนย์จะกลับมาisModal trueนี่ตั้งใจหรือเปล่า?
Hlung

28

self.navigationController! = nil หมายความว่าอยู่ในสแต็กการนำทาง

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

extension UIViewController {
    var isModal: Bool {
        if let index = navigationController?.viewControllers.firstIndex(of: self), index > 0 {
            return false
        } else if presentingViewController != nil {
            return true
        } else if let navigationController = navigationController, navigationController.presentingViewController?.presentedViewController == navigationController {
            return true
        } else if let tabBarController = tabBarController, tabBarController.presentingViewController is UITabBarController {
            return true
        } else {
            return false
        }
    }
}

โดยทั่วไปเมื่อคุณนำเสนอแบบโมฆะคุณใส่ viewController บน navigationController และคุณนำเสนอ หากเป็นเช่นนั้นคำแถลงของคุณอาจผิดอย่างไรก็ตามในกรณีนี้จะมีการจัดการกับรหัส โปรดปรับปรุงคำตอบของคุณ :)
E-Riddie

งานดีที่เกี่ยวข้องกับกรณีการใช้งานทั้งหมด มีที่ว่างสำหรับการ refactoring เล็กน้อย แต่ยังคงโหวต !!
Jean Raymond Daher

12

สวิฟต์ 4

var isModal: Bool {
    return presentingViewController != nil ||
           navigationController?.presentingViewController?.presentedViewController === navigationController ||
           tabBarController?.presentingViewController is UITabBarController
}

Swift 4.2 / iOS 12 ยังใช้งานได้ดี แต่โปรดทราบว่า navigationController? .presentingViewController? .presentedViewController === navigationController จะประเมินเป็นจริงหากทั้งสองไม่มี (ตัวอย่างเช่นหากคุณเรียกใช้บนคอนโทรลเลอร์มุมมองที่ยังไม่ได้ นำเสนอ)
Eli Burke

7

Swift 5.สะอาดและเรียบง่าย

if navigationController.presentingViewController != nil {
    // Navigation controller is being presented modally
}

1
นี่เป็นเคล็ดลับสำหรับฉัน
Radu Ursache

3

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

DEViewController.h ไฟล์:

#import <UIKit/UIKit.h>

// it is a base class for all view controllers within a project
@interface DEViewController : UIViewController 

// specify a way viewcontroller, is presented  by another viewcontroller
// the presented view controller should manually assign the value to it
typedef NS_ENUM(NSUInteger, SSViewControllerPresentationMethod) {
    SSViewControllerPresentationMethodUnspecified = 0,
    SSViewControllerPresentationMethodPush,
    SSViewControllerPresentationMethodModal,
};
@property (nonatomic) SSViewControllerPresentationMethod viewControllerPresentationMethod;

// other properties/methods...
@end

ตอนนี้งานนำเสนอสามารถจัดการได้ด้วยวิธีนี้:

ผลักบนสแต็กการนำทาง:

// DETestViewController inherits from DEViewController
DETestViewController *vc = [DETestViewController new];
vc.viewControllerPresentationMethod = SSViewControllerPresentationMethodPush;
[self.navigationController pushViewController:vc animated:YES];

นำเสนอแบบสุภาพพร้อมการนำทาง:

DETestViewController *vc = [DETestViewController new];
vc.viewControllerPresentationMethod = SSViewControllerPresentationMethodModal;
UINavigationController *nav = [[UINavigationController alloc]
                               initWithRootViewController:vc];
[self presentViewController:nav animated:YES completion:nil];

นำเสนอแบบสุภาพ:

DETestViewController *vc = [DETestViewController new];
vc.viewControllerPresentationMethod = SSViewControllerPresentationMethodModal;
[self presentViewController:vc animated:YES completion:nil];

นอกจากนี้ในDEViewControllerเราสามารถเพิ่มทางเลือกในการ "ตรวจสอบ" ได้หากคุณสมบัติดังกล่าวมีค่าเท่ากับSSViewControllerPresentationMethodUnspecified:

- (BOOL)isViewControllerPushed
{
    if (self.viewControllerPresentationMethod != SSViewControllerPresentationMethodUnspecified) {
        return (BOOL)(self.viewControllerPresentationMethod == SSViewControllerPresentationMethodPush);
    }

    else {
        // fallback to default determination method
        return (BOOL)self.navigationController.viewControllers.count > 1;
    }
}

3

สมมติว่า viewControllers ทั้งหมดที่คุณนำเสนอแบบโมดูลาร์ถูกรวมอยู่ใน navigationController ใหม่ (ซึ่งคุณควรทำต่อไป) คุณสามารถเพิ่มคุณสมบัตินี้ใน VC ของคุณได้

private var wasPushed: Bool {
    guard let vc = navigationController?.viewControllers.first where vc == self else {
        return true
    }

    return false
}

1
ซึ่งคุณควรทำอยู่เสมอ - โปรดอธิบายว่าทำไม?
Alexander Abakumov

อเล็กซานเดอร์คุณไม่ควรจริงๆ
nickdnk

2

ในการตรวจจับตัวควบคุมของคุณถูกผลักหรือไม่เพียงแค่ใช้โค้ดด้านล่างในทุกที่ที่คุณต้องการ:

if ([[[self.parentViewController childViewControllers] firstObject] isKindOfClass:[self class]]) {

    // Not pushed
}
else {

    // Pushed
}

ฉันหวังว่ารหัสนี้จะช่วยใครก็ได้ ...


1
วิธีนี้ใช้ไม่ได้เมื่อคุณใช้คลาสคอนโทรลเลอร์มุมมองเดียวกันในหลาย ๆ ที่เนื่องจากจะตรวจสอบเฉพาะคลาสของมันเท่านั้น คุณสามารถตรวจสอบความเท่าเทียมกันอย่างชัดเจนแทนได้
gklka

1

self.navigationController != nil หมายความว่ามันอยู่ในสแต็กการนำทาง


25
ยังคงอยู่ในตัวควบคุมการนำทางแบบโมดอลได้
ColdLogic

ดังนั้น 'modal' และ 'push on navigation stack' จึงไม่สามารถใช้ร่วมกันได้ การคิดนี้ขึ้นอยู่กับบริบท แต่การตรวจสอบว่า self.navigationController ไม่ใช่ศูนย์จะตอบว่าเป็นตัวควบคุมมุมมองของตัวควบคุมการนำทางหรือไม่
Daniel

@ แดเนียลความแตกต่างระหว่าง "ผลัก" และ "นำเสนอ" "Modal" ไม่มีส่วนเกี่ยวข้องใด ๆ ฉันเชื่อว่า "ColdLogic" หมายถึง "นำเสนอ" เมื่อพวกเขาพูดว่า "modal"
rmaddy


1

Swift 5
ส่วนขยายที่ใช้งานง่ายนี้สามารถจัดการเคสได้มากกว่าคำตอบก่อนหน้าเล็กน้อย กรณีเหล่านี้คือ VC (ตัวควบคุมมุมมอง) เป็น VC หลักของหน้าต่างแอป VC จะถูกเพิ่มเป็นลูกของ VC หลัก มันพยายามที่จะคืนค่าจริงก็ต่อเมื่อมีการนำเสนอวิวคอนโทรลเลอร์เท่านั้น

extension UIViewController {
    /**
      returns true only if the viewcontroller is presented.
    */
    var isModal: Bool {
        if let index = navigationController?.viewControllers.firstIndex(of: self), index > 0 {
            return false
        } else if presentingViewController != nil {
            if let parent = parent, !(parent is UINavigationController || parent is UITabBarController) {
                return false
            }
            return true
        } else if let navController = navigationController, navController.presentingViewController?.presentedViewController == navController {
            return true
        } else if tabBarController?.presentingViewController is UITabBarController {
            return true
        }
        return false
    }
}

ขอบคุณกับคำตอบของ Jonauz อีกครั้งมีพื้นที่สำหรับการปรับให้เหมาะสมมากขึ้น โปรดพูดคุยเกี่ยวกับกรณีที่ต้องจัดการในส่วนความคิดเห็น


0

หากคุณใช้ ios 5.0 หรือใหม่กว่าโปรดใช้รหัสนี้

-(BOOL)isPresented
{
    if ([self isBeingPresented]) {
        // being presented
         return YES;
    } else if ([self isMovingToParentViewController]) {
        // being pushed
         return NO;
    } else {
        // simply showing again because another VC was dismissed
         return NO;
    }
}

-1

สำหรับบางคนที่สงสัยว่าจะบอก ViewController ได้อย่างไรว่ากำลังนำเสนอ

ถ้าAกำลังนำเสนอ / ผลักดันB

  1. กำหนดenumและpropertyในB

    enum ViewPresentationStyle {
        case Push
        case Present
    }
    
    //and write property 
    
    var vcPresentationStyle : ViewPresentationStyle = .Push //default value, considering that B is pushed 
  2. ตอนนี้ในAมุมมองตัวควบคุมบอกBว่ามีการนำเสนอ / ผลักดันโดยการมอบหมายหรือไม่presentationStyle

    func presentBViewController() {
        let bViewController = B()
        bViewController.vcPresentationStyle = .Present //telling B that it is being presented
        self.presentViewController(bViewController, animated: true, completion: nil)
    }
  3. การใช้งานในBView Controller

    override func viewDidLoad() {
        super.viewDidLoad()
    
        if self.vcPresentationStyle == .Present {
            //is being presented 
        }
        else {
            //is being pushed
        }
    
    }

-2
id presentedController = self.navigationController.modalViewController;
if (presentedController) {
     // Some view is Presented
} else {
     // Some view is Pushed
}

สิ่งนี้จะแจ้งให้คุณทราบว่ามีการนำเสนอหรือผลัก viewController


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