Apakah mungkin untuk menentukan apakah ViewController disajikan sebagai Modal?

117

Apakah mungkin untuk memeriksa di dalam kelas ViewController yang disajikan sebagai pengontrol tampilan modal?

lukewar
sumber

Jawaban:

96

Karena modalViewControllersudah tidak digunakan lagi di iOS 6, berikut adalah versi yang berfungsi untuk iOS 5+ dan yang terkompilasi tanpa peringatan.

Objective-C:

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

Cepat:

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

Tip topi untuk jawaban Felipe.

Gabriele Petronella
sumber
2
tangkapan bagus, saya hanya harus menggunakannya lagi setelah sekian lama dan menyadari bahwa penghentian itu terjadi ... Saya mengedit jawaban saya sehingga orang-orang mulai mencari di sini untuk kode yang benar saat menggunakan iOS 6+, terima kasih
Felipe Sabino
10
Tidak berfungsi jika pengontrol tampilan induk adalah modal tempat pengontrol tampilan kami didorong.
arti-penting
2
Ada bug, kita harus memeriksa apakah kedua sisi nihil, karena nil == nilmengembalikan YES, dan itu bukan hasil yang kita inginkan.
CocoaBob
1
@GabrielePetronella Apakah Anda keberatan jika saya memperbarui jawaban untuk juga menyertakan implementasi metode Swift?
Air Terjun Michael
1
@MichaelWaterfall yang akan sangat dihargai, terima kasih
Gabriele Petronella
77

Jika Anda mencari iOS 6+, jawaban ini sudah usang dan Anda harus memeriksa jawaban Gabriele Petronella


Tidak ada cara yang tepat untuk melakukan itu, sebagai properti atau metode asli UIKit. Yang dapat Anda lakukan adalah memeriksa beberapa aspek pengontrol Anda untuk memastikannya disajikan sebagai modal.

Jadi, untuk memeriksa apakah pengontrol saat ini (direpresentasikan seperti selfpada kode di bawah) disajikan dengan cara modal atau tidak, saya memiliki fungsi di bawah ini baik dalam UIViewControllerkategori, atau (jika proyek Anda tidak perlu menggunakan pengontrol UIKit lainnya, seperti UITableViewControllermisalnya) dalam pengontrol dasar yang diwarisi oleh pengontrol saya yang lain

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

}

EDIT: Saya menambahkan pemeriksaan terakhir untuk melihat apakah UITabBarController sedang digunakan, dan Anda menyajikan UITabBarController lain sebagai modal.

EDIT 2: menambahkan cek iOS 5+, di mana UIViewControllertidak menjawab parentViewControllerlagi, tetapi presentingViewControllersebagai gantinya.

EDIT 3: Saya telah membuat intinya untuk berjaga-jaga jika https://gist.github.com/3174081

Felipe Sabino
sumber
Perlu diingat bahwa modalViewControllerproperti ini tidak digunakan lagi mulai iOS 6. Dokumentasi menyarankan untuk digunakan presentedViewController.
Bart Jacobs
@BartJobs poin yang bagus! Saya belum melihat jawaban ini setelah rilis iOS6, jadi mungkin jawaban ini tidak up-to-date. Saya akan mencoba membuat beberapa tes di minggu ini untuk memperbaruinya, tks!
Felipe Sabino
NSLog(@"%@", self.navigationController.parentViewController)cetakan (null)- bisakah Anda menjelaskan mengapa? ViewController saya terhubung dengan pengontrol tampilan modal melalui navController di storyboard.
Roman
@oyatek dapatkah Anda menggunakan pastebin atau yang serupa dan menunjukkan beberapa kode?
Felipe Sabino
@Feilpe Saya menemukan masalah - .parentViewControllersudah usang, .presentingViewControllerharus digunakan sebagai gantinya.
Roman
35

Di iOS5 +, Seperti yang Anda lihat di Referensi Kelas UIViewController , Anda bisa mendapatkannya dari properti "presentingViewController".

presentingViewController Pengontrol tampilan yang menyajikan pengontrol tampilan ini. (hanya baca)

@property (nonatomik, hanya baca) UIViewController * Diskusi presentingViewController

Jika pengontrol tampilan yang menerima pesan ini disajikan oleh pengontrol tampilan lain, properti ini menahan pengontrol tampilan yang mempresentasikannya. Jika pengontrol tampilan tidak disajikan, tetapi salah satu pendahulunya disajikan, properti ini menahan pengontrol tampilan yang menyajikan leluhur terdekat. Jika tidak ada pengontrol tampilan atau salah satu leluhurnya yang disajikan, properti ini memegang nol.

Ketersediaan
Tersedia di iOS 5.0 dan lebih baru.
Dinyatakan Dalam
UIViewController.h

Raj
sumber
3
Bekerja dengan sempurna, gunakan if (self.presentingViewController) {// Ini adalah modal viewContoller} else {// Ini adalah ViewController normal}
mashdup
2
IMHO, ini satu - satunya jawaban yang benar di sini. Periksa saja keberadaan file presentingViewController. Ini juga akan bekerja di pengontrol tampilan kontainer, karena secara otomatis melintasi leluhur.
Daniel Rinser
17

Jika tidak ada, Anda bisa mendefinisikan properti untuk this ( presentedAsModal) di subkelas UIViewController Anda dan menyetelnya ke YESsebelum menyajikan ViewController sebagai tampilan modal.

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

Anda dapat memeriksa nilai ini di viewWillAppearoverride Anda .

Saya yakin tidak ada properti resmi yang menyatakan bagaimana tampilan tersebut disajikan, tetapi tidak ada yang menghalangi Anda untuk membuatnya sendiri.

hpique
sumber
Benar dan inilah yang telah saya lakukan tetapi saya sedang mencari solusi lain yang rapi. Terima kasih.
lukewar
solusi ini tidak berfungsi jika Anda menampilkan UINavigationControllersebagai modal ... kecuali Anda membuat pengontrol navigasi kustom hanya untuk menambahkan properti ini. Dan setelah itu, di dalam pengontrol, Anda harus terus mentransmisikan self.navigationControllerke kelas khusus ini setiap kali Anda perlu memeriksa apakah pengontrol disajikan sebagai modal
Felipe Sabino
8

Jawaban Petronella tidak berfungsi jika self.navigationController disajikan secara sederhana tetapi self tidak sama dengan self.navigationController.viewControllers [0], dalam hal ini self didorong.

Inilah cara Anda memperbaiki masalah.

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]];

Dan di 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
Semih Cihan
sumber
6

Ini seharusnya berhasil.

if(self.parentViewController.modalViewController == self)…
kubi
sumber
Sayangnya ini tidak berhasil. Ini adalah percobaan pertama saya. Tapi mengembalikan modalViewController in nil :(.
lukewar
Jika Anda hanya mendapatkan 'self.parentViewController' apakah itu mengembalikan objek induk yang benar?
kubi
4
Masalahnya mungkin subkelas UIViewController Anda berada di dalam UINavigationController atau UITabBarController (atau keduanya), dalam hal ini Anda mungkin perlu menggali lebih banyak dalam hierarki tampilan untuk mengetahui induk yang disajikan sebagai pengontrol tampilan modal.
hpique
@hgpc Saya membutuhkan chck ini dalam proyek saya, jadi saya hanya menambahkan jawaban untuk memeriksa keduanya UINavigationControllerdan UITabBarControllerkasus. Ini bekerja dengan cukup baik sejauh ini
Felipe Sabino
4

Cara terbaik untuk memeriksanya

 if (self.navigationController.presentingViewController) {
         NSLog(@"Model Present");
    }
Sunny Shah
sumber
2

Jika Anda tidak perlu membedakan antara tampilan modal layar penuh dan tampilan non-modal, yang merupakan kasus dalam proyek saya (saya berurusan dengan masalah yang hanya terjadi dengan lembar formulir dan lembar halaman), Anda dapat menggunakan modalPresentationStyle properti 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;
}
arlomedia
sumber
2

Di 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
}
Raja-Penyihir
sumber
Ada Masalah dengan kasus penggunaan ini. Jika saya dalam pengontrol tampilan root dari UINavigationController, ia masih mengembalikan nilai true tanpa presentasi modal apa pun.
mariusLAN
1
Pernyataan if pertama mencakup semua yang ada di pernyataan if kedua, membuat pernyataan kedua menjadi berlebihan. Saya tidak yakin apa maksudnya di sini.
isoiphone
1

Dalam proyek saya, saya memiliki pengontrol tampilan (Detail) yang dapat disajikan baik secara sederhana (saat menambahkan item baru) atau dengan push (saat mengedit yang sudah ada) oleh pengontrol tampilan Master. Ketika pengguna mengetuk [Selesai], pengontrol tampilan Detail memanggil metode pengontrol tampilan Master untuk memberi tahu bahwa itu siap untuk ditutup. Guru harus menentukan bagaimana Detail disajikan untuk mengetahui bagaimana menutupnya. Beginilah cara saya melakukan ini:

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

Peretasan seperti ini mungkin berhasil.

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
}

Namun, saya pikir jawaban saya sebelumnya adalah solusi yang lebih bersih.

hpique
sumber
0

Apa yang berhasil untuk saya adalah sebagai berikut:

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

Sejauh saya mengujinya, ini berfungsi untuk iOS7 dan iOS8. Namun tidak mencoba di iOS6.

mixtly87
sumber
0

Saya telah melihat-lihat untuk menemukan jawaban yang tepat untuk pertanyaan ini, dan saya tidak dapat menemukan jawaban yang mencakup semua kemungkinan skenario. Saya menulis beberapa baris kode ini yang tampaknya berhasil. Anda dapat menemukan beberapa komentar sebaris untuk mencari tahu apa yang telah diperiksa.

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

Semoga bantuan ini.

DennyLou
sumber
0

Inilah versi modifikasi saya dari @ GabrielePetronella's isModal, yang berfungsi dengan pengontrol tampilan yang ada di dalamnya yang berjalan naik hierarki parentViewController terlebih dahulu. Juga tarik kode menjadi beberapa baris sehingga jelas apa yang dilakukannya.

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
}
Ryan
sumber