子类化 UIAlertController 并遇到运行时错误

Posted

技术标签:

【中文标题】子类化 UIAlertController 并遇到运行时错误【英文标题】:Subclassing UIAlertController and encountering runtime errors 【发布时间】:2014-11-14 17:35:04 【问题描述】:

我是 ios 和 OSX 编程的新手,我决定从 Swift 开始,将 iOS8 API 用于测试应用程序,并尝试一些我在其他环境中使用的编程技术。但是,我遇到了一个奇怪的情况,我希望有人可以识别并帮助我。这涉及到继承 UIAlertController 的困难。

这是我的第一次尝试:

import UIKit

class FubarAlertController: UIAlertController 

    convenience init (message:String) 
        self.init(title: "Alert", message: message, preferredStyle: UIAlertControllerStyle.Alert);
        self.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.Default, handler: nil))
    

但是,我在 Xcode 中收到以下警告,但我不明白 - 在我看来,这些错误似乎是自相矛盾的(请原谅这个不好的双关语)

use of self in delegating initializer before self.init is called
Self.init is't called on all paths in delegating initialiser

然后我尝试了

class FubarAlertController: UIAlertController 

    convenience init (message:String) 
        self.init();
        self.title = "Alert";
        self.message = message;
        self.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.Default, handler: nil))
    

并且没有遇到任何编译时错误。但是,当我像这样从一个简单的 ViewController 使用它时

class FubarController: UIViewController 

    // method invoked when a UIBarButtonItem action takes place 

    @IBAction
    func enterTextButtonAction(sender: UIBarButtonItem) 

        let controller = FubarAlertController(message: "Fubar!");
        presentViewController(controller, animated: true, completion: nil);
       

我再次收到以下运行时错误,我不太明白

*** Terminating app due to uncaught exception 'NSGenericException', 
reason: 'Your application has presented a UIAlertController
(<UiToolKit.TextChangedAlertController: 0x7adf2340>) of style 
UIAlertControllerStyleActionSheet. The modalPresentationStyle of a 
UIAlertController with this style is UIModalPresentationPopover. You 
must provide location information for this popover through the alert 
controller's popoverPresentationController. You must provide either a 
sourceView and sourceRect or a barButtonItem.  If this information is
not known when you present the alert controller, you may provide it in the 
UIPopoverPresentationControllerDelegate method -prepareForPopoverPresentation.'

但是,如果我只是简单地实现 FubarAlertController 而没有任何方法或属性,我可以像这样在界面构建器操作中调用它,就像它是 UIAlertController 一样

class FubarController: UIViewController 

    // method invoked when a UIBarButtonItem action takes place 

    @IBAction
    func enterTextButtonAction(sender: UIBarButtonItem) 
        let controller = FubarAlertController(title: "Alert", message: "Fubar!", preferredStyle: UIAlertControllerStyle.Alert);
        controller.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.Default, handler: nil))
        presentViewController(controller, animated: true, completion: nil);
       
   

...一切都按预期工作 - 没有编译时或运行时错误,但我不明白为什么!

所以我的问题是:

(i) 为什么我不能以我第一次尝试的方式实现便捷的 init 方法?我在这里缺少什么魔法知识?为什么我不能在便利初始化程序中调用 self 上下文中的超类 init 方法?

(ii) 当我第二次实现 UIAlertController 时,为什么会出现运行时错误而不是编译时错误?

非常感谢您能做到这一点并期待一些反馈 - 我很难过!

【问题讨论】:

【参考方案1】:

我知道这是一个老问题,但认为它至少可以使用部分答案。

(I) 我必须和你做同样的事情才能让 UIAlertController 的子类正常工作。我认为简单的答案是“不要这样做”。根据他们的文档,Apple 在技术上不允许 UIAlertController 的子类化:

UIAlertController 类旨在按原样使用,不支持子类化。此类的视图层次结构是私有的,不得修改。

https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIAlertController_class/

(二) 在 iPad 上运行我的 iPhone 应用程序时,我遇到了同样的错误。

*** Terminating app due to uncaught exception 'NSGenericException', 
reason: 'Your application has presented a UIAlertController of style 
UIAlertControllerStyleActionSheet. The modalPresentationStyle of a 
UIAlertController with this style is UIModalPresentationPopover. You 
must provide location information for this popover through the alert 
controller's popoverPresentationController. You must provide either a 
sourceView and sourceRect or a barButtonItem.  If this information is
not known when you present the alert controller, you may provide it in the 
UIPopoverPresentationControllerDelegate method -prepareForPopoverPresentation.'

由于 iPad 上的弹出框不是全屏的,因此 iPad 需要设置 sourceViewsourceRect 属性来确定弹出框应从屏幕的哪个位置显示。

每当您将样式设置为 .ActionSheet 时,将类似这样的代码添加到您的 UIAlertController:

//For iPad, check that there is a popover and set action button as the sender
if let popoverController = controller.popoverPresentationController 
        //Sender will be a UIButton, cast down to UIView
        popoverController.sourceView = sender as? UIView
        popoverController.sourceRect = sender.bounds

您可能需要对此进行调整,具体取决于您的情况。我上面的示例来自一个 IBAction 函数,其中 sender 是一个 UIButton。

【讨论】:

以上是关于子类化 UIAlertController 并遇到运行时错误的主要内容,如果未能解决你的问题,请参考以下文章

Swift:自定义 UIAlertController 视图

UIAlertController 子类问题迅速

子类化 MKTileOverlay 的困难

动态子类化 UIViewController 的错误

UIAlertController 按钮不可见

子类化 UIImageView 并添加 UIControl