我正在尝试创建可重用的自定义视图,这些视图可以在以后进行自定义。理想情况下,我可以定义默认的修饰符,如Color、Font等,这些视图没有任何定制,但允许这些修饰符被后面使用的附加修饰符轻松覆盖。
例如,如果我有自定义视图:
struct MyCustomTextField: View {
@Binding var text: String
var body: some View {
HStack {
Image(systemName: "envelope").foregroundColor(.gray)
TextField("", text: $text)
.foregroundColor(.gray)
.font(.system(.title))
}
}
}视图将具有默认的灰色前景色,而TextField将具有标题字体。但是现在,如果我想重用这个视图并为我的特定用例定制它,我可能想重写以下这些修饰符:
struct ContentView: View {
@State var text = "hello"
var body: some View {
MyCustomTextField(text: $text)
.foregroundColor(.blue)
.font(.system(.body))
}
}但这些外部修饰语并不有效,因为内部修饰语有先例。
创建自定义视图的最佳方法是什么,以便我可以为它们的内容定义默认修饰符,但以后仍然能够覆盖这些缺省值?
谢谢
发布于 2022-01-16 20:11:40
font修饰符将Font存储在环境中,以便通过修改的视图进行访问。您可以阅读它并提供如下默认设置:
struct MyCustomTextField: View {
@Binding var text: String
@Environment(\.font)
var envFont: Font?
var body: some View {
HStack {
Image(systemName: "envelope").foregroundColor(.gray)
TextField("", text: $text)
.foregroundColor(.gray)
.font(envFont ?? .system(.title))
}
}
}不幸的是,foregroundColor修饰符没有将其设置存储在(公共)环境属性中,因此您必须选择不同的路径。一种方法是直接在自定义视图上提供修饰符,如下所示:
struct MyCustomTextField: View {
@Binding var text: String
var _myColor: Color = .gray
@Environment(\.font)
var envFont: Font?
func myColor(_ color: Color) -> Self {
var copy = self
copy._myColor = color
return copy
}
var body: some View {
HStack {
Image(systemName: "envelope").foregroundColor(.gray)
TextField("", text: $text)
.foregroundColor(_myColor)
.font(envFont ?? .system(.title))
}
}
}然后,您可以在如下的myColor修饰符上直接使用MyCustomTextField修饰符:
PlaygroundPage.current.setLiveView(
MyCustomTextField(text: .constant("hello"))
.myColor(.red)
.padding()
.font(.body)
)但是您不能在任何封闭视图或任何非MyCustomTextField-specific修饰符之后使用它。例如,这将使而不是工作:
PlaygroundPage.current.setLiveView(
MyCustomTextField(text: .constant("hello"))
.padding()
.myColor(.red) // Error: Value of type 'some View' has no member 'myColor'
.font(.body)
)如果希望这样做,则需要在环境中存储自定义颜色,如下所示:
struct MyColor: EnvironmentKey {
static var defaultValue: Color? { nil }
}
extension EnvironmentValues {
var myColor: Color? {
get { self[MyColor.self] }
set { self[MyColor.self] = newValue }
}
}
extension View {
func myColor(_ color: Color?) -> some View {
return self.environment(\.myColor, color)
}
}
struct MyCustomTextField: View {
@Binding var text: String
@Environment(\.myColor)
var envColor: Color?
@Environment(\.font)
var envFont: Font?
var body: some View {
HStack {
Image(systemName: "envelope").foregroundColor(.gray)
TextField("", text: $text)
.foregroundColor(envColor ?? .gray)
.font(envFont ?? .system(.title))
}
}
}然后,您可以在任何视图上使用myColor修饰符,它将应用于所有封闭的子视图:
PlaygroundPage.current.setLiveView(
MyCustomTextField(text: .constant("hello"))
.padding()
.myColor(.red)
.font(.body)
)发布于 2022-01-16 18:01:23
我们应该始终记住,SwiftUI视图是值类型,因此您不能像在UIKit上那样在初始化之后更改它们。一种可能的方法是在初始化时注入它。但是,如果您希望在初始化后使其可调(例如,按下其他按钮时更改颜色),请使用绑定机制:
将@Binding添加到MyCustomTextField中,并在foregroundColor修饰符处使用它的值。
struct MyCustomTextField: View {
@Binding var text: String
@Binding var foregroundColor: Color
var body: some View {
HStack {
Image(systemName: "envelope").foregroundColor(.gray)
TextField("", text: $text)
.foregroundColor(foregroundColor)
.font(.system(.title))
}
}
}然后,在客户端视图中,您可以这样做:
struct MyView: View {
@State var myText: String = ""
@State var hasSpecialColor: Bool = false
var foregroundColor: Binding<Color> {
return Binding<Color>(get: {
return hasSpecialColor ? Color.red : Color.black
}, set: { newValue in
hasSpecialColor = newValue == Color.red
})
}
var body: some View {
VStack {
Text("Some Header Text")
MyCustomTextField(text: $myText, foregroundColor: foregroundColor)
Button("click to toggle color") {
hasSpecialColor.toggle()
}
}
}
}现在,每次单击该按钮时,它都会切换hasSpecialColor (即@State),因此视图将呈现,@Binding foregroundColor的值将相应更改。
https://stackoverflow.com/questions/70732315
复制相似问题