调用 PopMenuButton
对于 PopMenuButton 的封装已经完毕,我都迫不及待的想要添加到服务器选择上面。
我们新增一个属性获取当前可以选择服务器的数据源。
swift
private var serverUrls:[String] = [
AppServer.debug.rawValue,
AppServer.release.rawValue
]
我们将 PopMenuButton 添加进 ServerSelectMenuView 里面。
swift
var body: some View {
LoginValueContentView(left: leftView,
center: centerView,
right: rightView)
.popMenuButton(items: serverUrls,
currentItem: $viewModel.currentAppServer)
}
服务器选择组件终于做完了,没想到费了我们这么多的时间,主要我觉得是从之前自动布局换成这种线性布局,包括一些数据交互的处理发生了变化,导致我们没有完全适应。
使用 TextField 输入内容
接下来就是登陆页面输入用户名的界面,我们新建在 LoginPage 的 View中新建一个 UserNameValueContentView.swift,用来封装用户输入用户名。
swift
struct UserNameValueContentView: View {
@EnvironmentObject private var viewModel:LoginPageViewModel
var body: some View {
LoginValueContentView(left: icon,
center: userNameField,
right: EmptyView())
}
private var icon: some View {
Image("user_icon_34_34")
}
private var userNameField:some View {
TextField("请输入用户名", text: $viewModel.userName)
}
}
界面十分的简单,我们用到了 TextField。 其实使用 TextField 非常的简单,我们传入一个 提示语和一个接受变更文本的 @Binding变量即可。
除了 TextField,我们还用到了一个 EmptyView 的空试图。这个 EmptyView 没有任何意思,只是作为语法里面的占位符而已,比如你不能传入空对象的时候。
为了可以接受到输入框输入的用户名,我们在 LoginPageViewModel 新增一个变量。
swift
@Published var userName:String = ""
@Published这个我们也接触到一个陌生的属性包装器,和@State有什么区别呢?@State只能在视图的内部使用,虽然可以通过 @Binging进行传递使用,但是需要一层层的传递。但是@Published就不一样,只要两个视图拿到同一个对象,那么就通过 @Published 监听和修改对应的值,这就是做 MVVM的核心所在。
使用 SecureField 作为密码输入框
我们在 LoginPage 的 View 文件夹创建一个 PasswordValueContentView.swift用户组件输入密码的组件。
swift
struct PasswordValueContentView: View {
@EnvironmentObject private var viewModel:LoginPageViewModel
var body: some View {
LoginValueContentView(left: icon,
center: passwordField,
right: EmptyView())
}
private var icon:some View {
Image("password_icon_34x34")
}
private var passwordField:some View {
SecureField("请输入密码", text: $viewModel.password)
}
}
我们在封装输入密码的组件里面用到了 SecureField 。SecureField 和 TextField 的区别在于,输入的密码是明文还是密文。
我们将 UserNameValueContentView 和 PasswordValueContentView.swift 添加到 LoginPage中。
swift
ServerSelectMenuView()
.padding(.leading,45)
.padding(.trailing,45)
Spacer()
.frame(height:30)
UserNameValueContentView()
.padding(.leading,45)
.padding(.trailing,45)
Spacer()
.frame(height:30)
PasswordValueContentView()
.padding(.leading,45)
.padding(.trailing,45)
虽然我们得到我们想要的界面效果,但是我们的代码感觉很重复,每个组件都需要设置 Padding进行对其,并且组件和组件之间还要设置间隙。这个不就是VStack该做的事情吗?我们修改代码如下。
swift
VStack(spacing:30) {
ServerSelectMenuView()
UserNameValueContentView()
PasswordValueContentView()
}
.padding(.leading,45)
.padding(.trailing,45)
我们将代码精简了需求,同时一样的效果,所以在后续代码的增多,合理的利用组件精简代码。