0

У меня возникла странная проблема, и, возможно, это всего лишь нехватка знаний о Swift 3.0/iOS 10, поэтому, надеюсь, вы можете направлять меня в правильном направлении или объяснять меня то, что я делаю неправильно.Утилита UIAlertController не может вызвать делегат UITextField или целевой объект, но работает в UIViewController

КАК ЭТО РАБОТАЕТ НАСТОЯЩЕЕ

Я пытаюсь создать UIAlertController со стилем .alert, так что я могу получить ввод текста пользователя для моего приложения. Мои требования заключаются в том, что текст не должен быть пустым, и если раньше там был текст, он должен быть другим.

я могу использовать следующий код, чтобы достичь того, чего я хочу:

//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 
} 
} 

ПРОБЛЕМА

Моя проблема заключается в том, что независимо от того, где я пытаюсь установить цель для UITextField t он UIAlertController, он никогда не выполняет мою цель. Я попытался установить делегат TextFields в alertController.addTextField {}, а также установить там цель. Проблема, которая меня смущает больше всего, заключается в том, что установка заполнитель и оригинальный текст работает просто отлично, но функции делегирования или целевые функции никогда не вызываются. Почему же тот же код работает при выполнении в UIViewController, но не работает, когда выполняется в классе утилиты?

РЕШЕНИЕ (ДОПОЛНЕНО)

Видимо, я сделал ошибку. В моем контроллере view я создаю экземпляр 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 в переменной экземпляра и вызвать настоящий метод всякий раз, когда мне нужно.

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

, в каком классе вам нужно позвонить своему делегату или цели? – KKRocks

+0

Вы покажете класс контроллера вида, где вы пытаетесь вызвать этих делегатов? – User511

+0

Из моего класса UIViewController я хочу сделать вызов MyPopUp(). PresentTextDialog (...) {input in} – xxtesaxx

ответ

0

Хорошо, так вот именно то, что я сделал неправильно.

  1. Я создал класс утилиты, которые я должен был экземпляр

  2. Сам класс был в основном пуст, и это единственная цель быть объектом или делегировать из UITextField

  3. Я создал экземпляр класса и сразу назвал функцию представления без сохранения ссылки вокруг

Не сохраняя ссылки на мой экземпляр, объект должен быть выпущен сразу же после представления UIAlertController в моем диспетчере просмотра.

Решение: Просто держите ссылку в своем диспетчере просмотра. Конечно, локальная переменная не будет делать. Я сохраняю ссылку в переменной экземпляра моего диспетчера представлений, но это не кажется очень «экономным». Я все еще начинаю стремительно и, возможно, мой ум «поврежден» другими языками (java, C#). Вероятно, я начну с того, что сделаю мой класс утилиты singleton или создав расширение для UIViewController для представления предупреждения. Если у вас есть другие хорошие идеи, не стесняйтесь учить их мне :)

0

Вместо представления диалога в классе 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) 
} 

ViewController.скоро

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. 

    } 

} 
+0

. Представление UIAlertController не является проблемой, так как я помещал ссылку на viewcontroller в вызове функции, а затем выполните viewController.present (alertController, animated: true) в последней строке presentTextDialog. Проблема в том, что UITextField никогда не вызывает мой делегат/цель, если я делаю это через мой popup-класс. Возникает вопрос: почему работает тот же самый код когда используется в контроллере представления, но не работает, когда используется в классе утилиты? Я пропустил какой-то особый объект цели c, например, определенный протокол или сом ething? – xxtesaxx

+0

Где вы звоните делегату. Я не могу видеть, что – User511

+0

В примере кода я не использую делегат, но я использую textField.addTarget() вместо – xxtesaxx

0

В вашем MyPopUp классе сначала вам нужно соответствовать UITextFieldDelegate методы как этого

class MyPopUp:NSObject,UITextFieldDelegate { 

затем при добавлении вашего UITextField, чтобы предупредить вам необходимо установить делегат, что UITextField как это

alertController.addTextField { textField in 
    log.debug("Setting up AlertDialog target") 
    textField.delegate = self 
    self.textField = textField 
} 

тогда вам нужно реализовать метод UITextField Delegate, чтобы получить изменение в вашем UITextField, например,

func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool { 

Это позволит решить ваши problem.Also проверить this

+0

Ну, это именно то, что я сделал. Как я уже говорил, я пробовал оба подхода. Я попробовал подход делегата и подход addTarget, и ни один из них не работает ... – xxtesaxx

+0

Ответ, который вы предлагаете, более или менее совпадает с моим рабочим примером. Тем не менее, я хочу поместить это в класс утилиты, чтобы я * не * должен реализовывать шаблон делегирования каждый раз в каждом диспетчере представлений – xxtesaxx

 Смежные вопросы

  • Нет связанных вопросов^_^