UIAlertController 实用程序类无法调用 UITextField 委托或目标,但在 UIViewController 中有效 [英] UIAlertController utility class fails to call UITextField delegate or target but works in UIViewController

查看:29
本文介绍了UIAlertController 实用程序类无法调用 UITextField 委托或目标,但在 UIViewController 中有效的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我遇到了一个奇怪的问题,也许只是对 Swift 3.0/iOS 10 缺乏了解,所以希望您能指导我走向正确的方向或向我解释我做错了什么.

I encountered a strange issue and maybe it is only a lack of knowledge about Swift 3.0 / iOS 10 so hopefully you can guide me into the right direction or explain to me what I'm doing wrong.

目前的运作方式

我正在尝试创建一个样式为 .alert 的 UIAlertController,以便我可以为我的应用程序获取用户文本输入.我的要求是文本不能为空,如果之前有文本,则必须不同.

I am trying to create a UIAlertController with style .alert so I can get a user text input for my app. My requirements are that the text must not be empty and if there was a text there before, it must be different.

我可以使用以下代码来实现我想要的:

I can use the following code to achieve what I want:

//This function gets called by a UIAlertController of style .actionSheet
func renameDevice(action: UIAlertAction) {

    //The AlertController
    let alertController = UIAlertController(title: "Enter Name",
                                            message: "Please enter the new name for this device.",
                                            preferredStyle: .alert)

    //The cancel button
    let cancelAction = UIAlertAction(title: "Cancel", style: .cancel)

    //The confirm button. Make sure to deactivate on first start
    let confirmAction = UIAlertAction(title: "Ok", style: .default, handler: { action in
        self.renameDevice(newName: alertController.textFields?.first?.text)
    })

    //Configure the user input UITextField
    alertController.addTextField { textField in
        log.debug("Setting up AlertDialog target")
        textField.placeholder = "Enter Name"
        textField.text = self.device.getName()
        textField.addTarget(self, action: #selector(self.textFieldDidChange(_:)), for: .editingChanged)
    }
    //Disable the OK button so that the user first has to change the text
    confirmAction.isEnabled = false
    self.confirmAction = confirmAction
    //Add the actions to the AlertController
    alertController.addAction(cancelAction)
    alertController.addAction(confirmAction)
    present(alertController, animated: true, completion: nil)

}
var confirmAction: UIAlertAction?

func textFieldDidChange(_ textField: UITextField){
    log.debug("IT CHAGNED!=!=!=!=!")
    if let text = textField.text {
        if !text.isEmpty && text != self.device.getName() {
            confirmAction?.isEnabled = true
            return
        }
    }
    confirmAction?.isEnabled = false
}

//Finally this code gets executed if the OK button was pressed
func renameDevice(newName: String?){ ... }

我希望它如何工作

到目前为止一切都很好,但我要在不同的地方要求用户输入文本,所以我想使用一个实用程序类来为我处理所有这些事情.最终调用应如下所示:

So far so good but I'm going to ask the user for a text input at various places so I want to use a utility class to handle all this stuff for me. The final call shall look like this:

func renameDevice(action: UIAlertAction) {
     MyPopUp().presentTextDialog(title: "Enter Name",
                                 message: "Please enter the new name for this device.",
                                 placeholder: "New Name",
                                 previousText: self.device.getName(),
                                 confirmButton: "Rename",
                                 cancelButton: "Cancel",
                                 viewController: self){ input: String in
        //do something with the input, e. g. call self.renameDevice(newName: input)

    }

我想出了什么

所以我在这个小类中实现了所有内容:

So I implemented everything in this little class:

class MyPopUp: NSObject {

var confirmAction: UIAlertAction!
var previousText: String?
var textField: UITextField?

func presentTextDialog(title: String, message: String?, placeholder: String?, previousText: String?, confirmButton: String, cancelButton: String, viewController: UIViewController, handler: ((String?) -> Swift.Void)? = nil) {

    //The AlertController
    let alertController = UIAlertController(title: title, message: message, preferredStyle: .alert)

    //The cancel button
    let cancelAction = UIAlertAction(title: cancelButton, style: .cancel)

    //The confirm button. Make sure to deactivate on first start
    confirmAction = UIAlertAction(title: confirmButton, style: .default, handler: { action in
        handler?(alertController.textFields?.first?.text)
    })

    //Configure the user input UITextField
    alertController.addTextField { textField in
        log.debug("Setting up AlertDialog target")
        self.textField = textField
    }

    //Set placeholder if necessary
    if let placeholder = placeholder {
        self.textField?.placeholder = placeholder
    }

    //Set original text if necessary
    if let previousText = previousText {
        self.textField?.text = previousText
    }

    //Set the target for our textfield
    self.textField?.addTarget(self, action: #selector(textChanged), for: .editingChanged)

    log.debug("It appears that our textfield \(self.textField) has targets: \(self.textField?.allTargets)")

    //Store the original text for a later comparison with the new entered text
    self.previousText = previousText

    //Disable the OK button so that the user first has to change the text
    confirmAction.isEnabled = false

    //Add the actions to the AlertController
    alertController.addAction(cancelAction)
    alertController.addAction(confirmAction)
    viewController.present(alertController, animated: true, completion: nil)
}

func textChanged() {
    if let text = textField?.text {
        if !text.isEmpty && text != previousText {
            confirmAction.isEnabled = true
            return
        }
    }
    confirmAction.isEnabled = false
}
}

问题

我的问题是,无论我在哪里尝试为 UIAlertController 的 UITextField 设置目标,它都不会执行我的目标.我尝试在 alertController.addTextField{} 中设置 TextFields 委托,并在那里设置目标.最让我困惑的问题是设置占位符和原始文本工作正常,但从未调用委托或目标函数.为什么相同的代码在 UIViewController 中执行时有效,但在实用程序类中执行时无效?

My problem is that no matter where I try to set the target for the UITextField of the UIAlertController, it never executes my target. I tried setting the TextFields delegate in alertController.addTextField{} as well as setting the target there. The issue which confuses me the most is that setting the placeholder and original text works just fine but delegate or target functions are never called. Why does the same code works when executed in a UIViewController but does not work when executed in a utility class?

解决方案(更新)

显然我犯了一个错误.在我的视图控制器中,我创建了一个 MyPopUp 实例并在其上调用 present() 函数.

Apparently I made a mistake. In my viewcontroller, I create an instance of MyPopUp and call the present() function on it.

MyPopUp().presentTextDialog(title: "Enter Name",
                             message: "Please enter the new name for this device.",
                             placeholder: "New Name",
                             previousText: self.device.getName(),
                             confirmButton: "Rename",
                             cancelButton: "Cancel",
                             viewController: self)

在 presentTextDialog() 中,我认为将 MyPopUp 的当前实例设置为委托/目标就足够了,但似乎 MyPopUp 实例会立即释放,因此从未调用过.我非常简单的解决方法是在实例变量中创建 MyPopUp 实例,并在需要时调用当前方法.

In the presentTextDialog() I thought setting the current instance of MyPopUp as the delegate/target would be enough but it seems that the MyPopUp instance is released immediately and therefore never called. My very simple workaround is to create the MyPopUp instance in an instance variable and call the present method whenever I need to.

let popup = MyPopUp()
func renameDevice(action: UIAlertAction) {
    popup.presentTextDialog(...){ userinput in
        renameDevice(newName: userinput)
    }
}

推荐答案

好吧,这正是我做错的地方.

Okay so here's exactly what I did wrong.

  1. 我创建了一个实用程序类,我必须实例化

类本身基本上是空的,它的唯一目的是成为 UITextField 的目标或委托

The class itself was basically empty and it's only purpose was to be the target or delegate of the UITextField

我实例化了这个类并立即调用了演示函数没有保留引用

I instantiated the class and immediately called the presentation function without keeping a reference around

通过不保留对我的实例的引用,在我的视图控制器中呈现 UIAlertController 后,该对象必须立即被释放.

By not keeping a reference to my instance, the object must have gotten released immediately after presenting the UIAlertController in my viewcontroller.

解决方案:只需在您的视图控制器中保留一个引用即可.当然局部变量不行.我将引用存储在我的视图控制器的实例变量中,但这感觉不是很迅速".我仍然是 swift 的初学者,也许我的思想被其他语言(java,c#)破坏"了.我可能会首先使我的实用程序类成为单例或为 UIViewController 创建一个扩展来显示警报.如果您有其他好的想法,请随时教给我 :)

Solution: Just keep a reference around in your viewcontroller. Of course a local variable won't do. I store the reference in an instance variable of my viewcontroller but this doesn't feel very "swifty". I'm still a beginner in swift and maybe my mind is "damaged" by other languages (java, c#). I will probably start by making my utility class a singleton or creating an extension for UIViewController to present the alert. If you have other good ideas feel free to teach them to me :)

这篇关于UIAlertController 实用程序类无法调用 UITextField 委托或目标,但在 UIViewController 中有效的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆