SwiftUI:UIAlertController 的 textField 在 UIAlertAction 中没有响应

Posted

技术标签:

【中文标题】SwiftUI:UIAlertController 的 textField 在 UIAlertAction 中没有响应【英文标题】:SwiftUI: UIAlertController's textField does not responding in UIAlertAction 【发布时间】:2020-04-17 07:35:46 【问题描述】:

我需要使用UIAlertContoller,因为SwiftUI 的Alert 不支持TextField

由于各种原因(可访问性、DynamicType、暗模式支持等),我不能使用自定义创建的 AlertView。

基本思想是,SwiftUI 的警报必须保持 TextField 并且输入的文本必须反射回来才能使用。

我通过遵循 UIViewControllerRepresentable 创建了一个 SwiftUI view 以下是工作代码。

struct AlertControl: UIViewControllerRepresentable 

    typealias UIViewControllerType = UIAlertController

    @Binding var textString: String
    @Binding var show: Bool

    var title: String
    var message: String

    func makeUIViewController(context: UIViewControllerRepresentableContext<AlertControl>) -> UIAlertController 

        let alert = UIAlertController(title: title, message: message, preferredStyle: .alert)

        alert.addTextField  textField in
            textField.placeholder = "Enter some text"
        

        let cancelAction = UIAlertAction(title: "cancel", style: .destructive)  (action) in
            self.show = false
        

        let submitAction = UIAlertAction(title: "Submit", style: .default)  (action) in
            self.show = false
        

        alert.addAction(cancelAction)
        alert.addAction(submitAction)

        return alert
    

    func updateUIViewController(_ uiViewController: UIAlertController, context: UIViewControllerRepresentableContext<AlertControl>) 

    

    func makeCoordinator() -> AlertControl.Coordinator 
        Coordinator(self)
    

    class Coordinator: NSObject, UITextFieldDelegate 

        var control: AlertControl

        init(_ control: AlertControl) 
            self.control = control
        

        func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool 
            if let text = textField.text 
                self.control.textString = text
            

            return true
        
    


// SwiftUI View in some content view
 AlertControl(textString: self.$text,
                             show: self.$showAlert,
                             title: "Title goes here",
                             message: "Message goes here")

问题:

点击警报操作时没有任何活动。我设置了断点来检查,但它从来没有打到那里。

即使是UITextFieldDelegate 的函数也没有命中。

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

编辑:cancelActionsubmitAction 不会在点击这些字段时触发。

【问题讨论】:

【参考方案1】:

这是适用于解决方案的完整演示模块。使用 Xcode 13.3 / ios 14.0+ 测试

另见重要的 cmets inline

import SwiftUI

struct CustomAlert: UIViewControllerRepresentable 
    @Binding var text: String
    @Binding var isPresented: Bool
    var title: String
    var message: String
    var onSubmit: (() -> ())?
    var onCancel: (() -> ())?
    
    func makeUIViewController(context: Context) -> UIViewController 
        UIViewController() // holder controller - required to present alert
    
    
    func updateUIViewController(_ uiViewController: UIViewController, context: Context) 
        guard context.coordinator.alert == nil else  return 
        if isPresented 
            let alert = makeAlert(with: context)
            context.coordinator.alert = alert
            Task 
                uiViewController.present(alert, animated: true) 
                    self.isPresented = false  // hide holder after alert dismiss
                    context.coordinator.alert = nil
                
            
        
    
    
    private func makeAlert(with context: Context) -> UIAlertController 
        let alert = UIAlertController(title: title, message: message, preferredStyle: .alert)
        alert.addTextField  textField in
            textField.placeholder = "Enter some text"
            textField.text = text                       // << initial value if any
            textField.delegate = context.coordinator    // << use coordinator as delegate
        
        alert.addAction(cancelAction)
        alert.addAction(submitAction)
        return alert
    
    
    private var cancelAction: UIAlertAction 
        UIAlertAction(title: "cancel", style: .destructive)  _ in
            onCancel?()
        
    
    
    private var submitAction: UIAlertAction 
        UIAlertAction(title: "Submit", style: .default)  _ in
            onSubmit?()
        
    
    
    func makeCoordinator() -> Coordinator 
        Coordinator(self)
    
    
    class Coordinator: NSObject, UITextFieldDelegate 
        var parent: CustomAlert
        var alert: UIAlertController?
        
        init(_ parent: CustomAlert) 
            self.parent = parent
        
        
        func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool 
            if let text = textField.text as NSString? 
                parent.text = text.replacingCharacters(in: range, with: string)
             else 
                parent.text = ""
            
            return true
        
    


struct DemoCustomAlert: View 
    @State private var text = ""
    @State private var alertIsDisplayed = false
    
    var body: some View 
        VStack 
            Button 
                alertIsDisplayed.toggle()
             label: 
                Text("Alert")
            
            .background(
                CustomAlert(
                    text: $text,
                    isPresented: $alertIsDisplayed,
                    title: "Title goes here",
                    message: "Message goes here",
                    onSubmit:  print("didSubmit") ,
                    onCancel:  print("didCancel") 
                )
            )
            Text(text)
        
    

【讨论】:

感谢您的回复,但这并不能解决实际问题。当点击这些按钮时,cancelActionsubmitAction 不会触发。 感谢@Asperi,这按预期工作,但有时屏幕冻结&我在控制台中看到以下警告。 2020-04-17 22:11:27.121106+0530 SwiftUI Alert Controller[98884:3206838] Warning: Attempt to present &lt;UIAlertController: 0x7f8cdd87e000&gt; on &lt;UIViewController: 0x7f8cdd704fd0&gt; which is already presenting (null) 总的来说它解决了这个问题。再次感谢。 @Nasir,为已显示的警报添加保护以避免意外刷新 感谢@Asperi,但我看不出协调器中的守卫声明和警报实例的目的是什么,即使有上述这些更改,也会出现警告。似乎出现了警报控制器,但它从未被解雇。在某些地方,我们需要关闭呈现的警报。让我知道你的想法,我正在努力,一旦我得出任何结论,我会更新。顺便说一句,感谢您的努力。 :) @Nasir,你所说的“从不被解雇”是什么意思?正如我测试和演示的那样,警报按预期工作,并在任何按钮单击时消失。您是否使用不同的 Xcode 版本或条件? guard 是必需的,因为我们无法控制 SwiftUI 何时以可表示形式调用更新,因此为避免在警报已显示时发生冲突,我添加了保护并修改代码以存储警报。当我使用这种方法进行测试时,我不再在控制台中收到任何警告。

以上是关于SwiftUI:UIAlertController 的 textField 在 UIAlertAction 中没有响应的主要内容,如果未能解决你的问题,请参考以下文章

iOS核心笔记——UIAlertController

从第一个 UIAlertController 打开第二个 UIAlertController

swift UIAlertController使用 UIAlertController的宽度 为270

从 AppDelegate 到 PresentedViewController 的警报:“尝试在...上呈现 UIAlertController 已经在呈现 UIAlertController”

禁止UIAlertController的dimiss

UIAlertController的popover变形