gpt4 book ai didi

ios - SwiftUI 登录页面布局

转载 作者:行者123 更新时间:2023-12-02 00:45:43 27 4
gpt4 key购买 nike

我正在探索 SwiftUI,因为我正在尝试构建登录 View ,现在我遇到了问题

这就是我想要实现的目标:

What I already achieved

如您所见,我已经达到了这一点,但我不喜欢我的实现

struct ContentView : View {
@State var username: String = ""
var body: some View {
VStack(alignment: .leading) {
Text("Login")
.font(.title)
.multilineTextAlignment(.center)
.lineLimit(nil)
Text("Please")
.font(.subheadline)

HStack {
VStack (alignment: .leading, spacing: 20) {
Text("Username: ")
Text("Password: ")

}
VStack {
TextField($username, placeholder: Text("type something here..."))
.textFieldStyle(.roundedBorder)
TextField($username, placeholder: Text("type something here..."))
.textFieldStyle(.roundedBorder)
}
}
}.padding()
}
}

因为为了使用户名和密码文本在文本字段的中间精确对齐,我必须将文字间距值 20 放入我的 VStack 中。不喜欢,因为很可能它无法在不同的设备尺寸上工作。

有人认为有更好的方法来达到相同的结果吗?

谢谢

最佳答案

我们将实现两个新的 View 修饰符方法,以便我们可以编写以下内容:

struct ContentView: View {
@State var labelWidth: CGFloat? = nil
@State var username = ""
@State var password = ""

var body: some View {
VStack {
HStack {
Text("User:")
.equalSizedLabel(width: labelWidth, alignment: .trailing)
TextField("User", text: $username)
}
HStack {
Text("Password:")
.equalSizedLabel(width: labelWidth, alignment: .trailing)
SecureField("Password", text: $password)
}
}
.padding()
.textFieldStyle(.roundedBorder)
.storeMaxLabelWidth(in: $labelWidth)
}
}

两个新修饰符是 equalSizedLabel(width:alignment:)storeMaxLabelWidth(in:)

equalSizedLabel(width:alignment) 修饰符有两个作用:

  1. 它将宽度对齐应用于其内容(Text(“User:”)Text(“Password :”) View )。
  2. 它测量其内容的宽度并将其传递给任何需要它的祖先 View 。

storeMaxLabelWidth(in:) 修饰符接收由 equalSizedLabel 测量的宽度,并将最大宽度存储在我们传递给的 $labelWidth 绑定(bind)中它。

那么,我们如何实现这些修饰符呢?我们如何将值从后代 View 传递到祖先 View ?在 SwiftUI 中,我们使用(当前未记录的)“首选项”系统来执行此操作。

为了定义新的首选项,我们定义一个符合 PreferenceKey 的类型。为了符合 PreferenceKey,我们必须定义偏好的默认值,并且必须定义如何组合多个 subview 的偏好。我们希望我们的偏好是所有标签的最大宽度,因此默认值为零,我们通过取最大值来组合偏好。这是我们将使用的 PreferenceKey:

struct MaxLabelWidth: PreferenceKey {
static var defaultValue: CGFloat { 0 }
static func reduce(value: inout Value, nextValue: () -> Value) {
value = max(value, nextValue())
}
}

preference 修饰符函数设置首选项,因此我们可以使用 .preference(key: MaxLabelWidth.self, value: width) 来设置我们的首选项,但是我们有了解要设置的宽度。我们需要使用 GeometryReader 来获取宽度,正确执行有点棘手,因此我们将其包装在 ViewModifier 中,如下所示:

extension MaxLabelWidth: ViewModifier {
func body(content: Content) -> some View {
return content
.background(GeometryReader { proxy in
Color.clear
.preference(key: Self.self, value: proxy.size.width)
})
}
}

上面发生的事情是我们将背景 View 附加到内容,因为背景始终与其附加的内容大小相同。背景View是一个GeometryReader,它(通过代理)提供对其自身大小的访问。我们必须为 GeometryReader 提供其自己的内容。由于我们实际上并不想在原始内容后面显示背景,因此我们使用 Color.clear 作为 GeometryReader 的内容。最后,我们使用 preference 修饰符将宽度存储为 MaxLabelWidth 首选项。

现在可以定义 equalSizedLabel(width:alignment:)storeMaxLabelWidth(in:) 修饰符方法:

extension View {
func equalSizedLabel(width: CGFloat?, alignment: Alignment) -> some View {
return self
.modifier(MaxLabelWidth())
.frame(width: width, alignment: alignment)
}
}

extension View {
func storeMaxLabelWidth(in binding: Binding<CGFloat?>) -> some View {
return self.onPreferenceChange(MaxLabelWidth.self) {
binding.value = $0
}
}
}

结果如下:

result screenshot

关于ios - SwiftUI 登录页面布局,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56623310/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com