在本章中,你将学会如何使用Combine
异步编程框架和MVVM
开发模式完成一个登录注册
页面及其逻辑交互。
在几乎所有App
中,都需要一个用户注册登录页面,用来获取和绑定用户信息。用户量,特别是付费用户量,决定这一款产品能否成为爆款商品。
因此,做好注册登录页面是非要有必要的。
那么,我们开始吧。
项目创建
首先,创建一个新项目,命名为SwiftUIRegistration
。
主要页面绘制
我们简单分析下页面组成,它由一个用户名称输入框、一个密码输入框、一个密码二次确认输入框组成。
首先是用户名称输入框,它是一个简答的TextField
输入框,我们只需要使用@State
初始化存储一个String
类型的参数,绑定就可以了。
//用户名 TextField("用户名", text: $username) .font(.system(size: 20, weight: .semibold)) .padding(.horizontal)
然后是密码输入框,它有点不同,在SwiftUI
中,除了TextField
输入框外,还有一种密码类型的输入框SecureField
,使用方法和TextField
输入框类似。
//密码 SecureField("密码", text: $password) .font(.system(size: 20, weight: .semibold)) .padding(.horizontal)
密码二次确认输入框也是同样是SecureField
密码输入框,为了隔开内容,输入框都可以使用Divider
分割线分开,这样美观一些。
//分割线 Divider() .frame(height: 1) .background(Color(red: 240/255, green: 240/255, blue: 240/255)) .padding(.horizontal)
最后是注册按钮,按照之前的章节,我们放一个简单的按钮。
//注册按钮 Button(action: { }) { Text("注册") .font(.system(.body, design: .rounded)) .foregroundColor(.white) .bold() .padding() .frame(minWidth: 0, maxWidth: .infinity) .background(Color(red: 51 / 255, green: 51 / 255, blue: 51 / 255)) .cornerRadius(10) .padding(.horizontal) }
我们准备好这些元素后,将他们用VStack
纵向排布在一起。
代码优化
我们发现,相同的代码太多了,这不够优雅。
哪怕是做任何项目,我们编程都力求优雅。我们可以把相同的内容抽离出来,除了样式外,我们发现TextField
输入框和SecureField
密码输入框除了类型不一样,其他都是一样的。
那么我们可以通过一个判断
来抽离主体内容。
//注册视图 struct RegistrationView:View { var isTextField = false var fieldName = "" @Binding var fieldValue: String var body: some View { VStack { //判断是不是输入框 if isTextField { //输入框 TextField(fieldName, text: $fieldValue) .font(.system(size: 20, weight: .semibold)) .padding(.horizontal) } else { //密码输入框 SecureField(fieldName, text: $fieldValue) .font(.system(size: 20, weight: .semibold)) .padding(.horizontal) } //分割线 Divider() .frame(height: 1) .background(Color(red: 240/255, green: 240/255, blue: 240/255)) .padding(.horizontal) } } }
然后,我们只需要在ContentView
主视图中引用并绑定
就完成了页面设计。
RegistrationView(isTextField: true, fieldName: "用户名", fieldValue: $username) RegistrationView(isTextField: false, fieldName: "密码", fieldValue: $password) RegistrationView(isTextField: false, fieldName: "再次输入密码", fieldValue: $passwordCon
报错信息绘制
然后,我们来实现了报错信息提醒。
同理,我们也把相同元素抽离出来,我们可以看到如果校验不通过,它会展示一个Image
图标和错误信息Text
文字,逻辑我们先放一边,完成页面。
//错误信息 struct InputErrorView:View { var iconName = "" var text = "" var body: some View { HStack { Image(systemName: iconName) .foregroundColor(Color(red: 251/255, green: 128/255, blue: 128/255)) Text(text) .font(.system(.body, design: .rounded)) .foregroundColor(Color(red: 251/255, green: 128/255, blue: 128/255)) Spacer() }.padding(.leading,10) } }
然后在ContentView
主视图中引用并绑定,效果如下:
恭喜你,完成了本章页面的设计编程。
由于这一章的内容有点多,那么我们也将分章节写完,不要着急。
下一章,我们将基于这个注册登录页面学习Combine
异步编程框架的使用。
完整代码
import SwiftUI struct ContentView: View { @State private var username = "" @State private var password = "" @State private var passwordConfirm = "" var body: some View { VStack (alignment: .leading, spacing: 40) { //用户名 VStack { RegistrationView(isTextField: true, fieldName: "用户名", fieldValue: $username) InputErrorView(iconName: "exclamationmark.circle.fill", text: "用户不存在") } //密码 VStack{ RegistrationView(isTextField: false, fieldName: "密码", fieldValue: $password) InputErrorView(iconName: "exclamationmark.circle.fill", text: "密码不正确") } //再次输入密码 VStack { RegistrationView(isTextField: false, fieldName: "再次输入密码", fieldValue: $passwordConfirm) InputErrorView(iconName: "exclamationmark.circle.fill", text: "两次密码需要相同") } //注册按钮 Button(action: { }) { Text("注册") .font(.system(.body, design: .rounded)) .foregroundColor(.white) .bold() .padding() .frame(minWidth: 0, maxWidth: .infinity) .background(Color(red: 51 / 255, green: 51 / 255, blue: 51 / 255)) .cornerRadius(10) .padding(.horizontal) } }.padding() } } struct ContentView_Previews: PreviewProvider { static var previews: some View { ContentView() } } //注册视图 struct RegistrationView:View { var isTextField = false var fieldName = "" @Binding var fieldValue: String var body: some View { VStack { //判断是不是输入框 if isTextField { //输入框 TextField(fieldName, text: $fieldValue) .font(.system(size: 20, weight: .semibold)) .padding(.horizontal) } else { //密码输入框 SecureField(fieldName, text: $fieldValue) .font(.system(size: 20, weight: .semibold)) .padding(.horizontal) } //分割线 Divider() .frame(height: 1) .background(Color(red: 240/255, green: 240/255, blue: 240/255)) .padding(.horizontal) } } } //错误判断 struct InputErrorView:View { var iconName = "" var text = "" var body: some View { HStack { Image(systemName: iconName) .foregroundColor(Color(red: 251/255, green: 128/255, blue: 128/255)) Text(text) .font(.system(.body, design: .rounded)) .foregroundColor(Color(red: 251/255, green: 128/255, blue: 128/255)) Spacer() }.padding(.leading,10) } }
快来动手试试吧!