首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何将默认修饰符分配给SwiftUI视图?

如何将默认修饰符分配给SwiftUI视图?
EN

Stack Overflow用户
提问于 2022-01-16 17:09:49
回答 2查看 659关注 0票数 4

我正在尝试创建可重用的自定义视图,这些视图可以在以后进行自定义。理想情况下,我可以定义默认的修饰符,如Color、Font等,这些视图没有任何定制,但允许这些修饰符被后面使用的附加修饰符轻松覆盖。

例如,如果我有自定义视图:

代码语言:javascript
复制
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将具有标题字体。但是现在,如果我想重用这个视图并为我的特定用例定制它,我可能想重写以下这些修饰符:

代码语言:javascript
复制
struct ContentView: View {
  @State var text = "hello"

  var body: some View {
    MyCustomTextField(text: $text)
      .foregroundColor(.blue)
      .font(.system(.body))
  }
}

但这些外部修饰语并不有效,因为内部修饰语有先例。

创建自定义视图的最佳方法是什么,以便我可以为它们的内容定义默认修饰符,但以后仍然能够覆盖这些缺省值?

谢谢

EN

回答 2

Stack Overflow用户

发布于 2022-01-16 20:11:40

font修饰符将Font存储在环境中,以便通过修改的视图进行访问。您可以阅读它并提供如下默认设置:

代码语言:javascript
复制
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修饰符没有将其设置存储在(公共)环境属性中,因此您必须选择不同的路径。一种方法是直接在自定义视图上提供修饰符,如下所示:

代码语言:javascript
复制
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修饰符:

代码语言:javascript
复制
PlaygroundPage.current.setLiveView(
    MyCustomTextField(text: .constant("hello"))
        .myColor(.red)
        .padding()
        .font(.body)
)

但是您不能在任何封闭视图或任何非MyCustomTextField-specific修饰符之后使用它。例如,这将使而不是工作:

代码语言:javascript
复制
PlaygroundPage.current.setLiveView(
    MyCustomTextField(text: .constant("hello"))
        .padding()
        .myColor(.red) // Error: Value of type 'some View' has no member 'myColor'
        .font(.body)
)

如果希望这样做,则需要在环境中存储自定义颜色,如下所示:

代码语言:javascript
复制
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修饰符,它将应用于所有封闭的子视图:

代码语言:javascript
复制
PlaygroundPage.current.setLiveView(
    MyCustomTextField(text: .constant("hello"))
        .padding()
        .myColor(.red)
        .font(.body)
)
票数 4
EN

Stack Overflow用户

发布于 2022-01-16 18:01:23

我们应该始终记住,SwiftUI视图是值类型,因此您不能像在UIKit上那样在初始化之后更改它们。一种可能的方法是在初始化时注入它。但是,如果您希望在初始化后使其可调(例如,按下其他按钮时更改颜色),请使用绑定机制:

@Binding添加到MyCustomTextField中,并在foregroundColor修饰符处使用它的值。

代码语言:javascript
复制
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))
      }
    }
}

然后,在客户端视图中,您可以这样做:

代码语言:javascript
复制
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的值将相应更改。

票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/70732315

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档