যখন ভিউ কন্ট্রোলারে না থাকে তখন কীভাবে ইউআইএএলআর্টকন্ট্রোলার উপস্থাপন করবেন?


255

পরিস্থিতি: ব্যবহারকারী একটি ভিউ কন্ট্রোলারের একটি বোতামে ট্যাপ করে। নেভিগেশন স্ট্যাকের মধ্যে ভিউ কন্ট্রোলার শীর্ষতম (স্পষ্টতই)। ট্যাপটি অন্য শ্রেণিতে ডাকা একটি ইউটিলিটি ক্লাস পদ্ধতিটি আহ্বান করে। একটি খারাপ জিনিস সেখানে ঘটে এবং আমি ভিউ কন্ট্রোলারে নিয়ন্ত্রণ ফিরে আসার আগে ঠিক সেখানে একটি সতর্কতা প্রদর্শন করতে চাই।

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

এটি দিয়েই সম্ভব হয়েছিল UIAlertView (তবে সম্ভবত বেশ সঠিকভাবে নয়)।

এই ক্ষেত্রে, আপনি একটি উত্থাপন করেন কেন UIAlertControllerএ, ঠিক আছে myUtilityMethod?

উত্তর:


34

আমি কয়েক মাস আগে একটি অনুরূপ প্রশ্ন পোস্ট করেছি এবং মনে করি আমি শেষ পর্যন্ত সমস্যার সমাধান করেছি। আপনি যদি কেবল কোডটি দেখতে চান তবে আমার পোস্টের নীচের লিঙ্কটি অনুসরণ করুন।

সমাধানটি হ'ল অতিরিক্ত ইউআই উইন্ডো ব্যবহার করা।

আপনি যখন আপনার ইউআইএলআরলেট কনট্রোলার প্রদর্শন করতে চান:

  1. আপনার উইন্ডোটিকে কী এবং দৃশ্যমান উইন্ডোটি তৈরি করুন (window.makeKeyAndVisible() )
  2. নতুন উইন্ডোর রুটভিউ কনট্রোলার হিসাবে কেবল একটি সরল ইউআইভিউকন্ট্রোলার উদাহরণটি ব্যবহার করুন। (window.rootViewController = UIViewController() )
  3. আপনার উইন্ডোটির রুটভিউকন্ট্রোলারটিতে আপনার ইউআইএলআরলেট কনট্রোলার উপস্থাপন করুন

কয়েকটি বিষয় লক্ষণীয়:

  • আপনার UIWindow অবশ্যই দৃ strongly়ভাবে উল্লেখ করা উচিত। যদি এটি দৃ strongly়ভাবে উল্লেখ না করা হয় তবে এটি কখনই প্রদর্শিত হবে না (কারণ এটি প্রকাশিত হয়েছে)। আমি একটি সম্পত্তি ব্যবহার করার পরামর্শ দিচ্ছি, তবে আমি কোনও যুক্ত বস্তুর সাথে সাফল্যও পেয়েছি
  • উইন্ডোটি অন্য সমস্ত কিছুর উপরে উপস্থিত রয়েছে তা নিশ্চিত করতে (সিস্টেম UIAlertControllers সহ) উইন্ডো লেভেল সেট করেছি। ( window.windowLevel = UIWindowLevelAlert + 1)

অবশেষে, আপনি যদি কেবল এটি দেখতে চান তবে আমার একটি সম্পূর্ণ বাস্তবায়ন রয়েছে।

https://github.com/dbettermann/DBAlertController


অবজেক্টিভ-সি এর জন্য আপনার এটি নেই, তাই না?
SAHM

2
হ্যাঁ, এটি এমনকি সুইফট ২.০ / আইওএস ৯ এও কাজ করে I'm আমি এখনই একটি উদ্দেশ্য-সি সংস্করণে কাজ করছি কারণ অন্য কেউ এটি চেয়েছিল (সম্ভবত এটি আপনিই ছিলেন)। কাজ শেষ হয়ে গেলে আমি আবার পোস্ট করব।
ডিলান বেটারম্যান

322

ডাব্লুডাব্লুডিসিতে আমি একটি ল্যাবটিতে গিয়ে থামলাম এবং একটি অ্যাপল ইঞ্জিনিয়ারকে এই একই প্রশ্নটি জিজ্ঞাসা করলাম: "একটি প্রদর্শন করার জন্য সেরা অনুশীলনটি কী ছিল UIAlertController?" এবং তিনি বলেছিলেন যে তারা এই প্রশ্নটি অনেকটা পাচ্ছিলেন এবং আমরা রসিকতা করেছি যে তাদের এটি নিয়ে একটি অধিবেশন করা উচিত ছিল। তিনি বলেছিলেন যে অভ্যন্তরীণভাবে অ্যাপল UIWindowএকটি স্বচ্ছ দিয়ে একটি তৈরি করছে UIViewControllerএবং তারপরে এটি উপস্থাপন করছে UIAlertController। মূলত ডিলান বেটারম্যানের উত্তরে যা আছে।

তবে আমি একটি সাবক্লাস ব্যবহার করতে চাইনি UIAlertControllerকারণ এর জন্য আমার অ্যাপ্লিকেশনটিতে আমার কোড পরিবর্তন করা দরকার। সুতরাং কোনও সম্পর্কিত সামগ্রীর সাহায্যে, আমি একটি বিভাগ তৈরি করে UIAlertControllerযা সরবরাহ করে ashow উদ্দেশ্য-সিতে পদ্ধতি ।

এখানে প্রাসঙ্গিক কোড:

#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, dealloced, যেহেতু এটি শুধুমাত্র বস্তু যে ধারনকারী হয় UIWindow। তবে যদি আপনি UIAlertControllerকোনও সম্পত্তিকে অর্পণ করেন বা কোনও অ্যাকশন ব্লকে সতর্কতা অ্যাক্সেসের মাধ্যমে এটির ধরে রাখার গণনা বাড়িয়ে UIWindowতোলেন, তবে আপনার ইউআই লক করে দেওয়া স্ক্রিনে থাকবে। অ্যাক্সেস প্রয়োজনের ক্ষেত্রে এড়াতে উপরের নমুনা ব্যবহারের কোডটি দেখুনUITextField

আমি একটি পরীক্ষার প্রকল্পের সাথে একটি গিটহাব রেপো তৈরি করেছি: FFGlobalAlertController


1
ভাল জিনিস! মাত্র কিছু ব্যাকগ্রাউন্ড - আমি সম্পর্কিত আইটেমের পরিবর্তে একটি সাবক্লাস ব্যবহার করেছি কারণ আমি সুইফ্ট ব্যবহার করছিলাম। সংযুক্ত বস্তুগুলি উদ্দেশ্য-সি রানটাইমের একটি বৈশিষ্ট্য এবং আমি এটির উপর নির্ভর করতে চাই না। সুইট সম্ভবত নিজের রানটাইম পাওয়ার থেকে কয়েক বছর দূরে, তবে এখনও। :)
ডিলান বেটেরম্যান

1
আমি আপনার উত্তরের কমনীয়তা পছন্দ করি, তবে আমি কী আগ্রহী যে আপনি কীভাবে নতুন উইন্ডোটি অবসর গ্রহণ করেন এবং মূল উইন্ডোটিকে কীটি আবার বানান (অবশ্যই আমি উইন্ডোটি দিয়ে খুব বেশি উপদ্রব করি না)।
ডাস্টিন পফান্নস্টিল

1
কী উইন্ডোটি শীর্ষস্থানীয় দৃশ্যমান উইন্ডো, সুতরাং আমার বোধগম্যতা হল আপনি যদি "কী" উইন্ডোটি সরিয়ে / আড়াল করেন তবে পরবর্তী দৃশ্যমান উইন্ডোটি নীচে "কী" হয়ে যায়।
চতুরতা

19
viewDidDisappear:কোনও বিভাগে প্রয়োগ করা খারাপ ধারণা হিসাবে দেখায়। সংক্ষেপে, আপনি কাঠামোর বাস্তবায়নের সাথে প্রতিযোগিতা করছেন viewDidDisappear:। আপাতত এটি ঠিক হতে পারে তবে ভবিষ্যতে অ্যাপল যদি সেই পদ্ধতিটি বাস্তবায়নের সিদ্ধান্ত নেয় তবে এটির কল করার কোনও উপায় নেই (যেমন কোনও superবিভাগের বাস্তবায়ন থেকে কোনও পদ্ধতির প্রাথমিক প্রয়োগের ক্ষেত্রে সেই পয়েন্টগুলির সাদৃশ্য নেই ) ।
আদিব

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.premittedViewController! = শূন্য) {রুটভিউকন্ট্রোলার = রুটভিউকন্ট্রোলআর.প্রেজেন্টভিউ কনট্রোলার; }
DivideByZer0

1
সুইফট 3: 'সতর্কতা' 'সতর্কতা' থেকে নাম পরিবর্তন করা হয়েছে: দিন alertController = UIAlertController (শিরোনাম: "শিরোনাম", বার্তা: "বার্তা", preferredStyle: .alert)
Kaptain

পরিবর্তে একটি প্রতিনিধি ব্যবহার করুন!
অ্যান্ড্রু কিরনা

104

আপনি সুইফট ২.২ দিয়ে নিম্নলিখিতটি করতে পারেন:

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
ওফস, আমি যাচাই করার আগে গ্রহণ করেছি। এই কোডটি মূল দেখায় নিয়ন্ত্রককে ফেরত দেয় যা আমার ক্ষেত্রে নেভিগেশন নিয়ামক। এটি ত্রুটির কারণ নয় তবে সতর্কতা প্রদর্শন করে না।
মারে সাগাল 21

22
এবং আমি কনসোলে খেয়াল: Warning: Attempt to present <UIAlertController: 0x145bfa30> on <UINavigationController: 0x1458e450> whose view is not in the window hierarchy!
মারে সাগল 21

1
@ মুরেসাগাল একটি নেভিগেশন কন্ট্রোলার রেখে আপনি visibleViewControllerযে কোনও সময় যে সম্পত্তিটি সতর্কতা উপস্থাপন করতে হবে তা দেখতে সম্পত্তি পেতে পারেন । পরীক্ষা করে দেখুন ডক্স
Lubo

2
আমি এটি করেছি কারণ আমি অন্য কারও কাজের কৃতিত্ব নিতে চাই না। এটি @ জাভেইজেনবার্গের সমাধান যা আমি দ্রুত 3.0 এর জন্য সংশোধন করেছি। আমি যদি আর একটি উত্তর যুক্ত করতাম তবে আমি তার পক্ষে যোগ্য ভোট পেলাম।
jeet.chanchawat

1
ওহে, গতকাল আমি সমস্ত নাটকটি মিস করেছি, তবে আমি কেবল সুইফট ৩-এর জন্য পোস্টটি আপডেট করেছি I আমি জানি না যে নতুন ভাষার সংস্করণগুলির পুরানো উত্তরগুলি আপডেট করার বিষয়ে এসওয়ের নীতি কী, তবে আমি ব্যক্তিগতভাবে এটি আপত্তি করি না, উত্তর যতক্ষণ সঠিক!
জেভ আইজেনবার্গ

34

UIAlertController extensionসমস্ত ক্ষেত্রে UINavigationControllerএবং / অথবা এর জন্য বেশ জেনেরিক UITabBarController। এই মুহুর্তে স্ক্রিনে কোনও মডেল ভিসি থাকলে তাও কাজ করে।

ব্যবহার:

//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 সঙ্গে এই সমাধান সংমিশ্রণ ব্যবহার: আমি একটি Singleton আছে UI(! দুর্বল) শ্রেণী যেটি একটি ঝুলিতে currentVCধরনের UIViewController.আমি আছে BaseViewControllerযা উত্তরাধিকারী থেকে 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];

নিখুঁত, উইন্ডোটি খারিজ করার জন্য আমার ঠিক টিপটি দরকার ছিল, সাথী ধন্যবাদ
17'8

25

নিম্নলিখিত সমাধান হয়নি না যদিও এটা বেশ সব সংস্করণের সাথে প্রতিশ্রুতি লাগছিল কাজ করি। এই সমাধানটি সতর্কতা উত্পন্ন করছে

সতর্কতা: কার দৃষ্টিভঙ্গি উইন্ডো শ্রেণিবিন্যাসে নেই তা উপস্থাপনের চেষ্টা!

https://stackoverflow.com/a/34487871/2369867 => এটি তখন আশাব্যঞ্জক বলে মনে হচ্ছে। কিন্তু এটা ছিল না যে Swift 3। তাই আমি সুইফট 3 এই উত্তর করছি এবং এই হল না টেমপ্লেট উদাহরণ।

আপনি কোনও ফাংশনের ভিতরে পেস্ট করলে এটি নিজেই পুরোপুরি কার্যক্ষম কোড is

দ্রুত 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
এই কোডটি আমার পক্ষে পুরোপুরি কাজ করেছিল, এমন কোনও প্রসঙ্গে যেখানে কোনও ইউআইএলার্টকন্ট্রোলার কোনও মাইগ্রেশন ইস্যু সম্পর্কিত অ্যাপ ডেলিগেটে বহিষ্কার করা হয়েছিল, কোনও রুট ভিউ কন্ট্রোলার লোড হওয়ার আগেই। দুর্দান্ত কাজ করেছেন, কোনও সতর্কতা নেই।
ডানকান ব্যাবেজ

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!

আমি এটি পরীক্ষা করে দেখিনি, তবে আপনার রুট ভিসি কোনও নেভিগেশন নিয়ামক হিসাবে দেখা দিলে এটিও প্রয়োজনীয় হতে পারে।


হাম আমি সুইফটে এই সমস্যায় পড়ছি, এবং আমি কীভাবে আপনার আপত্তি কোডটি সুইফটে অনুবাদ করতে পারি তা খুঁজে পাচ্ছি না, সাহায্যের প্রশংসা হবে!

2
@ মায়ারজ অবজেক্টিভ সি সি সুইফটে অনুবাদ করা এত বড় বিষয় হওয়া উচিত নয়;) তবে আপনি এখানে:UIApplication.sharedApplication().keyWindow?.rootViewController?.presentedViewController?.presentViewController(controller, animated: true, completion: nil)
বোরচেরো

ধন্যবাদ অলিভিয়ার, আপনি ঠিক বলেছেন, পাই হিসাবে এটি সহজ, এবং আমি এটি এইভাবে অনুবাদ করেছি, তবে সমস্যাটি অন্য কোথাও ছিল। যাই হোক ধন্যবাদ!

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

@ তত্পরতা উত্তর এর উত্তর সুইফট 4 / আইওএস 11 এ অনুবাদ করেছে আমি স্থানীয়করণযুক্ত স্ট্রিং ব্যবহার করি নি, তবে আপনি এটি সহজেই পরিবর্তন করতে পারেন:

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প্রয়োজনীয় বলে মনে হচ্ছে না।
বেন প্যাচ

মনে রাখবেন যে অ্যাপল 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/docamentation/uikit/uialertcontroller
গ্রুবাস

6

অ্যাভিয়েল গ্রস উত্তরের মতো এক্সটেনশন তৈরি করুন। এখানে আপনার উদ্দেশ্য-সি এক্সটেনশন রয়েছে।

এখানে আপনার শিরোনাম ফাইল *। এইচ আছে

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

এটি কারণ উইন্ডোজ রুটভিউ কনট্রোলার উপস্থাপিত দর্শনের শীর্ষে নেই। এটি সংশোধন করার জন্য আমাদের উপস্থাপনা শৃঙ্খলা চালিয়ে যাওয়া দরকার, যেমনটি আমার ইউআইএআরএলআর্ট কন্ট্রোলার এক্সটেনশন কোডে সুইফট 3 তে লিখিত হয়েছে:

   /// 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 এ আপডেট হয়েছে:

পরীক্ষিত এবং নিশ্চিত করা হয়েছে যে উপরের যুক্তিটি সদ্য উপলভ্য আইওএস 11 জিএম বীজে দুর্দান্ত কাজ করে। তত্পরতা সহ শীর্ষে ভোট দেওয়া পদ্ধতিটি অবশ্য তা করে না: সদ্য সতর্কতার মধ্যে উপস্থিত সতর্কতা দর্শনUIWindow কীবোর্ডের নীচে এবং সম্ভাব্যরূপে ব্যবহারকারীকে তার বোতামগুলি ট্যাপ করা থেকে বিরত করে। এটি কারণ হ'ল আইওএস 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 জিএম বীজে ডান টিন্ট রঙের সাথে এবং কীবোর্ড স্লাইডিং আর্টফ্যাক্ট ছাড়াই দুর্দান্ত কাজ করে।


কেবল এখানে পূর্ববর্তী অনেক উত্তরের মধ্য দিয়ে গিয়ে কেভিন স্লাইচের উত্তরটি দেখেছিল, যা একই সমস্যাটিকে একই পদ্ধতির সাথে সমাধান করার চেষ্টা করছে তবে যা উপস্থাপনা শৃঙ্খলা চালিয়ে যাওয়া বন্ধ করে দিয়েছে, সুতরাং এটি একই ধরণের সমস্যার জন্য সংবেদনশীল হয়ে উঠেছে যেমন এটি সমাধান করার চেষ্টা করে ।
কোডব্রু

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

আইওএস 13 এর জন্য, পৌরাণিক কোডার এবং বোবিরেহমের উত্তরগুলি তৈরি করা :

আইওএস 13-এ, যদি আপনি সতর্কতা উপস্থাপনের জন্য আপনার নিজের উইন্ডোটি তৈরি করে থাকেন তবে আপনাকে সেই উইন্ডোটির একটি দৃ reference় রেফারেন্স রাখা দরকার অন্যথায় আপনার সতর্কতাটি প্রদর্শিত হবে না কারণ উইন্ডোটি যখন রেফারেন্সের সুযোগটি ছাড়বে তখন অবিলম্বে deallocated হয়ে যাবে।

তদ্ব্যতীত, নীচের মূল উইন্ডোতে ব্যবহারকারীর মিথস্ক্রিয়াটিকে চালিয়ে যাওয়ার জন্য উইন্ডোটি সরিয়ে ফেলার জন্য আপনাকে সতর্কতাটি বরখাস্ত করার পরে আবারও শূন্য করার জন্য রেফারেন্স সেট করতে হবে।

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)
    }
}

আপনার যদি সতর্কতাটি ম্যানুয়ালি খারিজ করার দরকার হয় তবে এটি কাজ করে না - উইন্ডো অ্যালার্টপ্রেজেনশন কন্ট্রোলারটি কখনই ডি-বরাদ্দ হয় না, যার ফলে হিমায়িত ইউআই হয় - উইন্ডোটির কারণে এখনও কিছুই ইন্টারেক্টিভ হয় না
জেব্লেক

আপনি যদি সতর্কতাটিকে ম্যানুয়ালি খারিজ করতে চান, dismissতবে উইন্ডো অ্যালার্টপ্রেজেশন কনট্রোলারকে সরাসরি কল করতে ভুলবেন নাalert.presentingViewController?.dismiss(animated: true, completion: nil)
JBlake

এলার্টকন্ট্রোলারকে = ইউআইএলআরএল্টকন্ট্রোলার (শিরোনাম: "শিরোনাম", বার্তা: "বার্তা", পছন্দসই স্টাইল: .ালার্ট); এলার্টকন্ট্রোলআর.প্রেসেন্টআইন ওয়ান উইন্ডো (অ্যানিমেটেড: মিথ্যা, সমাপ্তি: শূন্য) আমার জন্য দুর্দান্ত কাজ করে! ধন্যবাদ!
ব্রায়ান

এটি আইফোন 6 এ আইওএস 12.4.5 এর সাথে কাজ করে, তবে আইওএস 11 প্রোতে আইওএস 13.3.1 সহ নয়। কোনও ত্রুটি নেই, তবে সতর্কতাটি কখনই প্রদর্শিত হয় না। যেকোন পরামর্শ সম্মানের সহিত গৃহীত হবে।
jl303

আইওএস 13 এর জন্য দুর্দান্ত কাজ করে Cat ক্যাটালিস্টে কাজ করে না - একবার সতর্কতা বরখাস্ত হয়ে গেলে অ্যাপটি ইন্টারেক্ট-সক্ষম হয় না। @ পিটার লাপিসুর সমাধান দেখুন
জেব্লেক

3

উদ্দেশ্য-সি তে সতর্কতা উপস্থাপনের শর্টহ্যান্ড উপায়:

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

alertControllerআপনার UIAlertControllerউদ্দেশ্য কোথায় ?

দ্রষ্টব্য: আপনার সহায়ক ক্লাসটি প্রসারিত হয়েছে তাও আপনাকে নিশ্চিত করতে হবে UIViewController


3

যদি কেউ আগ্রহী হন তবে আমি @ সামঞ্জস্য উত্তরের একটি সুইফট 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
    }
}

@ ছাথুরঙ্গ: আমি আপনার সম্পাদনাটি ফিরিয়ে দিয়েছি। এই "ত্রুটি পরিচালনা" সম্পূর্ণ অপ্রয়োজনীয়।
মার্টিন আর

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)

একটি বিষয় লক্ষণীয় হ'ল যদি বর্তমানে কোনও ইউআইএলার্টকন্ট্রোলার প্রদর্শিত হচ্ছে তবে এটি 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 *)bestPresentationController
{
    UIViewController *bestPresentationController = [UIApplication sharedApplication].keyWindow.rootViewController;

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

    return bestPresentationController;
}

আমার পরীক্ষায় এখন পর্যন্ত সমস্ত কাজ করা দেখে মনে হচ্ছে।

ধন্যবাদ কেভিন!


1

প্রদত্ত দুর্দান্ত উত্তরের পাশাপাশি ( চতুরতা , আদিব , ম্যালহাল )। ভাল পুরানো ইউআইএলআর্টভিউয়ের মতো কুইউং আচরণে পৌঁছানোর জন্য (সতর্কতা উইন্ডোজ ওভারল্যাপ এড়ান), উইন্ডো স্তরের উপলব্ধতা পর্যবেক্ষণ করতে এই ব্লকটি ব্যবহার করুন:

@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

আমি উল্লিখিত সমস্ত কিছু চেষ্টা করেছিলাম, তবে কোনও সাফল্য পাইনি। আমি সুইফট ৩.০ এর জন্য যে পদ্ধতিটি ব্যবহার করেছি:

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

এই উত্তরগুলির মধ্যে কিছু আমার জন্য কেবলমাত্র আংশিকভাবে কাজ করেছিল, এ্যাপডিলেজেটে নিম্নলিখিত শ্রেণির পদ্ধতিতে সেগুলি সংমিশ্রণ করা আমার জন্য সমাধান ছিল। মডেল উপস্থাপন করার সময় এটি আইপ্যাডে, ইউআইটিববারকন্ট্রোলার দর্শনে, ইউআইএনএভিগেশন কন্ট্রোলার-এ কাজ করে। আইওএস 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

আইওএস 13 দৃশ্যের সহায়তা (ইউআইওয়াইন্ডোসেসিন ব্যবহার করার সময়)

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
আইএমএইচও ব্যবহারকারীর কাছে কোনও মতামত (এইভাবে সতর্কতা) উপস্থাপন করা ভিউকন্ট্রোলারদের দায়িত্ব। সুতরাং কোডের কিছু অংশ যদি ভিউকন্ট্রোলার সম্পর্কে কিছু না জানে এটি ব্যবহারকারীর কাছে কোনও ত্রুটি উপস্থাপন করা উচিত নয় বরং কোডটির "ভিউকন্ট্রোলার সচেতন" অংশগুলিতে তাদের পাস করা উচিত
ভ্লাদ সোরোকা

2
আমি রাজী. তবে এখন অবহেলিতদের সুবিধাগুলি UIAlertViewআমাকে কয়েক দাগে সেই নিয়ম ভাঙতে পরিচালিত করেছিল।
মারে সাগাল

0

আপনি ব্যবহার করতে পারেন এমন 2 টি পদ্ধতি:

UIAlertViewপরিবর্তে ব্যবহার করুন বা 'ইউআইএএকশনশিট' (প্রস্তাবিত নয়, এটি আইওএস 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

আমি আমার অ্যাপ্লিকেশন শ্রেণিতে কিছুটা ব্যক্তিগত প্রকরণের সাথে এই কোডটি ব্যবহার করি

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

সহায়ক শ্রেণি সতর্কতা উইন্ডো তৈরি করুন এবং এর চেয়ে বেশি ব্যবহার করুন

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);
    }
}
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.