Ios SwiftUI登录页面布局
我正在探索SwiftUI,因为我试图构建一个登录视图,现在我面临一个问题 这就是我努力实现的目标: 正如您所看到的,我已经达到了这一点,但我不喜欢我的实现Ios SwiftUI登录页面布局,ios,swift,swiftui,swift5.1,Ios,Swift,Swiftui,Swift5.1,我正在探索SwiftUI,因为我试图构建一个登录视图,现在我面临一个问题 这就是我努力实现的目标: 正如您所看到的,我已经达到了这一点,但我不喜欢我的实现 struct ContentView : View { @State var username: String = "" var body: some View { VStack(alignment: .leading) { Text("Login") .font(.title)
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()
}
}
因为为了使用户名和密码文本正好在文本字段的中间对齐,所以我不得不在< VStack > <代码> >代码> > <代码> >中,我不喜欢它的文字间距值,因为它很可能在不同的设备大小上不能工作。 有人认为实现同样结果的更好方法吗
谢谢您可以使用
垫片
s以及固定尺寸
高度修饰符。您应该设置任意行对象的高度,以实现精确的表格样式
视图:
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()
}
}
请注意在
文本字段上设置高度
不会直接影响其高度,但它只会设置其内容文本高度的高度。我们将实施两种新的视图
修改器方法,以便编写以下内容:
struct ContentView:View{
@状态变量labelWidth:CGFloat?=nil
@状态变量username=“”
@State var password=“”
var body:一些观点{
VStack{
HStack{
文本(“用户:”)
.equalSizedLabel(宽度:labelWidth,对齐:。尾随)
文本字段(“用户”,文本:$username)
}
HStack{
文本(“密码:”)
.equalSizedLabel(宽度:labelWidth,对齐:。尾随)
SecureField(“密码”,文本:$Password)
}
}
.padding()
.textFieldStyle(.roundedBorder)
.storeMaxLabelWidth(单位:$labelWidth)
}
}
这两个新的修改器是equalSizedLabel(宽度:对齐:)
和storeMaxLabelWidth(in:)
equalSizedLabel(宽度:对齐)
修改器做两件事:
宽度
和对齐
应用于其内容(文本(“用户:”)
和文本(“密码:”)
视图)storeMaxLabelWidth(in:)
修饰符接收由equalSizedLabel
测量的宽度,并将最大宽度存储在传递给它的$labelWidth
绑定中
那么,我们如何实现这些修饰符呢?我们如何将值从后代视图传递给祖先?在SwiftUI中,我们使用(当前未记录的)“首选项”系统来实现这一点
为了定义一个新的首选项,我们定义了一个符合PreferenceKey
的类型。为了符合PreferenceKey
,我们必须为首选项定义默认值,并且必须定义如何组合多个子视图的首选项。我们希望我们的首选项是所有标签的最大宽度,因此默认值为零,我们通过取最大值来组合首选项。以下是我们将使用的首选项键
:
struct MaxLabelWidth:PreferenceKey{
静态var defaultValue:CGFloat{0}
静态func REDUCT(值:inout值,nextValue:()->值){
value=max(value,nextValue())
}
}
preference
修饰符函数设置一个首选项,因此我们可以说.preference(key:MaxLabelWidth.self,value:width)
来设置我们的首选项,但我们必须知道要设置的宽度。我们需要使用GeometryReader
来获得宽度,这有点棘手,因此我们将在ViewModifier
中对其进行总结,如下所示:
扩展名MaxLabelWidth:ViewModifier{
func正文(内容:内容)->某些视图{
返回内容
.background(GeometryReader{proxy in
颜色。清晰
.首选项(键:Self.Self,值:proxy.size.width)
})
}
}
上面发生的事情是我们将背景视图
附加到内容上,因为背景总是与其附加到的内容大小相同。背景视图
是一个GeometryReader
,它(通过代理
)提供对自身大小的访问。我们必须给出GeometryReader
自己的内容。因为我们实际上不想在原始内容后面显示背景,所以我们使用Color.clear
作为GeometryReader
的内容。最后,我们使用首选项
修饰符将宽度存储为MaxLabelWidth
首选项
现在可以定义equalSizedLabel(宽度:对齐:)
和storeMaxLabelWidth(in:)
修改器方法:
扩展视图{
func equalSizedLabel(宽度:CGFloat?,对齐方式:对齐)->一些视图{
回归自我
.modifier(MaxLabelWidth())
.框架(宽度:宽度,对齐:对齐)
}
}
扩展视图{
func storeMaxLabelWidth(在绑定中:binding)->一些视图{
返回self.onPreferenceChange(MaxLabelWidth.self){
binding.value=$0
}
}
}
结果如下:
如果您希望在macOS上使用
按钮执行类似操作,请注意,从Xcode 11.3开始,您将在运行时完成以下操作:
而不是:
我已经写下了我的解决方案。它与@罗布·梅耶夫的答案非常相似,而且对于标签和按钮都是有效的。对不起,我没有正确地考虑这个问题。没问题:你应该考虑从“文本:文本字段”中查看。对于用户名|密码:输入并将其放在VStack中,而不是在HStack中有两个单独的VStack,即文本和文本字段。@MartinM是的,当我这样做时,文本字段未在前导位置对齐。每个文本字段的X位置取决于左侧文本的宽度。I