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