Mos*_*fat 2 ios swift swiftui swift5.1
我正在尝试建立登录视图时正在探索SwiftUI,现在我遇到了问题
这是我要实现的目标:
如您所见,我已经达到了这一点,但是我不喜欢我的实现
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()
}
}
Run Code Online (Sandbox Code Playgroud)
因为为了使在文本框的中间正好对准用户名和密码的文本,我不得不把字面间距值20的VStack,我不喜欢,因为最有可能它不会在不同的设备尺寸的工作。
有人看到一种更好的方法来达到相同的结果吗?
谢谢
您可以将Spacers 与fixedSize高度修饰符一起使用。您应该设置任何行对象的设定高度以获得精确的table style视图:
struct ContentView : View {
private let height: Length = 32
@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) {
Text("Username: ") .frame(height: height)
Spacer()
Text("Password: ") .frame(height: height)
}
VStack {
TextField($username, placeholder: Text("type something here..."))
.textFieldStyle(.roundedBorder)
.frame(height: height)
Spacer()
TextField($username, placeholder: Text("type something here..."))
.textFieldStyle(.roundedBorder)
.frame(height: height)
}
}
.fixedSize(horizontal: false, vertical: true)
}
.padding()
}
}
Run Code Online (Sandbox Code Playgroud)
请注意,设置高度TextField不会直接影响其高度,但它只会设置其内容文本的高度。
我们将实现两个新的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)
}
}
Run Code Online (Sandbox Code Playgroud)
这两个新的修饰符是equalSizedLabel(width:alignment:)和storeMaxLabelWidth(in:)。
该equalSizedLabel(width:alignment)修改做了两两件事:
width和alignment应用于其内容(Text(“User:”)和Text(“Password:”)视图)。所述storeMaxLabelWidth(in:)改性剂接收由测量那些宽度equalSizedLabel并存储的最大宽度在$labelWidth我们传递给它的结合。
那么,我们如何实现这些修饰符?我们如何将一个值从后代视图传递到祖先?在SwiftUI中,我们使用(当前未记录的)“首选项”系统执行此操作。
要定义新的首选项,我们定义一个符合的类型PreferenceKey。为了符合PreferenceKey,我们必须为我们的首选项定义默认值,并且我们必须定义如何组合多个子视图的首选项。我们希望我们的首选项是所有标签的最大宽度,因此默认值是零,我们通过采用最大值来组合首选项。这是PreferenceKey我们将使用的:
struct MaxLabelWidth: PreferenceKey {
static var defaultValue: CGFloat { 0 }
static func reduce(value: inout Value, nextValue: () -> Value) {
value = max(value, nextValue())
}
}
Run Code Online (Sandbox Code Playgroud)
该preference修正功能设置首选项,所以我们可以说.preference(key: MaxLabelWidth.self, value: width),以确定我们的喜好,但我们要知道什么width来定。我们需要使用a 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)
})
}
}
Run Code Online (Sandbox Code Playgroud)
上面发生的事情是我们View为内容附加了背景,因为背景的大小总是与其所附加内容的大小相同。背景View为GeometryReader,通过(可通过proxy)访问其自身的大小。我们必须给出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
}
}
}
Run Code Online (Sandbox Code Playgroud)
结果如下:
| 归档时间: |
|
| 查看次数: |
1277 次 |
| 最近记录: |