เป็นไปได้หรือไม่ที่จะระบุว่า ViewController แสดงเป็น Modal?


คำตอบ:


96

เนื่องจากmodalViewControllerได้เลิกใช้งานใน iOS 6 แล้วนี่คือเวอร์ชันที่ใช้งานได้กับ iOS 5+ และจะรวบรวมโดยไม่มีคำเตือน

Objective-C:

- (BOOL)isModal {
    return self.presentingViewController.presentedViewController == self
      || (self.navigationController != nil && self.navigationController.presentingViewController.presentedViewController == self.navigationController)
      || [self.tabBarController.presentingViewController isKindOfClass:[UITabBarController class]];
}

สวิฟท์:

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

เคล็ดลับสำหรับคำตอบของเฟลิเป้


2
จับได้ดีฉันต้องใช้มันอีกครั้งหลังจากนั้นไม่นานและสังเกตเห็นว่าการเลิกใช้งานเกิดขึ้น ... ฉันแก้ไขคำตอบของฉันเพื่อให้ผู้คนเริ่มมองหารหัสที่ถูกต้องเมื่อใช้ iOS 6+ ขอบคุณ
Felipe Sabino

10
ไม่ทำงานหากตัวควบคุมมุมมองหลักเป็นโมดอลที่ตัวควบคุมมุมมองของเราถูกผลัก
เรื่องความหมาย

2
มีข้อผิดพลาดเราควรตรวจสอบว่าทั้งสองฝ่ายเป็นศูนย์หรือไม่เพราะnil == nilผลตอบแทนYESไม่ใช่ผลลัพธ์ที่เราต้องการ
CocoaBob

1
@GabrielePetronella คุณรังเกียจไหมถ้าฉันอัปเดตคำตอบเพื่อรวมการใช้งานวิธีการอย่างรวดเร็ว
Michael Waterfall

1
@MichaelWaterfall ที่จะได้รับการชื่นชมอย่างมากขอบคุณ
Gabriele Petronella

77

หากคุณกำลังมองหา iOS 6+ คำตอบนี้เลิกใช้แล้วและคุณควรตรวจสอบคำตอบของ Gabriele Petronella


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

ดังนั้นเพื่อตรวจสอบว่าตัวควบคุมปัจจุบัน (แสดงเป็นselfรหัสร้อง) ถูกนำเสนอในรูปแบบโมดอลหรือไม่ฉันมีฟังก์ชั่นดังต่อไปนี้ในUIViewControllerหมวดหมู่หรือ (หากโครงการของคุณไม่จำเป็นต้องใช้คอนโทรลเลอร์ UIKit อื่นเช่นUITableViewControllerตัวอย่าง) ในตัวควบคุมพื้นฐานที่ตัวควบคุมอื่น ๆ ของฉันสืบทอดมา

-(BOOL)isModal {

     BOOL isModal = ((self.parentViewController && self.parentViewController.modalViewController == self) || 
            //or if I have a navigation controller, check if its parent modal view controller is self navigation controller
            ( self.navigationController && self.navigationController.parentViewController && self.navigationController.parentViewController.modalViewController == self.navigationController) || 
            //or if the parent of my UITabBarController is also a UITabBarController class, then there is no way to do that, except by using a modal presentation
            [[[self tabBarController] parentViewController] isKindOfClass:[UITabBarController class]]);

    //iOS 5+
    if (!isModal && [self respondsToSelector:@selector(presentingViewController)]) {

        isModal = ((self.presentingViewController && self.presentingViewController.modalViewController == self) || 
             //or if I have a navigation controller, check if its parent modal view controller is self navigation controller
             (self.navigationController && self.navigationController.presentingViewController && self.navigationController.presentingViewController.modalViewController == self.navigationController) || 
             //or if the parent of my UITabBarController is also a UITabBarController class, then there is no way to do that, except by using a modal presentation
             [[[self tabBarController] presentingViewController] isKindOfClass:[UITabBarController class]]);

    }

    return isModal;        

}

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

แก้ไข 2: เพิ่มการตรวจสอบ iOS 5+ ซึ่งUIViewControllerไม่ตอบโจทย์parentViewControllerอีกต่อไป แต่เป็นpresentingViewControllerแทน

แก้ไข 3: ฉันได้สร้างส่วนสำคัญสำหรับมันในกรณีที่https://gist.github.com/3174081


โปรดทราบว่าmodalViewControllerคุณสมบัตินี้เลิกใช้แล้วใน iOS 6 เอกสารแนะนำให้ใช้presentedViewControllerแทน
Bart Jacobs

@BartJacobs จุดดี! ฉันไม่ได้ดูคำตอบนี้หลังจากเปิดตัว iOS6 ดังนั้นจึงอาจไม่เป็นปัจจุบัน ฉันจะพยายามทำการทดสอบบางอย่างในสัปดาห์ต่อไปเพื่ออัปเดต tks!
Felipe Sabino

NSLog(@"%@", self.navigationController.parentViewController)พิมพ์(null)- คุณช่วยอธิบายว่าทำไม My ViewController เชื่อมต่อกับ modal view controller ผ่าน navController ใน storyboard
โรมัน

@oyatek คุณสามารถใช้ pastebin หรือสิ่งที่คล้ายกันและแสดงรหัสได้หรือไม่?
Felipe Sabino

@Feilpe พบปัญหา - .parentViewControllerเลิกใช้งานแล้ว.presentingViewControllerต้องใช้แทน
โรมัน

35

ใน iOS5 + ดังที่คุณเห็นในUIViewController Class Referenceคุณสามารถรับได้จากคุณสมบัติ "presentationViewController"

PresentationViewController ตัวควบคุมมุมมองที่นำเสนอตัวควบคุมมุมมองนี้ (อ่านเท่านั้น)

@property (nonatomic อ่านอย่างเดียว) UIViewController * presentationViewController
Discussion

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

ความพร้อม
ใช้งานมีให้ใน iOS 5.0 และใหม่กว่า
ประกาศใน
UIViewController.h


3
ทำงานได้อย่างสมบูรณ์ใช้ if (self.presentingViewController) {// This is a modal viewContoller} else {// This is a normal ViewController}
mashdup

2
IMHO นี่เป็นคำตอบเดียวที่ถูกต้องที่นี่ เพียงตรวจสอบว่ามีไฟล์presentingViewController. นอกจากนี้ยังทำงานในตัวควบคุมมุมมองคอนเทนเนอร์ด้วยเนื่องจากจะข้ามผ่านบรรพบุรุษโดยอัตโนมัติ
Daniel Rinser

17

หากไม่มีคุณสามารถกำหนดคุณสมบัติสำหรับสิ่งนี้ ( presentedAsModal) ในคลาสย่อย UIViewController ของคุณและตั้งค่าเป็นYESก่อนนำเสนอ ViewController เป็นมุมมองโมดอล

childVC.presentedAsModal = YES;
[parentVC presentModalViewController:childVC animated:YES];

คุณสามารถตรวจสอบค่านี้ได้ในการviewWillAppearแทนที่ของคุณ

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


RIght และนี่คือสิ่งที่ฉันทำไปแล้ว แต่ฉันกำลังมองหาวิธีแก้ปัญหาอื่น ๆ ที่เป็นระเบียบ ขอบคุณ
lukewar

โซลูชันนี้ใช้ไม่ได้หากคุณกำลังนำเสนอUINavigationControllerเป็นโมดอล ... เว้นแต่คุณจะสร้างตัวควบคุมการนำทางแบบกำหนดเองเพียงเพื่อเพิ่มคุณสมบัตินี้ และหลังจากนั้นภายในคอนโทรลเลอร์คุณจะต้องแคสต์ไปself.navigationControllerยังคลาสที่กำหนดเองนี้ทุกครั้งที่คุณต้องตรวจสอบว่าคอนโทรลเลอร์ถูกนำเสนอเป็นโมดอลหรือไม่
เฟลิเป้ซาบิโน

8

คำตอบของ Petronella ใช้ไม่ได้หากมีการนำเสนอ self.navigationController แต่ self ไม่เท่ากับ self.navigationController.viewControllers [0] ในกรณีนั้นด้วยตนเองจะถูกผลัก

นี่คือวิธีที่คุณสามารถแก้ไขปัญหาได้

return self.presentingViewController.presentedViewController == self
            || (self.navigationController != nil && self.navigationController.presentingViewController.presentedViewController == self.navigationController && self == self.navigationController.viewControllers[0])
            || [self.tabBarController.presentingViewController isKindOfClass:[UITabBarController class]];

และใน Swift:

return self.presentingViewController?.presentedViewController == self
        || (self.navigationController != nil && self.navigationController?.presentingViewController?.presentedViewController == self.navigationController && self.navigationController?.viewControllers[0] == self)
        || self.tabBarController?.presentingViewController is UITabBarController

6

สิ่งนี้ควรใช้งานได้

if(self.parentViewController.modalViewController == self)…

น่าเสียดายที่ไม่ได้ผล นี่เป็นการลองครั้งแรกของฉัน แต่ส่งคืน modalViewController ins nil :(
lukewar

หากคุณได้รับ 'self.parentViewController' มันจะส่งคืนอ็อบเจ็กต์หลักที่ถูกต้องหรือไม่?
kubi

4
ปัญหาอาจเกิดจากคลาสย่อย UIViewController ของคุณอยู่ภายใน UINavigationController หรือ UITabBarController (หรือทั้งสองอย่าง) ซึ่งในกรณีนี้คุณอาจต้องขุดลึกลงไปอีกเล็กน้อยในลำดับชั้นมุมมองเพื่อค้นหาพาเรนต์ที่ถูกนำเสนอเป็นตัวควบคุมมุมมองโมดอล
hpique

@hgpc ฉันต้องการ chck นี้ในโครงการของฉันดังนั้นฉันจึงเพิ่งเพิ่มคำตอบเพื่อตรวจสอบทั้งสองUINavigationControllerและUITabBarControllerกรณี ตอนนี้ใช้งานได้ดี
Felipe Sabino


2

หากคุณไม่จำเป็นต้องแยกความแตกต่างระหว่างมุมมองโมดอลแบบเต็มหน้าจอและมุมมองที่ไม่ใช่โมดอลซึ่งเป็นกรณีในโครงการของฉัน (ฉันกำลังจัดการกับปัญหาที่เกิดขึ้นกับแผ่นฟอร์มและแผ่นหน้าเท่านั้น) คุณสามารถใช้ modalPresentationStyle คุณสมบัติของ UIViewController:

switch (self.modalPresentationStyle) {
    case 0: NSLog(@"full screen, or not modal"); break;
    case 1: NSLog(@"page sheet"); break;
    case 2: NSLog(@"form sheet"); break;
}

2

ในSwift :

func isUIViewControllerPresentedAsModal() -> Bool {
    if((self.presentingViewController) != nil) {
        return true
    }

    if(self.presentingViewController?.presentedViewController == self) {
        return true
    }

    if(self.navigationController?.presentingViewController?.presentedViewController == self.navigationController) {
        return true
    }

    if((self.tabBarController?.presentingViewController?.isKindOfClass(UITabBarController)) != nil) {
        return true
    }

    return false
}

มีปัญหากับกรณีการใช้งานนี้ ถ้าฉันอยู่ในตัวควบคุมมุมมองรูทของ UINavigationController มันยังคงส่งคืนจริงโดยไม่มีการนำเสนอโมดอล
mariusLAN

1
คำสั่ง if แรกครอบคลุมทุกอย่างที่อยู่ในคำสั่ง if ที่สองการแสดงผลคำสั่งที่สองซ้ำซ้อน ผมไม่แน่ใจว่าที่นี่มีเจตนาอะไร
isoiphone

1

ในโปรเจ็กต์ของฉันฉันมีตัวควบคุมมุมมอง (รายละเอียด) ที่สามารถนำเสนอได้ทั้งแบบโมดูล (เมื่อเพิ่มรายการใหม่) หรือด้วยการกด (เมื่อแก้ไขรายการที่มีอยู่) โดยตัวควบคุมมุมมองหลัก เมื่อผู้ใช้แตะ [เสร็จสิ้น] ตัวควบคุมมุมมองรายละเอียดจะเรียกใช้เมธอดของ Master view controller เพื่อแจ้งว่าพร้อมที่จะปิด อาจารย์ต้องกำหนดว่าจะนำเสนอ Detail อย่างไรจึงจะรู้ว่าจะปิดอย่างไร นี่คือวิธีที่ฉันทำ:

UIViewController *vc = self.navigationController.viewControllers.lastObject;
if (vc == self) {
    [self dismissViewControllerAnimated:YES completion:NULL];
} else {
    [self.navigationController popViewControllerAnimated:YES];
}

0

การแฮ็กเช่นนี้อาจใช้ได้ผล

UIViewController* child = self;
UIViewController* parent = child.parentViewController;
while (parent && parent.modalViewController != child) {
    child = parent;
    parent = child.parentViewController;
}
if (parent) {
    // A view controller in the hierarchy was presented as a modal view controller
}

อย่างไรก็ตามฉันคิดว่าคำตอบก่อนหน้านี้เป็นวิธีที่สะอาดกว่า


0

สิ่งที่ได้ผลสำหรับฉันมีดังต่อไปนี้:

// this is the trick: set parent view controller as application's window root view controller
UIApplication.sharedApplication.delegate.window.rootViewController = viewController;

// assert no modal view is presented
XCTAssertNil(viewController.presentedViewController);

// simulate button tap which shows modal view controller
[viewController.deleteButton sendActionsForControlEvents:UIControlEventTouchUpInside];

// assert that modal view controller is presented
XCTAssertEqualObjects(viewController.presentedViewController.class, MyModalViewController.class);

เท่าที่ฉันทดสอบมันใช้ได้กับ iOS7 และ iOS8 ไม่ได้ลองใช้ iOS6 อย่างไรก็ตาม


0

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

- (BOOL)isModal {
    BOOL modal = NO;
    if ([self presentingViewController]) { //Some view Controller is presenting the current stack
        UIViewController *presented = [[self presentingViewController] presentedViewController]; // What's been presented
        if ([presented respondsToSelector:@selector(viewControllers)]) { // There's a stack
            NSArray *viewControllers = [presented performSelector:@selector(viewControllers)];
            modal = [viewControllers firstObject] == self; // Current VC is presented modally if it's the first in the stack
        }
        else {
            modal = presented == self; // Don't think this is actually needed. set modal = YES should do the job tho.
        }
    }
    return modal;
}

หวังว่าจะช่วยได้


0

นี่คือ @ GabrielePetronella's เวอร์ชันแก้ไขของฉันisModalซึ่งทำงานร่วมกับตัวควบคุมมุมมองที่มีอยู่ซึ่งจะเดินตามลำดับชั้น parentViewController ก่อน ยังดึงโค้ดออกเป็นหลายบรรทัดเพื่อให้ชัดเจนว่ากำลังทำอะไร

var isModal: Bool {
    // If we are a child view controller, we need to check our parent's presentation
    // rather than our own.  So walk up the chain until we don't see any parentViewControllers
    var potentiallyPresentedViewController : UIViewController = self
    while (potentiallyPresentedViewController.parentViewController != nil) {
        potentiallyPresentedViewController = potentiallyPresentedViewController.parentViewController!
    }

    if self.presentingViewController?.presentedViewController == potentiallyPresentedViewController {
        return true
    }

    if let navigationController = potentiallyPresentedViewController.navigationController {
        if navigationController.presentingViewController?.presentedViewController == navigationController {
            return true
        }
    }

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