Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/ios/98.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Ios UIAlertController实用程序类无法调用UITextField委托或目标,但可在UIViewController中工作_Ios_Uitextfield_Swift3_Uialertcontroller_Uitextfielddelegate - Fatal编程技术网

Ios UIAlertController实用程序类无法调用UITextField委托或目标,但可在UIViewController中工作

Ios UIAlertController实用程序类无法调用UITextField委托或目标,但可在UIViewController中工作,ios,uitextfield,swift3,uialertcontroller,uitextfielddelegate,Ios,Uitextfield,Swift3,Uialertcontroller,Uitextfielddelegate,我遇到了一个奇怪的问题,可能只是对Swift 3.0/iOS 10缺乏了解,所以希望您能引导我走向正确的方向,或者向我解释我做错了什么 它当前的工作方式 我正在尝试使用style.alert创建UIAlertController,以便为我的应用程序获取用户文本输入。我的要求是文本不能为空,如果之前有文本,则必须不同 我可以使用以下代码实现我想要的: //This function gets called by a UIAlertController of style .actionSheet f

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

它当前的工作方式

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

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

//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?){ ... }
我希望它如何工作

到目前为止还不错,但是我会要求用户在不同的地方输入文本,所以我想使用一个实用程序类来为我处理所有这些东西。最终调用应如下所示:

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)

    }
我想到了什么

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

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中执行时有效,但在实用程序类中执行时无效

解决方案(更新)

显然我犯了一个错误。在我的viewcontroller中,我创建了MyPopUp的一个实例,并对其调用present()函数

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实例,并在需要时调用present方法

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

而不是在NSObject类中显示对话。您必须使用代理和协议来显示警报。用这个替换你的代码。在视图控制器中,我们有一个名为renameDevice的函数。您可以在此处显示警报

MyPopUp.swift

import UIKit

class MyPopUp: NSObject {
    var confirmAction: UIAlertAction!
    var previousText: String?
    var textField: UITextField?
    var delegate : MyPopUpDelegate!


    func textChanged() {
        if let text = textField?.text {
            if !text.isEmpty && text != previousText {
                confirmAction.isEnabled = true
                return
            }
        }
        confirmAction.isEnabled = false
    }
}
protocol MyPopUpDelegate {
    func renameDevice(action: UIAlertAction)
}
import UIKit

class ViewController: UIViewController,MyPopUpDelegate {

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.



    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

    func renameDevice(action: UIAlertAction) {

    // Add dialogue that you wnat to create.

    }

}
ViewController.swift

import UIKit

class MyPopUp: NSObject {
    var confirmAction: UIAlertAction!
    var previousText: String?
    var textField: UITextField?
    var delegate : MyPopUpDelegate!


    func textChanged() {
        if let text = textField?.text {
            if !text.isEmpty && text != previousText {
                confirmAction.isEnabled = true
                return
            }
        }
        confirmAction.isEnabled = false
    }
}
protocol MyPopUpDelegate {
    func renameDevice(action: UIAlertAction)
}
import UIKit

class ViewController: UIViewController,MyPopUpDelegate {

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.



    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

    func renameDevice(action: UIAlertAction) {

    // Add dialogue that you wnat to create.

    }

}

MyPopUp
类中,首先需要遵循
UITextFieldDelegate
这样的方法

class MyPopUp:NSObject,UITextFieldDelegate {
然后,在将UITextField添加到警报时,需要像下面这样将委托设置为该UITextField

alertController.addTextField { textField in
    log.debug("Setting up AlertDialog target")
    textField.delegate = self
    self.textField = textField
}
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
然后,您需要实现UITextField委托方法来获得UITextField中的更改,如下所示

alertController.addTextField { textField in
    log.debug("Setting up AlertDialog target")
    textField.delegate = self
    self.textField = textField
}
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {

这将解决您的问题。同时检查

好的,下面就是我做错的地方

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

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

  • 我实例化了这个类,并立即调用了presentation函数,而没有保留引用

  • 通过不保留对我的实例的引用,对象必须在我的viewcontroller中显示UIAlertController后立即释放


    解决方案:只需在viewcontroller中保留一个引用即可。当然,局部变量不行。我将引用存储在viewcontroller的一个实例变量中,但这感觉不是很“快速”。我仍然是swift的初学者,也许我的思维被其他语言(java、c#)所“损伤”。我可能会首先将我的实用程序类设置为singleton,或者为UIViewController创建一个扩展来显示警报。如果您有其他好主意,请随意教我:)

    您需要在哪个类中调用您的委托或目标?您是否可以向视图控制器类显示您试图调用这些委托的位置?我想在我的UIViewController类中调用“MyPopUp().presentTextDialog(…){input in}MyPopUp类将创建UIAlertController并将其显示在ViewController中当用户输入时,我想激活“OK“按钮,如果按下“确定”按钮,我想在我的viewcontroller中调用闭包表示UIAlertController不是问题所在,因为我在函数调用中引用了viewcontroller,然后在presentTextDialog的最后一行执行viewcontroller.present(alertController,动画:true)。问题是,如果我通过弹出类调用我的委托/目标,UITextField永远不会调用它。这就是问题所在:为什么在视图控制器中使用完全相同的代码时有效,而在实用程序类中使用时无效?我是否遗漏了一些特殊的目标c内容,比如某个协议或其他什么?你在哪里给代表打电话。我看不出在示例代码中我没有使用委托,而是使用了textField.addTarget()代替headi。我尝试了这两种方法,向textField添加了一个目标,并设置了它的委托。这两种方法都不可行,您在哪里调用重命名设备函数?这正是我所做的。正如我提到的,我尝试了两种方法。我尝试了委托方法和addTarget方法,但两种方法都不起作用……您建议的答案与我的工作示例大致相同。但是,我想把它放在一个实用程序类中,这样我就不必每次在每个ViewControllert中都实现委托模式。如果有一个战利品,我就浪费了3个小时。