SwiftUI中如何访问onSubmit修饰器传入的action参数?
我基于UITextField实现了自定义的MyTextField(底层实现无需讨论),现有如下代码结构:
Form { MyTextField() } .onSubmit { // 需要触发的逻辑 }
SwiftUI的onSubmit修饰器定义如下:
extension View { public func onSubmit(of triggers: SubmitTriggers = .text, _ action: @escaping (() -> Void)) -> some View }
当键盘的Return按钮按下时,我能收到UITextField的代理回调,希望在此回调中触发onSubmit传入的action。同时想了解:从更广泛的角度,如何访问View协议实现的视图修饰器所传入的任意参数?这些参数存储在何处?
一、触发Form的onSubmit动作
SwiftUI中onSubmit(针对.text触发源)的提交动作是通过**环境(Environment)**传递的,你可以在MyTextField中读取对应的环境值,再在UITextField的代理回调中调用它,具体步骤如下:
- 在
MyTextField中添加环境属性,读取文本提交动作:
struct MyTextField: UIViewRepresentable { // 读取系统文本类型的提交动作环境值 @Environment(\.submitHandler) private var submitHandler func makeUIView(context: Context) -> UITextField { let textField = UITextField() textField.delegate = context.coordinator return textField } func updateUIView(_ uiView: UITextField, context: Context) {} func makeCoordinator() -> Coordinator { Coordinator(self) } class Coordinator: NSObject, UITextFieldDelegate { let parent: MyTextField init(_ parent: MyTextField) { self.parent = parent } func textFieldShouldReturn(_ textField: UITextField) -> Bool { // 触发onSubmit传入的动作 parent.submitHandler() textField.resignFirstResponder() return true } } }
- 若需要兼容旧版本SwiftUI,可显式声明对应的环境键(系统内部已默认存在该键,此为示例):
private struct SubmitHandlerEnvironmentKey: EnvironmentKey { static let defaultValue: () -> Void = {} } extension EnvironmentValues { var submitHandler: () -> Void { get { self[SubmitHandlerEnvironmentKey.self] } set { self[SubmitHandlerEnvironmentKey.self] = newValue } } }
完成上述修改后,当UITextField的Return按钮按下时,就能触发Form上onSubmit传入的action。
二、视图修饰器参数的存储与访问
视图修饰器的本质是返回一个包装后的View,传入的参数主要通过两种方式存储和传递:
方式1:环境(Environment)传递
像onSubmit、font、foregroundColor这类全局生效的系统修饰器,通常会把参数存入Environment。子视图可以通过@Environment属性直接读取对应的值,这是SwiftUI跨视图传递配置的标准方式。方式2:包装View的属性存储
自定义修饰器一般会创建一个自定义View结构体,将传入的参数作为该结构体的属性存储。示例如下:
extension View { func customOverlay(text: String) -> some View { CustomOverlayView(content: self, overlayText: text) } } struct CustomOverlayView<Content: View>: View { let content: Content let overlayText: String // 修饰器参数存储在此处 var body: some View { content .overlay(Text(overlayText)) } }
这种情况下,只有包装View能直接访问参数,如果子视图需要使用,需要通过环境或者显式传参的方式把参数向下传递。
总结
- 系统修饰器的参数优先通过
@Environment读取; - 自定义修饰器的参数,要么通过环境传递给子视图,要么在包装View中直接使用,若子视图需要则需显式传递。
内容的提问来源于stack exchange,提问作者Davyd Geyl

