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

时间:2014-11-14 17:35:04

标签: ios swift xcode6

我是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)为什么我不能以第一次尝试的方式实现便利初始化方法?我在这里错过了什么神奇的知识?为什么我不能在便捷初始化器中在self的上下文中调用超类init方法?

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

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

1 个答案:

答案 0 :(得分:2)

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

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

  

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

history views

(II) 我在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。