जब व्यू कंट्रोलर में UIAlertController नहीं है तो कैसे प्रस्तुत करें?


255

परिदृश्य: उपयोगकर्ता एक बटन पर एक दृश्य नियंत्रक पर टैप करता है। नेविगेशन स्टैक में दृश्य नियंत्रक सबसे ऊपर (स्पष्ट रूप से) है। नल एक उपयोगिता वर्ग विधि का आह्वान करता है जिसे अन्य कक्षा में बुलाया जाता है। एक बुरी चीज वहां होती है और मैं कंट्रोल कंट्रोलर को व्यू कंट्रोलर के सामने एक अलर्ट दिखाना चाहता हूं।

+ (void)myUtilityMethod {
    // do stuff
    // something bad happened, display an alert.
}

यह संभव था UIAlertView(लेकिन शायद बिल्कुल उचित नहीं)।

इस मामले में, आप कैसे एक को प्रस्तुत करते हैं UIAlertController, वहीं myUtilityMethod?

जवाबों:


34

मैंने कुछ महीने पहले इसी तरह का प्रश्न पोस्ट किया था और मुझे लगता है कि मैंने समस्या को हल कर लिया है। यदि आप कोड देखना चाहते हैं तो मेरी पोस्ट के नीचे दिए गए लिंक का अनुसरण करें।

समाधान एक अतिरिक्त UIWindow का उपयोग करना है।

जब आप अपना UIAlertController प्रदर्शित करना चाहते हैं:

  1. अपनी विंडो को कुंजी और दृश्यमान विंडो बनाएं ( window.makeKeyAndVisible())
  2. बस नई विंडो के rootViewController के रूप में एक सादे UIViewController उदाहरण का उपयोग करें। ( window.rootViewController = UIViewController())
  3. अपनी विंडो के rootViewController पर अपना UIAlertController प्रस्तुत करें

नोट करने के लिए कुछ बातें:

  • आपके UIWindow को दृढ़ता से संदर्भित किया जाना चाहिए। यदि यह दृढ़ता से संदर्भित नहीं है, तो यह कभी नहीं दिखाई देगा (क्योंकि यह जारी किया गया है)। मैं एक संपत्ति का उपयोग करने की सलाह देता हूं, लेकिन मुझे एक संबद्ध वस्तु के साथ सफलता भी मिली है ।
  • यह सुनिश्चित करने के लिए कि विंडो सब कुछ ऊपर दिखाई देती है (सिस्टम UIAlertControllers सहित), मैं windowLevel सेट करता हूं। ( window.windowLevel = UIWindowLevelAlert + 1)

अंत में, मेरे पास एक पूर्ण कार्यान्वयन है यदि आप बस उसी को देखना चाहते हैं।

https://github.com/dbettermann/DBAlertController


आपके पास यह उद्देश्य-सी के लिए नहीं है, क्या आप?
SAHM

2
हां, यह स्विफ्ट 2.0 / आईओएस 9 में भी काम करता है। मैं अभी एक उद्देश्य-सी संस्करण पर काम कर रहा हूं, क्योंकि किसी और ने इसके लिए पूछा (शायद यह आप थे)। जब मैं करूंगा तब पोस्ट करूंगा।
डायलन बेटरमैन

322

WWDC में, मैं एक प्रयोगशाला में रुक गया और एक Apple इंजीनियर से यही सवाल पूछा: "प्रदर्शित करने के लिए सबसे अच्छा अभ्यास क्या था UIAlertController?" और उन्होंने कहा कि उन्हें यह सवाल बहुत हो रहा था और हमने मजाक में कहा कि उन्हें इस पर एक सत्र करना चाहिए था। उन्होंने कहा कि आंतरिक रूप से Apple UIWindowएक पारदर्शी UIViewControllerऔर फिर उस UIAlertControllerपर प्रस्तुति दे रहा है। मूल रूप से डायलन बेटरमैन के जवाब में क्या है।

लेकिन मैं एक उपवर्ग का उपयोग नहीं करना चाहता था, UIAlertControllerक्योंकि मुझे अपने ऐप में अपना कोड बदलने की आवश्यकता होगी। तो एक संबद्ध वस्तु की मदद से, मैंने UIAlertControllerउस पर एक श्रेणी बनाई जो कि showउद्देश्य-सी में एक विधि प्रदान करती है।

यहाँ प्रासंगिक कोड है:

#import "UIAlertController+Window.h"
#import <objc/runtime.h>

@interface UIAlertController (Window)

- (void)show;
- (void)show:(BOOL)animated;

@end

@interface UIAlertController (Private)

@property (nonatomic, strong) UIWindow *alertWindow;

@end

@implementation UIAlertController (Private)

@dynamic alertWindow;

- (void)setAlertWindow:(UIWindow *)alertWindow {
    objc_setAssociatedObject(self, @selector(alertWindow), alertWindow, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

- (UIWindow *)alertWindow {
    return objc_getAssociatedObject(self, @selector(alertWindow));
}

@end

@implementation UIAlertController (Window)

- (void)show {
    [self show:YES];
}

- (void)show:(BOOL)animated {
    self.alertWindow = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
    self.alertWindow.rootViewController = [[UIViewController alloc] init];

    id<UIApplicationDelegate> delegate = [UIApplication sharedApplication].delegate;
    // Applications that does not load with UIMainStoryboardFile might not have a window property:
    if ([delegate respondsToSelector:@selector(window)]) {
        // we inherit the main window's tintColor
        self.alertWindow.tintColor = delegate.window.tintColor;
    }

    // window level is above the top window (this makes the alert, if it's a sheet, show over the keyboard)
    UIWindow *topWindow = [UIApplication sharedApplication].windows.lastObject;
    self.alertWindow.windowLevel = topWindow.windowLevel + 1;

    [self.alertWindow makeKeyAndVisible];
    [self.alertWindow.rootViewController presentViewController:self animated:animated completion:nil];
}

- (void)viewDidDisappear:(BOOL)animated {
    [super viewDidDisappear:animated];
    
    // precaution to ensure window gets destroyed
    self.alertWindow.hidden = YES;
    self.alertWindow = nil;
}

@end

यहाँ एक नमूना उपयोग है:

// need local variable for TextField to prevent retain cycle of Alert otherwise UIWindow
// would not disappear after the Alert was dismissed
__block UITextField *localTextField;
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Global Alert" message:@"Enter some text" preferredStyle:UIAlertControllerStyleAlert];
[alert addAction:[UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
    NSLog(@"do something with text:%@", localTextField.text);
// do NOT use alert.textfields or otherwise reference the alert in the block. Will cause retain cycle
}]];
[alert addTextFieldWithConfigurationHandler:^(UITextField *textField) {
    localTextField = textField;
}];
[alert show];

यह UIWindowबनाया गया UIAlertControllerहै, जब यह निपटाया जाता है तब नष्ट हो जाएगा , क्योंकि यह एकमात्र वस्तु है जो इसे बनाए रख रही है UIWindow। लेकिन अगर आप UIAlertControllerकिसी प्रॉपर्टी को असाइन करते हैं या एक्शन ब्लॉक में से किसी एक में अलर्ट एक्सेस करके इसकी रिटेन काउंट बढ़ाने का कारण बनते हैं, तो UIWindowस्क्रीन पर रहेगा, आपके यूआई को लॉक करेगा। उपयोग करने की आवश्यकता के मामले में बचने के लिए ऊपर का नमूना उपयोग कोड देखें UITextField

मैं एक परीक्षण परियोजना के साथ एक GitHub रेपो बनाया: FFGlobalAlertController


1
अच्छी चीज़! बस कुछ पृष्ठभूमि - मैंने एक संबद्ध वस्तु के बजाय एक उपवर्ग का उपयोग किया क्योंकि मैं स्विफ्ट का उपयोग कर रहा था। एसोसिएटेड ऑब्जेक्ट्स ऑब्जेक्टिव-सी रनटाइम की एक विशेषता है और मैं इस पर निर्भर नहीं रहना चाहता। स्विफ्ट शायद सालों से दूर है क्योंकि यह खुद का रनटाइम है, लेकिन फिर भी। :)
डायलन बेटरमैन

1
मैं वास्तव में आपके उत्तर की लालित्य को पसंद करता हूं, हालांकि मुझे उत्सुकता है कि आप नई विंडो को कैसे रिटायर करते हैं और मूल विंडो को फिर से कुंजी बनाते हैं (वास्तव में मैं खिड़की के साथ आसपास नहीं घूमता हूं)।
डस्टिन पफेननस्टियल

1
की विंडो सबसे ऊपरी दिखाई देने वाली विंडो है, इसलिए मेरी समझ यह है कि यदि आप "की" विंडो को हटाते / छिपाते हैं, तो अगली दिखाई जाने वाली विंडो नीचे "की" हो जाती है।
चपलता

19
viewDidDisappear:एक श्रेणी पर लागू करना एक खराब विचार की तरह लगता है। संक्षेप में, आप ढांचे के कार्यान्वयन के साथ प्रतिस्पर्धा कर रहे हैं viewDidDisappear:। अभी के लिए यह ठीक हो सकता है, लेकिन अगर Apple भविष्य में उस पद्धति को लागू करने का निर्णय लेता है, तो आपके लिए इसे कॉल करने का कोई तरीका नहीं है (अर्थात superश्रेणी कार्यान्वयन से किसी विधि के प्राथमिक कार्यान्वयन के लिए उस बिंदु का कोई भी समान नहीं है ) ।
adib

5
महान है, लेकिन कैसे इलाज करने के लिए काम करता है prefersStatusBarHiddenऔर preferredStatusBarStyleएक अतिरिक्त उपवर्ग के बिना?
केविन फ्लैशमैन

109

तीव्र

let alertController = UIAlertController(title: "title", message: "message", preferredStyle: .alert)
//...
var rootViewController = UIApplication.shared.keyWindow?.rootViewController
if let navigationController = rootViewController as? UINavigationController {
    rootViewController = navigationController.viewControllers.first
}
if let tabBarController = rootViewController as? UITabBarController {
    rootViewController = tabBarController.selectedViewController
}
//...
rootViewController?.present(alertController, animated: true, completion: nil)

उद्देश्य सी

UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"Title" message:@"message" preferredStyle:UIAlertControllerStyleAlert];
//...
id rootViewController = [UIApplication sharedApplication].delegate.window.rootViewController;
if([rootViewController isKindOfClass:[UINavigationController class]])
{
    rootViewController = ((UINavigationController *)rootViewController).viewControllers.firstObject;
}
if([rootViewController isKindOfClass:[UITabBarController class]])
{
    rootViewController = ((UITabBarController *)rootViewController).selectedViewController;
}
//...
[rootViewController presentViewController:alertController animated:YES completion:nil];

2
+1 यह एक शानदार सरल उपाय है। (समस्या मैंने सामना की: मास्टर / डिटेल टेम्पलेट के डिटेल व्यू कंट्रोलर में अलर्ट प्रदर्शित करना - आईपैड पर शो, आईफोन पर कभी नहीं)
डेविड

8
अच्छा, आप किसी अन्य भाग में जोड़ना चाह सकते हैं: यदि (rootViewController.presentedViewController! = Nil) {rootViewController = rootViewController.presentedViewController; }
DivideByZer0

1
स्विफ्ट 3: 'अलर्ट' का नाम बदलकर 'अलर्ट' कर दिया गया है: letContController = UIAlertController (शीर्षक: "शीर्षक", संदेश: "संदेश", अधिमान्य: .आलर्ट)
Kaptain

इसके बजाय एक प्रतिनिधि का उपयोग करें!
एंड्रयू कर्ण

104

आप स्विफ्ट 2.2 के साथ निम्नलिखित कर सकते हैं:

let alertController: UIAlertController = ...
UIApplication.sharedApplication().keyWindow?.rootViewController?.presentViewController(alertController, animated: true, completion: nil)

और स्विफ्ट 3.0:

let alertController: UIAlertController = ...
UIApplication.shared.keyWindow?.rootViewController?.present(alertController, animated: true, completion: nil)

12
उफ़, मैंने जाँच करने से पहले स्वीकार कर लिया। वह कोड रूट व्यू कंट्रोलर लौटाता है, जो मेरे मामले में नेविगेशन कंट्रोलर है। यह एक त्रुटि का कारण नहीं बनता है लेकिन अलर्ट प्रदर्शित नहीं करता है।
मुरैना सागर

22
और मैंने कंसोल में देखा Warning: Attempt to present <UIAlertController: 0x145bfa30> on <UINavigationController: 0x1458e450> whose view is not in the window hierarchy!:।
मुरैना सागर

1
@MurraySagal एक नेविगेशन नियंत्रक होने से आप visibleViewControllerकिसी भी समय संपत्ति प्राप्त कर सकते हैं यह देखने के लिए कि नियंत्रक क्या चेतावनी प्रस्तुत करता है। डॉक्स की
लुबो

2
मैंने ऐसा इसलिए किया क्योंकि मैं किसी और के काम का क्रेडिट नहीं लेना चाहता। यह @ZevEisenberg का समाधान था जिसे मैंने स्विफ्ट 3.0 के लिए संशोधित किया था। अगर मैं एक और जवाब जोड़ देता तो मुझे वोट अप मिल जाता, जिसके वो हकदार थे।
jeet.chanchawat

1
ओह हे, मैं कल सभी नाटक करने से चूक गया, लेकिन मुझे लगता है कि स्विफ्ट 3 के लिए पोस्ट को अभी अपडेट किया गया है। मुझे नहीं पता कि एसओ की नीति नए भाषा संस्करणों के लिए पुराने उत्तरों को अपडेट करने पर क्या है, लेकिन मैं व्यक्तिगत रूप से इसका बुरा नहीं मानता, जब तक उत्तर सही है!
ज़ेव ईसेनबर्ग

34

और / या के UIAlertController extensionसभी मामलों के लिए सुंदर सामान्यUINavigationControllerUITabBarController । इसके अलावा अगर स्क्रीन पर एक मोडल वीसी हो तो यह काम करता है।

उपयोग:

//option 1:
myAlertController.show()
//option 2:
myAlertController.present(animated: true) {
    //completion code...
}

यह विस्तार है:

//Uses Swift1.2 syntax with the new if-let
// so it won't compile on a lower version.
extension UIAlertController {

    func show() {
        present(animated: true, completion: nil)
    }

    func present(#animated: Bool, completion: (() -> Void)?) {
        if let rootVC = UIApplication.sharedApplication().keyWindow?.rootViewController {
            presentFromController(rootVC, animated: animated, completion: completion)
        }
    }

    private func presentFromController(controller: UIViewController, animated: Bool, completion: (() -> Void)?) {
        if  let navVC = controller as? UINavigationController,
            let visibleVC = navVC.visibleViewController {
                presentFromController(visibleVC, animated: animated, completion: completion)
        } else {
          if  let tabVC = controller as? UITabBarController,
              let selectedVC = tabVC.selectedViewController {
                presentFromController(selectedVC, animated: animated, completion: completion)
          } else {
              controller.presentViewController(self, animated: animated, completion: completion)
          }
        }
    }
}

1
मैं इस समाधान का उपयोग कर रहा था, और मैंने पाया कि यह वास्तव में सही, सुरुचिपूर्ण, साफ है ... लेकिन, हाल ही में मुझे अपने रूट व्यू कंट्रोलर को दृश्य पदानुक्रम में नहीं दृश्य में बदलना पड़ा, इसलिए यह कोड बेकार हो गया। किसी को भी इसे रखने के लिए एक डिक्स के बारे में सोच?

1
मैं और sometinhg के साथ इस समाधान के संयोजन का उपयोग: मैं एक सिंगलटन है UI(! कमजोर) वर्ग है जो एक रखती currentVCप्रकार के UIViewController.मैं है BaseViewControllerजो inherits से UIViewControllerऔर सेट UI.currentVCकरने के लिए selfपर viewDidAppearकरने के लिए तो nilपर viewWillDisappear। ऐप में मेरे सभी व्यू कंट्रोलर इनहेरिट करते हैं BaseViewController। इस तरह अगर आपके पास कुछ है UI.currentVC(यह नहीं है nil...) - यह निश्चित रूप से एक प्रस्तुति एनीमेशन के बीच में नहीं है, और आप इसे अपने पेश करने के लिए कह सकते हैं UIAlertController
अवीएल ग्रॉस

1
नीचे के अनुसार, रूट व्यू कंट्रोलर किसी सेगमेंट के साथ कुछ प्रस्तुत कर सकता है, जिस स्थिति में आपका अंतिम यदि स्टेटमेंट विफल रहता है, तो मुझे जोड़ना होगा else { if let presentedViewController = controller.presentedViewController { presentedViewController.presentViewController(self, animated: animated, completion: completion) } else { controller.presentViewController(self, animated: animated, completion: completion) } }
निकल्स

27

चपलता के जवाब में सुधार करते हुए , आपको एक पारदर्शी रूट व्यू कंट्रोलर के साथ एक विंडो बनाने और वहां से अलर्ट दृश्य प्रस्तुत करने की आवश्यकता होगी।

हालाँकि जब तक आपके अलर्ट कंट्रोलर में कोई एक्शन होता है, आपको विंडो का संदर्भ रखने की आवश्यकता नहीं होती है । एक्शन हैंडलर ब्लॉक के अंतिम चरण के रूप में, आपको बस सफाई कार्य के हिस्से के रूप में विंडो को छिपाने की आवश्यकता है। हैंडलर ब्लॉक में विंडो का संदर्भ होने से, यह एक अस्थायी परिपत्र संदर्भ बनाता है जिसे अलर्ट नियंत्रक को खारिज करने के बाद तोड़ दिया जाएगा।

UIWindow* window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
window.rootViewController = [UIViewController new];
window.windowLevel = UIWindowLevelAlert + 1;

UIAlertController* alertCtrl = [UIAlertController alertControllerWithTitle:... message:... preferredStyle:UIAlertControllerStyleAlert];

[alertCtrl addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"OK",@"Generic confirm") style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
    ... // do your stuff

    // very important to hide the window afterwards.
    // this also keeps a reference to the window until the action is invoked.
    window.hidden = YES;
}]];

[window makeKeyAndVisible];
[window.rootViewController presentViewController:alertCtrl animated:YES completion:nil];

बिल्कुल सही, ठीक यही टिप मुझे खिड़की से खारिज करने की ज़रूरत थी, धन्यवाद दोस्त
थिबुत नूह

25

निम्नलिखित समाधान काम नहीं किया, हालांकि यह सभी संस्करणों के साथ काफी आशाजनक लग रहा था। यह समाधान चेतावनी उत्पन्न कर रहा है

चेतावनी: खिड़की पर पदानुक्रम में किसका दृश्य प्रस्तुत करने का प्रयास नहीं किया गया!

https://stackoverflow.com/a/34487871/2369867 => तब यह आशाजनक लग रहा है। लेकिन यह अंदर नहीं था Swift 3। इसलिए मैं स्विफ्ट 3 में इसका जवाब दे रहा हूं और यह टेम्पलेट उदाहरण नहीं है

जब आप किसी फ़ंक्शन के अंदर पेस्ट करते हैं तो यह अपने आप में पूरी तरह कार्यात्मक कोड होता है।

शीघ्र Swift 3 स्व-निहित कोड

let alertController = UIAlertController(title: "<your title>", message: "<your message>", preferredStyle: UIAlertControllerStyle.alert)
alertController.addAction(UIAlertAction(title: "Close", style: UIAlertActionStyle.cancel, handler: nil))

let alertWindow = UIWindow(frame: UIScreen.main.bounds)
alertWindow.rootViewController = UIViewController()
alertWindow.windowLevel = UIWindowLevelAlert + 1;
alertWindow.makeKeyAndVisible()
alertWindow.rootViewController?.present(alertController, animated: true, completion: nil)

यह स्विफ्ट 3 में परीक्षण और काम करने वाला कोड है।


1
इस कोड ने मेरे लिए पूरी तरह से काम किया, एक संदर्भ में जहां एक UIAlertController को माइग्रेशन समस्या के संबंध में ऐप डेलिगेट में बंद किया जा रहा था, इससे पहले कि कोई रूट व्यू कंट्रोलर लोड किया गया हो। बढ़िया काम किया, कोई चेतावनी नहीं।
डंकन बैबेज

3
बस एक अनुस्मारक: आपको अपने मजबूत संदर्भ को संग्रहीत करने की आवश्यकता है UIWindowया अन्यथा विंडो जारी की जाएगी और दायरे से बाहर जाने के तुरंत बाद गायब हो जाएगी।
सायरन

24

यहां स्विफ्ट 4 में विस्तार, परीक्षण और काम के रूप में पौराणिक कथाओं का उत्तर दिया गया है:

extension UIAlertController {

    func presentInOwnWindow(animated: Bool, completion: (() -> Void)?) {
        let alertWindow = UIWindow(frame: UIScreen.main.bounds)
        alertWindow.rootViewController = UIViewController()
        alertWindow.windowLevel = UIWindowLevelAlert + 1;
        alertWindow.makeKeyAndVisible()
        alertWindow.rootViewController?.present(self, animated: animated, completion: completion)
    }

}

उदाहरण का उपयोग:

let alertController = UIAlertController(title: "<Alert Title>", message: "<Alert Message>", preferredStyle: .alert)
alertController.addAction(UIAlertAction(title: "Close", style: .cancel, handler: nil))
alertController.presentInOwnWindow(animated: true, completion: {
    print("completed")
})

यह तब भी उपयोग किया जा सकता है, अगर साझाकरण सुलभ नहीं है!
अल्फी

20

यह सामान्य दृश्य नियंत्रकों के लिए स्विफ्ट में काम करता है और भले ही स्क्रीन पर एक नेविगेशन नियंत्रक हो:

let alert = UIAlertController(...)

let alertWindow = UIWindow(frame: UIScreen.main.bounds)
alertWindow.rootViewController = UIViewController()
alertWindow.windowLevel = UIWindowLevelAlert + 1;
alertWindow.makeKeyAndVisible()
alertWindow.rootViewController?.presentViewController(alert, animated: true, completion: nil)

1
जब मैं अलर्ट को खारिज करता हूं, तो UIWindowगैर-जिम्मेदार है। windowLevelशायद के साथ कुछ करना है । मैं इसे कैसे उत्तरदायी बना सकता हूं?
स्लाइडर

1
नई विंडो की तरह लगता है खारिज नहीं किया गया था।
इगोर कुलगिन

ऐसा लगता है कि विंडो को ऊपर से हटाया नहीं गया है, इसलिए विंडो को एक बार हटाने की आवश्यकता है।
सून सैनी

जब आप इसके साथ समाप्त कर लें तो अपने alertWindowको सेट करें nil
C6Silver

13

ज़ेव के जवाब में जोड़ना (और ऑब्जेक्टिव-सी पर वापस स्विच करना), आप एक ऐसी स्थिति में भाग सकते हैं जहां आपका रूट व्यू कंट्रोलर किसी अन्य वीसी को एक सेगमेंट या कुछ और के माध्यम से पेश कर रहा है। प्रस्तुत वीसीओ पर कॉल करने पर रूट वीसी इस बात का ध्यान रखेगा:

[[UIApplication sharedApplication].keyWindow.rootViewController.presentedViewController presentViewController:alertController animated:YES completion:^{}];

इसने एक मुद्दे को सीधा किया, जिसमें रूट वीसी ने दूसरे वीसी को सीग किया था, और चेतावनी नियंत्रक को पेश करने के बजाय, ऊपर बताए गए लोगों की तरह एक चेतावनी जारी की गई थी:

Warning: Attempt to present <UIAlertController: 0x145bfa30> on <UINavigationController: 0x1458e450> whose view is not in the window hierarchy!

मैंने इसका परीक्षण नहीं किया है, लेकिन यह तब भी आवश्यक हो सकता है जब आपका रूट वीसी एक नेविगेशन नियंत्रक हो।


हम स्विफ्ट में इस समस्या में चल रहे हैं, और मुझे पता नहीं है कि स्विफ्ट करने के लिए अपने objc कोड का अनुवाद कैसे करें, मदद की बहुत सराहना की जाएगी!

2
@ मेयरेज़ का अनुवाद ऑब्जेक्टिव-सी टू स्विफ्ट इतनी बड़ी बात नहीं होनी चाहिए;) लेकिन यहाँ आप हैं:UIApplication.sharedApplication().keyWindow?.rootViewController?.presentedViewController?.presentViewController(controller, animated: true, completion: nil)
borchero

धन्यवाद ओलिवियर, आप सही हैं, यह पाई के रूप में आसान है, और मैंने इसका इस तरह अनुवाद किया, लेकिन समस्या कहीं और पड़ी थी। फिर भी धन्यवाद!

Attempting to load the view of a view controller while it is deallocating is not allowed and may result in undefined behavior (<UIAlertController: 0x15cd4afe0>)
मोजो 66

2
मैं एक ही दृष्टिकोण के साथ गया था, rootViewController.presentedViewControllerअगर इसका उपयोग शून्य है, अन्यथा उपयोग करें rootViewController। पूरी तरह से सामान्य समाधान के लिए, वीसी presentedViewControllerमें प्राप्त करने के लिए एस की श्रृंखला को चलना आवश्यक हो सकता हैtopmost
प्रोटॉन्गुन

9

@ चपलता का जवाब Swift4 / iOS11 में अनुवादित। मैंने स्थानीयकृत स्ट्रिंग्स का उपयोग नहीं किया है, लेकिन आप इसे आसानी से बदल सकते हैं:

import UIKit

/** An alert controller that can be called without a view controller.
 Creates a blank view controller and presents itself over that
 **/
class AlertPlusViewController: UIAlertController {

    private var alertWindow: UIWindow?

    override func viewDidLoad() {
        super.viewDidLoad()
    }

    override func viewDidDisappear(_ animated: Bool) {
        super.viewDidDisappear(animated)
        self.alertWindow?.isHidden = true
        alertWindow = nil
    }

    func show() {
        self.showAnimated(animated: true)
    }

    func showAnimated(animated _: Bool) {

        let blankViewController = UIViewController()
        blankViewController.view.backgroundColor = UIColor.clear

        let window = UIWindow(frame: UIScreen.main.bounds)
        window.rootViewController = blankViewController
        window.backgroundColor = UIColor.clear
        window.windowLevel = UIWindowLevelAlert + 1
        window.makeKeyAndVisible()
        self.alertWindow = window

        blankViewController.present(self, animated: true, completion: nil)
    }

    func presentOkayAlertWithTitle(title: String?, message: String?) {

        let alertController = AlertPlusViewController(title: title, message: message, preferredStyle: .alert)
        let okayAction = UIAlertAction(title: "Ok", style: .default, handler: nil)
        alertController.addAction(okayAction)
        alertController.show()
    }

    func presentOkayAlertWithError(error: NSError?) {
        let title = "Error"
        let message = error?.localizedDescription
        presentOkayAlertWithTitle(title: title, message: message)
    }
}

मुझे स्वीकृत उत्तर के साथ एक काली पृष्ठभूमि मिल रही थी। window.backgroundColor = UIColor.clearतय हो गया कि viewController.view.backgroundColor = UIColor.clearआवश्यक प्रतीत नहीं होता है।
बेन पैच

ध्यान रखें कि Apple UIAlertControllerउपवर्ग के बारे में चेतावनी देता है : The UIAlertController class is intended to be used as-is and does not support subclassing. The view hierarchy for this class is private and must not be modified. developer.apple.com/documentation/uikit/uialertcontroller
Grubas

6

एविएल ग्रॉस उत्तर की तरह एक्सटेंशन बनाएं। यहां आपके पास ऑब्जेक्टिव-सी एक्सटेंशन है।

यहाँ आपके पास हैडर फ़ाइल * .h है

//  UIAlertController+Showable.h

#import <UIKit/UIKit.h>

@interface UIAlertController (Showable)

- (void)show;

- (void)presentAnimated:(BOOL)animated
             completion:(void (^)(void))completion;

- (void)presentFromController:(UIViewController *)viewController
                     animated:(BOOL)animated
                   completion:(void (^)(void))completion;

@end

और कार्यान्वयन: *। एम

//  UIAlertController+Showable.m

#import "UIAlertController+Showable.h"

@implementation UIAlertController (Showable)

- (void)show
{
    [self presentAnimated:YES completion:nil];
}

- (void)presentAnimated:(BOOL)animated
             completion:(void (^)(void))completion
{
    UIViewController *rootVC = [UIApplication sharedApplication].keyWindow.rootViewController;
    if (rootVC != nil) {
        [self presentFromController:rootVC animated:animated completion:completion];
    }
}

- (void)presentFromController:(UIViewController *)viewController
                     animated:(BOOL)animated
                   completion:(void (^)(void))completion
{

    if ([viewController isKindOfClass:[UINavigationController class]]) {
        UIViewController *visibleVC = ((UINavigationController *)viewController).visibleViewController;
        [self presentFromController:visibleVC animated:animated completion:completion];
    } else if ([viewController isKindOfClass:[UITabBarController class]]) {
        UIViewController *selectedVC = ((UITabBarController *)viewController).selectedViewController;
        [self presentFromController:selectedVC animated:animated completion:completion];
    } else {
        [viewController presentViewController:self animated:animated completion:completion];
    }
}

@end

आप इस तरह से अपनी कार्यान्वयन फ़ाइल में इस एक्सटेंशन का उपयोग कर रहे हैं:

#import "UIAlertController+Showable.h"

UIAlertController* alert = [UIAlertController
    alertControllerWithTitle:@"Title here"
                     message:@"Detail message here"
              preferredStyle:UIAlertControllerStyleAlert];

UIAlertAction* defaultAction = [UIAlertAction
    actionWithTitle:@"OK"
              style:UIAlertActionStyleDefault
            handler:^(UIAlertAction * action) {}];
[alert addAction:defaultAction];

// Add more actions if needed

[alert show];

4

मेरा जवाब क्रॉस करें बाद से इन दो धागे के रूप में ध्वजांकित नहीं हैं ...

अब यह UIViewControllerउत्तरदाता श्रृंखला का हिस्सा है, आप कुछ इस तरह से कर सकते हैं:

if let vc = self.nextResponder()?.targetForAction(#selector(UIViewController.presentViewController(_:animated:completion:)), withSender: self) as? UIViewController {

    let alert = UIAlertController(title: "A snappy title", message: "Something bad happened", preferredStyle: .Alert)
    alert.addAction(UIAlertAction(title: "OK", style: .Default, handler: nil))

    vc.presentViewController(alert, animated: true, completion: nil)
}

4

ज़ेव ईसेनबर्ग का जवाब सरल और सीधा है, लेकिन यह हमेशा काम नहीं करता है, और यह इस चेतावनी संदेश के साथ विफल हो सकता है:

Warning: Attempt to present <UIAlertController: 0x7fe6fd951e10>  
 on <ThisViewController: 0x7fe6fb409480> which is already presenting 
 <AnotherViewController: 0x7fe6fd109c00>

ऐसा इसलिए है क्योंकि विंडोज़ rootViewController प्रस्तुत विचारों के शीर्ष पर नहीं है। इसे ठीक करने के लिए हमें प्रस्तुति श्रृंखला को चलाने की जरूरत है, जैसा कि स्विफ्ट 3 में लिखे मेरे UIAlertController एक्सटेंशन कोड में दिखाया गया है:

   /// show the alert in a view controller if specified; otherwise show from window's root pree
func show(inViewController: UIViewController?) {
    if let vc = inViewController {
        vc.present(self, animated: true, completion: nil)
    } else {
        // find the root, then walk up the chain
        var viewController = UIApplication.shared.keyWindow?.rootViewController
        var presentedVC = viewController?.presentedViewController
        while presentedVC != nil {
            viewController = presentedVC
            presentedVC = viewController?.presentedViewController
        }
        // now we present
        viewController?.present(self, animated: true, completion: nil)
    }
}

func show() {
    show(inViewController: nil)
}

9/15/2017 को अपडेट:

परीक्षण किया और पुष्टि की कि उपरोक्त तर्क अभी भी नए उपलब्ध iOS 11 जीएम बीज में महान काम करता है। चपलता द्वारा शीर्ष मतदान पद्धति, हालांकि, यह नहीं है: एक नए खनन में प्रस्तुत किया गया सतर्क दृश्यUIWindow कीबोर्ड के नीचे है और संभावित रूप से उपयोगकर्ता को इसके बटन टैप करने से रोकता है। ऐसा इसलिए है क्योंकि iOS 11 में कीबोर्ड विंडो की तुलना में उच्चतर सभी विंडोलेवल्स को इसके नीचे के स्तर पर उतारा गया है।

keyWindowहालांकि प्रस्तुत करने की एक कलाकृतियों में कीबोर्ड का एनीमेशन है, जब अलर्ट प्रस्तुत किया जाता है, और अलर्ट खारिज होने पर फिर से फिसलने लगता है। यदि आप चाहते हैं कि कीबोर्ड प्रस्तुति के दौरान वहां रहे, तो आप शीर्ष विंडो से ही प्रस्तुत करने का प्रयास कर सकते हैं, जैसा कि नीचे दिए गए कोड में दिखाया गया है:

func show(inViewController: UIViewController?) {
    if let vc = inViewController {
        vc.present(self, animated: true, completion: nil)
    } else {
        // get a "solid" window with the highest level
        let alertWindow = UIApplication.shared.windows.filter { $0.tintColor != nil || $0.className() == "UIRemoteKeyboardWindow" }.sorted(by: { (w1, w2) -> Bool in
            return w1.windowLevel < w2.windowLevel
        }).last
        // save the top window's tint color
        let savedTintColor = alertWindow?.tintColor
        alertWindow?.tintColor = UIApplication.shared.keyWindow?.tintColor

        // walk up the presentation tree
        var viewController = alertWindow?.rootViewController
        while viewController?.presentedViewController != nil {
            viewController = viewController?.presentedViewController
        }

        viewController?.present(self, animated: true, completion: nil)
        // restore the top window's tint color
        if let tintColor = savedTintColor {
            alertWindow?.tintColor = tintColor
        }
    }
}

उपरोक्त कोड का एकमात्र इतना बड़ा हिस्सा नहीं है कि यह UIRemoteKeyboardWindowसुनिश्चित करने के लिए वर्ग नाम की जांच करता है कि हम इसे भी शामिल कर सकते हैं। फिर भी उपरोक्त कोड आईओएस 9, 10 और 11 जीएम बीज में, सही टिंट रंग के साथ और कीबोर्ड स्लाइडिंग कलाकृतियों के बिना शानदार काम करता है।


बस यहाँ पिछले कई उत्तरों के माध्यम से गया और केविन स्लीच का उत्तर देखा, जो समान दृष्टिकोण के साथ एक ही मुद्दे को हल करने की कोशिश कर रहा है, लेकिन जिसने प्रस्तुति श्रृंखला को चलने से रोक दिया, इस प्रकार इसे उसी त्रुटि के लिए अतिसंवेदनशील बना दिया क्योंकि यह हल करने की कोशिश करता है। ।
CodeBrew

4

स्विफ्ट 4+

समाधान जिसका मैं वर्षों से उपयोग करता हूं जिसमें कोई समस्या नहीं है। सबसे पहले मैं UIWindowइसे देखने के लिए बढ़ाता हूं यह दृश्य दृश्य नियंत्रक है। नोट : यदि आप कस्टम संग्रह * कक्षाएं (जैसे साइड मेनू) का उपयोग कर रहे हैं, तो आपको निम्नलिखित एक्सटेंशन में इस मामले के लिए हैंडलर जोड़ना चाहिए। शीर्ष सबसे दृश्य नियंत्रक मिलने के बाद यह पेश करने के लिए आसान है UIAlertControllerजैसे UIAlertView

extension UIAlertController {

  func show(animated: Bool = true, completion: (() -> Void)? = nil) {
    if let visibleViewController = UIApplication.shared.keyWindow?.visibleViewController {
      visibleViewController.present(self, animated: animated, completion: completion)
    }
  }

}

extension UIWindow {

  var visibleViewController: UIViewController? {
    guard let rootViewController = rootViewController else {
      return nil
    }
    return visibleViewController(for: rootViewController)
  }

  private func visibleViewController(for controller: UIViewController) -> UIViewController {
    var nextOnStackViewController: UIViewController? = nil
    if let presented = controller.presentedViewController {
      nextOnStackViewController = presented
    } else if let navigationController = controller as? UINavigationController,
      let visible = navigationController.visibleViewController {
      nextOnStackViewController = visible
    } else if let tabBarController = controller as? UITabBarController,
      let visible = (tabBarController.selectedViewController ??
        tabBarController.presentedViewController) {
      nextOnStackViewController = visible
    }

    if let nextOnStackViewController = nextOnStackViewController {
      return visibleViewController(for: nextOnStackViewController)
    } else {
      return controller
    }
  }

}

4

IOS 13 के लिए, पौराणिक कथाओं और बॉबीफ्रैम के उत्तरों पर निर्माण :

IOS 13 में, यदि आप अलर्ट प्रस्तुत करने के लिए अपनी स्वयं की विंडो बना रहे हैं, तो आपको उस विंडो का एक मजबूत संदर्भ रखने की आवश्यकता होती है, अन्यथा आपका अलर्ट प्रदर्शित नहीं किया जाएगा, क्योंकि इसके संदर्भ के दायरे से बाहर होने पर विंडो तुरंत समाप्त हो जाएगी।

इसके अलावा, आपको विंडो को हटाने के लिए चेतावनी को खारिज करने के बाद फिर से शून्य पर संदर्भ सेट करने की आवश्यकता होगी ताकि इसके नीचे मुख्य विंडो पर उपयोगकर्ता इंटरैक्शन को जारी रखने के लिए जारी रखा जा सके।

आप UIViewControllerविंडो मेमोरी मैनेजमेंट लॉजिक को इनकैप्सुलेट करने के लिए एक उपवर्ग बना सकते हैं :

class WindowAlertPresentationController: UIViewController {

    // MARK: - Properties

    private lazy var window: UIWindow? = UIWindow(frame: UIScreen.main.bounds)
    private let alert: UIAlertController

    // MARK: - Initialization

    init(alert: UIAlertController) {

        self.alert = alert
        super.init(nibName: nil, bundle: nil)
    }

    required init?(coder aDecoder: NSCoder) {

        fatalError("This initializer is not supported")
    }

    // MARK: - Presentation

    func present(animated: Bool, completion: (() -> Void)?) {

        window?.rootViewController = self
        window?.windowLevel = UIWindow.Level.alert + 1
        window?.makeKeyAndVisible()
        present(alert, animated: animated, completion: completion)
    }

    // MARK: - Overrides

    override func dismiss(animated flag: Bool, completion: (() -> Void)? = nil) {

        super.dismiss(animated: flag) {
            self.window = nil
            completion?()
        }
    }
}

आप इसका उपयोग कर सकते हैं, या यदि आप अपने लिए एक सुविधा विधि चाहते हैं UIAlertController, तो आप इसे एक्सटेंशन में फेंक सकते हैं:

extension UIAlertController {

    func presentInOwnWindow(animated: Bool, completion: (() -> Void)?) {

        let windowAlertPresentationController = WindowAlertPresentationController(alert: self)
        windowAlertPresentationController.present(animated: animated, completion: completion)
    }
}

यह काम नहीं करता है यदि आपको मैन्युअल रूप से चेतावनी को खारिज करने की आवश्यकता है
WindowAlertPresentationController

यदि आप चेतावनी को मैन्युअल रूप से खारिज करना चाहते हैं, तो dismissalert.presentingViewController?.dismiss(animated: true, completion: nil)
WindowAlertPresentationController पर

letContController = UIAlertController (शीर्षक: "शीर्षक", संदेश: "संदेश", पसंदीदा संदेश: .alert); alertController.pretInOwnWindow (एनिमेटेड: असत्य, पूर्ण: नील) मेरे लिए बहुत अच्छा काम करता है! धन्यवाद!
ब्रायन

यह iOS 6.4 के साथ iPhone 6 पर काम करता है, लेकिन iOS 11.3.1 के साथ iPhone 11 Pro पर नहीं। कोई त्रुटि नहीं है, लेकिन चेतावनी कभी प्रदर्शित नहीं होती है। किसी भी सुझाव का स्वागत है।
19

IOS 13. के लिए बहुत अच्छा काम करता है। उत्प्रेरक में काम नहीं करता है - एक बार अलर्ट खारिज होने के बाद, ऐप इंटरएक्टिव-सक्षम नहीं है। @Peter लापीसु का समाधान देखें
JBlake

3

ऑब्जेक्टिव-सी में अलर्ट पेश करने का शॉर्टहैंड तरीका:

[[[[UIApplication sharedApplication] keyWindow] rootViewController] presentViewController:alertController animated:YES completion:nil];

alertControllerतुम्हारी UIAlertControllerवस्तु कहाँ है ?

नोट: आपको यह सुनिश्चित करने की भी आवश्यकता होगी कि आपका सहायक वर्ग विस्तार करता है UIViewController


3

अगर किसी को दिलचस्पी है तो मैंने @agilityvision उत्तर का एक स्विफ्ट 3 संस्करण बनाया। कोड:

import Foundation
import UIKit

extension UIAlertController {

    var window: UIWindow? {
        get {
            return objc_getAssociatedObject(self, "window") as? UIWindow
        }
        set {
            objc_setAssociatedObject(self, "window", newValue, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC)
        }
    }

    open override func viewDidDisappear(_ animated: Bool) {
        super.viewDidDisappear(animated)
        self.window?.isHidden = true
        self.window = nil
    }

    func show(animated: Bool = true) {
        let window = UIWindow(frame: UIScreen.main.bounds)
        window.rootViewController = UIViewController(nibName: nil, bundle: nil)

        let delegate = UIApplication.shared.delegate
        if delegate?.window != nil {
            window.tintColor = delegate!.window!!.tintColor
        }

        window.windowLevel = UIApplication.shared.windows.last!.windowLevel + 1

        window.makeKeyAndVisible()
        window.rootViewController!.present(self, animated: animated, completion: nil)

        self.window = window
    }
}

@Chathuranga: मैंने आपका संपादन वापस कर दिया है। वह "त्रुटि से निपटने" पूरी तरह से अनावश्यक है।
मार्टिन आर।

2
extension UIApplication {
    /// The top most view controller
    static var topMostViewController: UIViewController? {
        return UIApplication.shared.keyWindow?.rootViewController?.visibleViewController
    }
}

extension UIViewController {
    /// The visible view controller from a given view controller
    var visibleViewController: UIViewController? {
        if let navigationController = self as? UINavigationController {
            return navigationController.topViewController?.visibleViewController
        } else if let tabBarController = self as? UITabBarController {
            return tabBarController.selectedViewController?.visibleViewController
        } else if let presentedViewController = presentedViewController {
            return presentedViewController.visibleViewController
        } else {
            return self
        }
    }
}

इससे आप अपना अलर्ट आसानी से पेश कर सकते हैं

UIApplication.topMostViewController?.present(viewController, animated: true, completion: nil)

एक बात का ध्यान रखें कि यदि वर्तमान में एक UIAlertController प्रदर्शित हो रही है, तो UIApplication.topMostViewControllerवह वापस आ जाएगी UIAlertController। एक UIAlertControllerअजीब व्यवहार के शीर्ष पर प्रस्तुत करना और बचा जाना चाहिए। जैसे, आपको या तो मैन्युअल रूप से जांच करनी चाहिए कि !(UIApplication.topMostViewController is UIAlertController)प्रस्तुत करने से पहले, या else ifयदि निल वापस करने के लिए कोई मामला जोड़ेंself is UIAlertController

extension UIViewController {
    /// The visible view controller from a given view controller
    var visibleViewController: UIViewController? {
        if let navigationController = self as? UINavigationController {
            return navigationController.topViewController?.visibleViewController
        } else if let tabBarController = self as? UITabBarController {
            return tabBarController.selectedViewController?.visibleViewController
        } else if let presentedViewController = presentedViewController {
            return presentedViewController.visibleViewController
        } else if self is UIAlertController {
            return nil
        } else {
            return self
        }
    }
}

1

आप एक पैरामीटर के रूप में वर्तमान दृश्य या नियंत्रक भेज सकते हैं:

+ (void)myUtilityMethod:(id)controller {
    // do stuff
    // something bad happened, display an alert.
}

हाँ, यह संभव है और काम करेगा। लेकिन मेरे लिए, यह एक कोड गंध का एक सा मिल गया है। पास किए गए पैरामीटर को आमतौर पर प्राथमिक कार्य करने के लिए बुलाया विधि के लिए आवश्यक होना चाहिए। साथ ही सभी मौजूदा कॉल को संशोधित करने की आवश्यकता होगी।
मुरैना सगल

1

केविन स्लीच ने एक महान समाधान प्रदान किया।

मैं अब अपने मुख्य UIViewController उपवर्ग में नीचे दिए गए कोड का उपयोग करता हूं।

एक छोटा परिवर्तन मैंने यह देखने के लिए किया था कि क्या सबसे अच्छा प्रस्तुति नियंत्रक एक सादा UIViewController नहीं है। यदि नहीं, तो यह कुछ वीसी हो गया है जो एक सादे वीसी प्रस्तुत करता है। इस प्रकार हम वीसी को लौटाते हैं जो इसके बजाय प्रस्तुत किया जा रहा है।

- (UIViewController *)bestPresentationController
{
    UIViewController *bestPresentationController = [UIApplication sharedApplication].keyWindow.rootViewController;

    if (![bestPresentationController isMemberOfClass:[UIViewController class]])
    {
        bestPresentationController = bestPresentationController.presentedViewController;
    }    

    return bestPresentationController;
}

मेरे परीक्षण में अब तक सभी काम करने लगता है।

धन्यवाद केविन!


1

दिए गए महान जवाब ( चपलता , एडिब , मल्हाल ) के अलावा। अच्छे पुराने UIAlertViews की तरह कतारबद्ध व्यवहार तक पहुँचने के लिए (अलर्ट विंडो ओवरलैप से बचें), विंडो स्तर उपलब्धता देखने के लिए इस ब्लॉक का उपयोग करें:

@interface UIWindow (WLWindowLevel)

+ (void)notifyWindowLevelIsAvailable:(UIWindowLevel)level withBlock:(void (^)())block;

@end

@implementation UIWindow (WLWindowLevel)

+ (void)notifyWindowLevelIsAvailable:(UIWindowLevel)level withBlock:(void (^)())block {
    UIWindow *keyWindow = [UIApplication sharedApplication].keyWindow;
    if (keyWindow.windowLevel == level) {
        // window level is occupied, listen for windows to hide
        id observer;
        observer = [[NSNotificationCenter defaultCenter] addObserverForName:UIWindowDidBecomeHiddenNotification object:keyWindow queue:nil usingBlock:^(NSNotification *note) {
            [[NSNotificationCenter defaultCenter] removeObserver:observer];
            [self notifyWindowLevelIsAvailable:level withBlock:block]; // recursive retry
        }];

    } else {
        block(); // window level is available
    }
}

@end

पूरा उदाहरण:

[UIWindow notifyWindowLevelIsAvailable:UIWindowLevelAlert withBlock:^{
    UIWindow *alertWindow = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
    alertWindow.windowLevel = UIWindowLevelAlert;
    alertWindow.rootViewController = [UIViewController new];
    [alertWindow makeKeyAndVisible];

    UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"Alert" message:nil preferredStyle:UIAlertControllerStyleAlert];
    [alertController addAction:[UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleCancel handler:^(UIAlertAction *action) {
        alertWindow.hidden = YES;
    }]];

    [alertWindow.rootViewController presentViewController:alertController animated:YES completion:nil];
}];

यह आपको अलर्ट विंडो ओवरलैप से बचने की अनुमति देगा। किसी भी संख्या में विंडो लेयर्स के लिए समान विधि को अलग करने और कतार दृश्य नियंत्रकों में रखने के लिए उपयोग किया जा सकता है।


1

मैंने उल्लेखित सभी चीजों की कोशिश की, लेकिन कोई सफलता नहीं मिली। विधि जो मैंने स्विफ्ट 3.0 के लिए इस्तेमाल की थी:

extension UIAlertController {
    func show() {
        present(animated: true, completion: nil)
    }

    func present(animated: Bool, completion: (() -> Void)?) {
        if var topController = UIApplication.shared.keyWindow?.rootViewController {
            while let presentedViewController = topController.presentedViewController {
                topController = presentedViewController
            }
            topController.present(self, animated: animated, completion: completion)
        }
    }
}

1

इनमें से कुछ जवाबों ने केवल मेरे लिए आंशिक रूप से काम किया, उन्हें AppDelegate में निम्न वर्ग विधि में संयोजित करना मेरे लिए समाधान था। यह iPad पर काम करता है, UITabBarController विचारों में, UINavigationController में, जब मॉडल प्रस्तुत करना। IOS 10 और 13 पर परीक्षण किया गया।

+ (UIViewController *)rootViewController {
    UIViewController *rootViewController = [UIApplication sharedApplication].delegate.window.rootViewController;
    if([rootViewController isKindOfClass:[UINavigationController class]])
        rootViewController = ((UINavigationController *)rootViewController).viewControllers.firstObject;
    if([rootViewController isKindOfClass:[UITabBarController class]])
        rootViewController = ((UITabBarController *)rootViewController).selectedViewController;
    if (rootViewController.presentedViewController != nil)
        rootViewController = rootViewController.presentedViewController;
    return rootViewController;
}

उपयोग:

[[AppDelegate rootViewController] presentViewController ...

1

iOS13 दृश्य समर्थन (UIWindowScene का उपयोग करते समय)

import UIKit

private var windows: [String:UIWindow] = [:]

extension UIWindowScene {
    static var focused: UIWindowScene? {
        return UIApplication.shared.connectedScenes
            .first { $0.activationState == .foregroundActive && $0 is UIWindowScene } as? UIWindowScene
    }
}

class StyledAlertController: UIAlertController {

    var wid: String?

    func present(animated: Bool, completion: (() -> Void)?) {

        //let window = UIWindow(frame: UIScreen.main.bounds)
        guard let window = UIWindowScene.focused.map(UIWindow.init(windowScene:)) else {
            return
        }
        window.rootViewController = UIViewController()
        window.windowLevel = .alert + 1
        window.makeKeyAndVisible()
        window.rootViewController!.present(self, animated: animated, completion: completion)

        wid = UUID().uuidString
        windows[wid!] = window
    }

    open override func viewDidDisappear(_ animated: Bool) {
        super.viewDidDisappear(animated)
        if let wid = wid {
            windows[wid] = nil
        }

    }

}

0

आप मेहाटोड UIViewControllerजैसे - (void)presentErrorMessage;और के साथ एक श्रेणी को लागू करने की कोशिश कर सकते हैं और उस पद्धति के अंदर आप यूआईएलर्टकंट्रोलर को लागू करते हैं और फिर इसे प्रस्तुत करते हैं self। आपके ग्राहक कोड में आपके पास कुछ इस तरह होगा:

[myViewController presentErrorMessage];

इस तरह आप खिड़की के पदानुक्रम में नहीं होने के बारे में अनावश्यक पैराशूट और चेतावनी से बचेंगे।


सिवाय इसके कि मेरे पास myViewControllerउस कोड में नहीं है जहां बुरी चीज होती है। यह एक यूटिलिटी मेथड में है जो व्यू कंट्रोलर के बारे में कुछ नहीं जानता है जो इसे कहते हैं।
मुरैना सगल

2
IMHO उपयोगकर्ता को कोई भी दृश्य (इस प्रकार अलर्ट) प्रस्तुत करना ViewControllers की जिम्मेदारी है। इसलिए यदि कोड का कुछ हिस्सा व्यू कॉंट्रोलर के बारे में कुछ नहीं जानता है तो उसे उपयोगकर्ता को कोई त्रुटि नहीं प्रस्तुत करनी चाहिए, बल्कि कोड के कुछ हिस्सों को "viewController जागरूक"
व्लाद सोरोका

2
मैं सहमत हूँ। लेकिन अब पदावनत की सुविधा UIAlertViewने मुझे कुछ ही स्थानों पर उस नियम को तोड़ने के लिए प्रेरित किया।
मुरैना सगल

0

वहाँ 2 दृष्टिकोण है कि आप का उपयोग कर सकते हैं:

-उपयोग UIAlertViewया 'UIActionSheet' के बजाय (अनुशंसित नहीं है, क्योंकि यह iOS 8 में पदावनत है, लेकिन यह अब काम करता है)

-सोमो अंतिम दृश्य नियंत्रक को याद रखें जो प्रस्तुत किया गया है। यहाँ उदाहरण है।

@interface UIViewController (TopController)
+ (UIViewController *)topViewController;
@end

// implementation

#import "UIViewController+TopController.h"
#import <objc/runtime.h>

static __weak UIViewController *_topViewController = nil;

@implementation UIViewController (TopController)

+ (UIViewController *)topViewController {
    UIViewController *vc = _topViewController;
    while (vc.parentViewController) {
        vc = vc.parentViewController;
    }
    return vc;
}

+ (void)load {
    [super load];
    [self swizzleSelector:@selector(viewDidAppear:) withSelector:@selector(myViewDidAppear:)];
    [self swizzleSelector:@selector(viewWillDisappear:) withSelector:@selector(myViewWillDisappear:)];
}

- (void)myViewDidAppear:(BOOL)animated {
    if (_topViewController == nil) {
        _topViewController = self;
    }

    [self myViewDidAppear:animated];
}

- (void)myViewWillDisappear:(BOOL)animated {
    if (_topViewController == self) {
        _topViewController = nil;
    }

    [self myViewWillDisappear:animated];
}

+ (void)swizzleSelector:(SEL)sel1 withSelector:(SEL)sel2
{
    Class class = [self class];

    Method originalMethod = class_getInstanceMethod(class, sel1);
    Method swizzledMethod = class_getInstanceMethod(class, sel2);

    BOOL didAddMethod = class_addMethod(class,
                                        sel1,
                                        method_getImplementation(swizzledMethod),
                                        method_getTypeEncoding(swizzledMethod));

    if (didAddMethod) {
        class_replaceMethod(class,
                            sel2,
                            method_getImplementation(originalMethod),
                            method_getTypeEncoding(originalMethod));
    } else {
        method_exchangeImplementations(originalMethod, swizzledMethod);
    }
}

@end 

उपयोग:

[[UIViewController topViewController] presentViewController:alertController ...];

0

मैं अपने AppDelegate वर्ग में कुछ छोटे व्यक्तिगत बदलावों के साथ इस कोड का उपयोग करता हूं

-(UIViewController*)presentingRootViewController
{
    UIViewController *vc = self.window.rootViewController;
    if ([vc isKindOfClass:[UINavigationController class]] ||
        [vc isKindOfClass:[UITabBarController class]])
    {
        // filter nav controller
        vc = [AppDelegate findChildThatIsNotNavController:vc];
        // filter tab controller
        if ([vc isKindOfClass:[UITabBarController class]]) {
            UITabBarController *tbc = ((UITabBarController*)vc);
            if ([tbc viewControllers].count > 0) {
                vc = [tbc viewControllers][tbc.selectedIndex];
                // filter nav controller again
                vc = [AppDelegate findChildThatIsNotNavController:vc];
            }
        }
    }
    return vc;
}
/**
 *   Private helper
 */
+(UIViewController*)findChildThatIsNotNavController:(UIViewController*)vc
{
    if ([vc isKindOfClass:[UINavigationController class]]) {
        if (((UINavigationController *)vc).viewControllers.count > 0) {
            vc = [((UINavigationController *)vc).viewControllers objectAtIndex:0];
        }
    }
    return vc;
}

0

काम करने लगता है:

static UIViewController *viewControllerForView(UIView *view) {
    UIResponder *responder = view;
    do {
        responder = [responder nextResponder];
    }
    while (responder && ![responder isKindOfClass:[UIViewController class]]);
    return (UIViewController *)responder;
}

-(void)showActionSheet {
    UIAlertController *alertController = [UIAlertController alertControllerWithTitle:nil message:nil preferredStyle:UIAlertControllerStyleActionSheet];
    [alertController addAction:[UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:nil]];
    [alertController addAction:[UIAlertAction actionWithTitle:@"Do it" style:UIAlertActionStyleDefault handler:nil]];
    [viewControllerForView(self) presentViewController:alertController animated:YES completion:nil];
}

0

सहायक वर्ग AlertWindow और के रूप में उपयोग से बनाएँ

let alertWindow = AlertWindow();
let alert = UIAlertController(title: "Hello", message: "message", preferredStyle: .alert);
let cancel = UIAlertAction(title: "Ok", style: .cancel){(action) in

    //....  action code here

    // reference to alertWindow retain it. Every action must have this at end

    alertWindow.isHidden = true;

   //  here AlertWindow.deinit{  }

}
alert.addAction(cancel);
alertWindow.present(alert, animated: true, completion: nil)


class AlertWindow:UIWindow{

    convenience init(){
        self.init(frame:UIScreen.main.bounds);
    }

    override init(frame: CGRect) {
        super.init(frame: frame);
        if let color = UIApplication.shared.delegate?.window??.tintColor {
            tintColor = color;
        }
        rootViewController = UIViewController()
        windowLevel = UIWindowLevelAlert + 1;
        makeKeyAndVisible()
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    deinit{
        //  semaphor.signal();
    }

    func present(_ ctrl:UIViewController, animated:Bool, completion: (()->Void)?){
        rootViewController!.present(ctrl, animated: animated, completion: completion);
    }
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.