使自定义 SwiftUI View 适应内置修饰符
Making a custom SwiftUI View adapt to built-in modifiers
我正在努力为 SwiftUI 编写自己的 BetterTextField
视图,因为内置的 TextField
在几个方面都缺乏。也就是说,我想支持延迟绑定(仅在定位焦点时更新绑定值,而不是在每次按键后强制重绘)、编程 focusing/responder 控制以及 UIKit UITextField
的一些 SwiftUI 缺乏的其他功能.
所以我创建了一个自定义的 UIViewRepresentable
并以协调器作为 UITextFieldDelegate
,并且工作正常。但是,为了与其他视图保持一致,我真的想让我的自定义文本字段适应某些现有的 SwiftUI 修饰符。
例如:
// Here's my content view
struct ContentView: View {
var body: some View {
BetterTextField("Username", text: $username)
// I want to adapt the view to this modifier
.textFieldStyle(RoundedBorderTextFieldStyle())
}
}
// Here's my (simplified) custom text field view
struct BetterTextField: UIViewRepresentable {
var title: String
@Binding var text: String
init(_ title: String, text: Binding<String>) {
self.title = title
self._text = text
}
func makeUIView(context: Context) -> UITextField {
let textField = UITextField()
textField.placeholder = title
return textField
}
func updateUIView(_ view: UITextField, context: Context) {
view.text = text
// How can I check for the .textFieldStyle() modifier here and set the corresponding UIKit style accordingly?
view.borderStyle = .roundedRect
}
}
如评论所述,如何调整 UITextField
的 borderStyle
属性 以匹配视图修饰符?
更一般地说,如何检查是否存在修饰符和 return 样式适当的自定义视图(例如 .bold()
可能转换为属性文本)?
@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *)
extension View {
/// Sets the style for `TextField` within the environment of `self`.
public func textFieldStyle<S>(_ style: S) -> some View where S : TextFieldStyle
}
见备注
在 self
的环境中设置 TextField
的样式
UIViewRepresentable 继承自 View 但在 'self'
中没有任何 TextField
.bold, .italic ... 是 Font 的修饰符,而不是通用 View 的修饰符。假设
Image("image001").italic()
效果不佳。
去抖动见Debounced Property Wrapper
'delayed' 绑定见
/// Creates an instance with a `Text` label generated from a localized title
/// string.
///
/// - Parameters:
/// - titleKey: The key for the localized title of `self`, describing
/// its purpose.
/// - text: The text to be displayed and edited.
/// - onEditingChanged: An `Action` that will be called when the user
/// begins editing `text` and after the user finishes editing `text`,
/// passing a `Bool` indicating whether `self` is currently being edited
/// or not.
/// - onCommit: The action to perform when the user performs an action
/// (usually the return key) while the `TextField` has focus.
public init(_ titleKey: LocalizedStringKey, text: Binding<String>, onEditingChanged: @escaping (Bool) -> Void = { _ in }, onCommit: @escaping () -> Void = {})
'delayed' 绑定示例
import SwiftUI
struct MyTextField<S>: View where S: StringProtocol {
let label: S
@State private var __text = ""
@Binding var text: String
var body: some View {
TextField(label, text: $__text, onEditingChanged: { (e) in
}) {
self.text = self.__text
}
}
}
struct ContentView: View {
@State var text = " "
var body: some View {
VStack {
MyTextField(label: "label", text: $text).textFieldStyle(RoundedBorderTextFieldStyle())
Text(text)
}.padding()
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
如果您需要不同的字体和.bold,请使用
MyTextField(label: "label", text: $text).textFieldStyle(RoundedBorderTextFieldStyle()).font(Font.title.bold())
或
MyTextField(label: "label", text: $text).font(Font.title.bold()).textFieldStyle(RoundedBorderTextFieldStyle())
View
修饰符只是 return 又 some View
的函数,因此您可以实现对任何修饰符的支持,符合您认为适合您的自定义类型的任何协议。您的控件在每个已实现的修改器上的行为取决于您。
下面是对 textFieldStyle
修饰符的简单演示支持,它使您的 ContentView
渲染 BetterTextField
取决于添加或删除的圆形矩形样式修饰符。
struct BetterTextField: UIViewRepresentable {
var title: String
@Binding var text: String
private let textField = UITextField()
init(_ title: String, text: Binding<String>) {
self.title = title
self._text = text
}
func makeUIView(context: Context) -> UITextField {
textField.placeholder = title
return textField
}
func updateUIView(_ view: UITextField, context: Context) {
view.text = text
}
}
extension BetterTextField {
func textFieldStyle<S>(_ style: S) -> some View where S : TextFieldStyle {
if style is RoundedBorderTextFieldStyle {
self.textField.borderStyle = .roundedRect
}
return self
}
}
我正在努力为 SwiftUI 编写自己的 BetterTextField
视图,因为内置的 TextField
在几个方面都缺乏。也就是说,我想支持延迟绑定(仅在定位焦点时更新绑定值,而不是在每次按键后强制重绘)、编程 focusing/responder 控制以及 UIKit UITextField
的一些 SwiftUI 缺乏的其他功能.
所以我创建了一个自定义的 UIViewRepresentable
并以协调器作为 UITextFieldDelegate
,并且工作正常。但是,为了与其他视图保持一致,我真的想让我的自定义文本字段适应某些现有的 SwiftUI 修饰符。
例如:
// Here's my content view
struct ContentView: View {
var body: some View {
BetterTextField("Username", text: $username)
// I want to adapt the view to this modifier
.textFieldStyle(RoundedBorderTextFieldStyle())
}
}
// Here's my (simplified) custom text field view
struct BetterTextField: UIViewRepresentable {
var title: String
@Binding var text: String
init(_ title: String, text: Binding<String>) {
self.title = title
self._text = text
}
func makeUIView(context: Context) -> UITextField {
let textField = UITextField()
textField.placeholder = title
return textField
}
func updateUIView(_ view: UITextField, context: Context) {
view.text = text
// How can I check for the .textFieldStyle() modifier here and set the corresponding UIKit style accordingly?
view.borderStyle = .roundedRect
}
}
如评论所述,如何调整 UITextField
的 borderStyle
属性 以匹配视图修饰符?
更一般地说,如何检查是否存在修饰符和 return 样式适当的自定义视图(例如 .bold()
可能转换为属性文本)?
@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *)
extension View {
/// Sets the style for `TextField` within the environment of `self`.
public func textFieldStyle<S>(_ style: S) -> some View where S : TextFieldStyle
}
见备注
在 self
TextField
的样式
UIViewRepresentable 继承自 View 但在 'self'
中没有任何 TextField.bold, .italic ... 是 Font 的修饰符,而不是通用 View 的修饰符。假设
Image("image001").italic()
效果不佳。
去抖动见Debounced Property Wrapper
'delayed' 绑定见
/// Creates an instance with a `Text` label generated from a localized title
/// string.
///
/// - Parameters:
/// - titleKey: The key for the localized title of `self`, describing
/// its purpose.
/// - text: The text to be displayed and edited.
/// - onEditingChanged: An `Action` that will be called when the user
/// begins editing `text` and after the user finishes editing `text`,
/// passing a `Bool` indicating whether `self` is currently being edited
/// or not.
/// - onCommit: The action to perform when the user performs an action
/// (usually the return key) while the `TextField` has focus.
public init(_ titleKey: LocalizedStringKey, text: Binding<String>, onEditingChanged: @escaping (Bool) -> Void = { _ in }, onCommit: @escaping () -> Void = {})
'delayed' 绑定示例
import SwiftUI
struct MyTextField<S>: View where S: StringProtocol {
let label: S
@State private var __text = ""
@Binding var text: String
var body: some View {
TextField(label, text: $__text, onEditingChanged: { (e) in
}) {
self.text = self.__text
}
}
}
struct ContentView: View {
@State var text = " "
var body: some View {
VStack {
MyTextField(label: "label", text: $text).textFieldStyle(RoundedBorderTextFieldStyle())
Text(text)
}.padding()
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
如果您需要不同的字体和.bold,请使用
MyTextField(label: "label", text: $text).textFieldStyle(RoundedBorderTextFieldStyle()).font(Font.title.bold())
或
MyTextField(label: "label", text: $text).font(Font.title.bold()).textFieldStyle(RoundedBorderTextFieldStyle())
View
修饰符只是 return 又 some View
的函数,因此您可以实现对任何修饰符的支持,符合您认为适合您的自定义类型的任何协议。您的控件在每个已实现的修改器上的行为取决于您。
下面是对 textFieldStyle
修饰符的简单演示支持,它使您的 ContentView
渲染 BetterTextField
取决于添加或删除的圆形矩形样式修饰符。
struct BetterTextField: UIViewRepresentable {
var title: String
@Binding var text: String
private let textField = UITextField()
init(_ title: String, text: Binding<String>) {
self.title = title
self._text = text
}
func makeUIView(context: Context) -> UITextField {
textField.placeholder = title
return textField
}
func updateUIView(_ view: UITextField, context: Context) {
view.text = text
}
}
extension BetterTextField {
func textFieldStyle<S>(_ style: S) -> some View where S : TextFieldStyle {
if style is RoundedBorderTextFieldStyle {
self.textField.borderStyle = .roundedRect
}
return self
}
}