首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >SwiftUI登录页面布局

SwiftUI登录页面布局
EN

Stack Overflow用户
提问于 2019-06-17 07:24:42
回答 3查看 3.2K关注 0票数 4

在尝试构建登录视图时,我正在探索SwiftUI,但现在我面临着一个问题

这就是我想要实现的:

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

代码语言:javascript
复制
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中放入20的文字间隔值,这是我不喜欢的,因为它很可能在不同大小的设备上不起作用。

有没有人看到更好的方法来达到同样的结果?

谢谢

EN

回答 3

Stack Overflow用户

发布于 2019-06-20 02:05:34

我们将实现两个新的View修饰符方法,这样我们就可以编写以下代码:

代码语言:javascript
复制
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. 它将widthalignment应用于其内容( Text(“User:”)Text(“Password:”)视图)。
  2. 它测量其内容的宽度,并将宽度向上传递给需要它的任何祖先视图。

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

那么,我们如何实现这些修饰符呢?我们如何将一个值从后代视图传递到祖先视图?在SwiftUI中,我们使用(目前没有文档记录的)“首选项”系统来实现这一点。

为了定义一个新的首选项,我们定义了一个符合PreferenceKey的类型。为了符合PreferenceKey,我们必须为我们的首选项定义默认值,并且我们必须定义如何组合多个子视图的首选项。我们希望我们的首选项是所有标签的最大宽度,所以默认值是零,我们通过取最大值来组合首选项。下面是我们将使用的PreferenceKey

代码语言:javascript
复制
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)来设置我们的首选项,但是我们必须知道要设置什么width。我们需要使用GeometryReader来获取宽度,正确地完成它有点麻烦,所以我们将它包装在一个ViewModifier中,如下所示:

代码语言:javascript
复制
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,它(通过proxy)提供对其自身大小的访问。我们必须给GeometryReader提供自己的内容。因为我们实际上不想在原始内容后面显示背景,所以我们使用Color.clear作为GeometryReader的内容。最后,我们使用preference修饰符将宽度存储为MaxLabelWidth首选项。

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

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

结果如下:

票数 10
EN

Stack Overflow用户

发布于 2019-06-17 19:10:49

您可以将Spacers与高度的fixedSize修饰符一起使用。您应该设置任何行的对象的高度,以实现精确的table style视图:

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

请注意,TextField上设置高度不会直接影响它的高度,但它只会设置其内容文本的高度。

票数 4
EN

Stack Overflow用户

发布于 2020-01-21 22:54:53

如果您希望在macOS上使用Buttons执行类似的操作,请注意,从Xcode11.3开始,您将在运行时得到以下代码:

而不是:

我已经写好了我的解决方案in this blog post。它与@rob-mayoff的答案非常相似,对标签和按钮都有效。

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

https://stackoverflow.com/questions/56623310

复制
相关文章

相似问题

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