私はworking iOS application
ためにsupport iOS8
、私は置き換えていますUIAlertView/UIActionSheet with UIAlertController
。
問題:
UIAlertControllerを表示するには、UIViewControllerクラスのpresentViewControllerメソッドが必要です。
ただし UIAlertViewは、inherited
fromUIView or NSObject
、
私は得ることができない [self presentViewController...]
明白な理由のためのメソッド。
私の仕事:
現在のウィンドウからrootViewControllerを取得してUIAlertControllerを表示しようとしました。
[[[UIApplication sharedApplication] keyWindow].rootViewController presentViewController ...]
しかし、現在のView Controllerに回転サポートがない場合、UIAlertControllerが開いていると回転するなどの回転の問題があります。
質問:
誰かが同じ問題に直面し、安全な解決策を見つけましたか?
はいの場合、いくつかの例を提供するか、ガイドを提供してください
現在(iOS8より前)、ビューオブジェクト内からアラートビューをトリガーしているようです。一般にアラートはアクションとロジックからトリガーされる必要があるため、これはかなり悪い習慣です。そして、そのコードはコントローラーに存在する必要があります。
現在のコードをリファクタリングして、アラートをトリガーするロジックを正しいコントローラーに移動し、self
をコントローラーとして使用してiOS 8に簡単にアップグレードできることをお勧めします。
代わりに、外部オブジェクトからアラートを呼び出す場合は、アラートを呼び出すメソッドにコントローラーを渡します。上流のどこかで、コントローラーの知識が必要です。
今日、本質的に同様の問題を解決しました。 Jageen のように、UIAlertControllerを表示したいが、非UIViewControllerクラスから表示したい状況に陥りました。私の場合、HTTPリクエストの失敗ブロックが実行されたときに警告がポップアップするようにしました。
これは私が使用したものであり、ここにいる友人とは異なり、私にとっては完璧に機能しました。
UIApplication.sharedApplication().keyWindow?.rootViewController?.presentViewController(errorAlert, animated: true, completion: nil)
UIView
クラスのより良い解決策は以下です
ObjectiveC
UIViewController *currentTopVC = [self currentTopViewController];
currentTopVC.presentViewController.........
- (UIViewController *)currentTopViewController
{
UIViewController *topVC = [[[[UIApplication sharedApplication] delegate] window] rootViewController];
while (topVC.presentedViewController)
{
topVC = topVC.presentedViewController;
}
return topVC;
}
スイフト
var topVC = UIApplication.sharedApplication().keyWindow?.rootViewController
while((topVC!.presentedViewController) != nil){
topVC = topVC!.presentedViewController
}
topVC?.presentViewController........
私の解決策は次のとおりです:
スイフト
class alert {
func msg(message: String, title: String = "")
{
let alertView = UIAlertController(title: title, message: message, preferredStyle: .Alert)
alertView.addAction(UIAlertAction(title: "Done", style: .Default, handler: nil))
UIApplication.sharedApplication().keyWindow?.rootViewController?.presentViewController(alertView, animated: true, completion: nil)
}
}
以下に使用例を示します。
let Alert = alert()
Alert.msg("My alert (without title)")
Alert.msg("This is my alert", title: "Warning!")
サブビューにそれを閉じるボタンが含まれている状況がありました。アクションを確認するアラートを表示します。サブビューを削除するために、サブビューを含むView Controllerであるデリゲートにメッセージを送信します
もともと、UIViewからUIAlertViewを提示しました。 UIAlertControllerのリファクタリングは、UIAlertControllerがUIAlertViewのようには表示できないため、次のことを思いつきました(Swiftで、ObjCに簡単に変換できます)。
サブビューにプロトコルを追加します。
protocol MySubviewDelegate {
// called when user taps subview/delete button
// or, you could call it from a gesture handler, etc.
func displayAlert(alert : UIAlertController)
// called when user confirms delete from the alert controller
func shouldRemoveSubview(sender : AnyObject)
}
サブビューのデリゲートを追加し、ボタン/ジェスチャータップのハンドラーを追加します。
class MySubview : UIView {
var subviewDelegate : MySubviewDelegate!
...
func handleTap(sender : AnyObject) {
// set up the alert controller here
var alert = UIAlertController(title: "Confirm Delete",
message: "This action is permanent. Do you wish to continue?",
preferredStyle: UIAlertControllerStyle.Alert)
// Cancel action
// nil handler means "no action if Cancel button selected"
alert.addAction(UIAlertAction(title: "Cancel",
style: UIAlertActionStyle.Cancel,
handler: nil))
// Confirm action
alert.addAction(UIAlertAction(title: "Confirm",
style: UIAlertActionStyle.Default,
handler: { (action : UIAlertAction!) -> Void in
// call delegate method to perform confirmed action, - i.e. remove
self.subviewDelegate.shouldRemoveSubview(self)
}))
// call delegate method to display alert controller
// send alert object to delegate
self.subviewDelegate.displayAlert(alert)
}
}
呼び出しUIViewControllerを、たとえばviewDidLoad()メソッドでサブビューのデリゲートとして設定し、プロトコルメソッドを含めます。
class viewController : UIViewController, MySubviewDelegate {
override func viewDidLoad() {
super.viewDidLoad()
self.subviewDelegate = self
...
}
func displayAlert(alert : UIAlertController) {
presentViewController(alert, animated: true, completion: nil)
}
func shouldRemoveSubview(sender : AnyObject) {
// cast as UIView / MySubview subclass
var subview = sender as MySubview
// remove the subview / perform the desired action
subview.removeFromSuperview()
...
}
...
}
これにより、最上位のView Controllerを見つけたり、View Controllerへの参照をサブビューに渡したりする必要がなくなります(オブジェクト/デリゲート関係以外)。
In Swift 3:
UIApplication.shared.keyWindow?.rootViewController?.present(alertView, animated: true, completion: nil)
Swift 4以上の場合
UIApplication.shared.keyWindow?.rootViewController?.present(alert, animated: true, completion: nil)
一般に、アラートはView Controllerで処理する必要があります。必要なコードの例を次に示します。
Swift
private func displayError(message: String) {
let alertController = UIAlertController(title: nil, message: message, preferredStyle: .alert)
let okayAction = UIAlertAction(title: "Okay", style: .default, handler: nil)
alertController.addAction(okayAction)
present(alertController, animated: true, completion: nil)
}
私は質問がすでに回答されていることを知っています...しかし、私は同じ問題を探していますが、上記の解決策のどれも私にとってはうまくいきませんでした。
最終的に多くの試行錯誤を繰り返した後、非常に簡単で持続可能な解決策を見つけました。
func showError(title: String?, error: String?) {
DispatchQueue.main.async(execute: {
let alert = UIAlertController(title: title, message: error, preferredStyle: UIAlertControllerStyle.alert)
alert.addAction(UIAlertAction(title: "OK", style: .cancel, handler: nil))
CommonMethods.instance.topMostController()?.present(alert, animated: true, completion: nil)
})
}
static let instance = CommonMethods()
fileprivate func topMostController() -> UIViewController? {
var presentedVC = UIApplication.shared.keyWindow?.rootViewController
while let pVC = presentedVC?.presentedViewController {
presentedVC = pVC
}
if presentedVC == nil { }
return presentedVC
}
NSObjectクラスのUIAlertControllerを表示するには、以下のコードを使用します。
UIAlertController * popup = [UIAlertController
alertControllerWithTitle:nil
message:nil
preferredStyle:UIAlertControllerStyleActionSheet];
UIAlertAction* cancel = [UIAlertAction
actionWithTitle:@"Cancel"
style:UIAlertActionStyleCancel
handler:^(UIAlertAction * action) {
[popup dismissViewControllerAnimated:YES completion:nil];
}];
[popup addAction:cancel];
UIViewController *rootViewController = [[Helper shareInstance] topViewController];
[rootViewController presentViewController:popup animated:YES completion:nil];
//グローバルヘルパークラスのメソッドBelowを配置します。
- (UIViewController *)topViewController {
return [self topViewController:[UIApplication sharedApplication].keyWindow.rootViewController];
}
- (UIViewController *)topViewController:(UIViewController *)rootViewController {
if (rootViewController.presentedViewController == nil) {
return rootViewController;
}
if ([rootViewController.presentedViewController isMemberOfClass:[UINavigationController class]]) {
UINavigationController *navigationController = (UINavigationController *)rootViewController.presentedViewController;
UIViewController *lastViewController = [[navigationController viewControllers] lastObject];
return [self topViewController:lastViewController];
}
UIViewController *presentedViewController = (UIViewController *)rootViewController.presentedViewController;
return [self topViewController:presentedViewController];
}
この問題が発生しました。別のView Controllerを表示する最上位のView Controllerを取得するためのコードについては、こちらの SO answer を参照してください。
ほとんどの場合、View ControllerではないオブジェクトからView Controllerを提示するのは悪い習慣ですが、必要な場合もあることに同意します。