// // SignInPhoneView.swift // IOS_study // // Created by CC-star on 2025/7/10. // import SwiftUI import Combine private let totalTime = 60 struct SignInPhoneView: View { enum FocusedField { case phoneNum, verCode }//聚焦在那个case @Environment(SignInPhoneViewModel.self) var vm @Environment(HUD.self) var hud @State var showVerBtn = true @State var timer: Publishers.Autoconnect?//后面的 ? 表示可选类型 @State var timeRemain = totalTime//倒计时 @State var isAgree = false //是否同意协议,默认未勾选 @State var showAgreeSheet = false//确认协议弹窗 @State var showAgreeView = false//展示协议 @State var agreeType = 0//0表示用户协议,1表示隐私政策 @FocusState var focusedField: FocusedField?//光标聚焦用,设置为可选性,方便赋予nil var enableLoginBtn: Bool { vm.phoneNum.isPhoneNum && vm.verCode.isVerCode } var totalNumCount: Int { vm.phoneNum.count + vm.verCode.count } var body: some View { @Bindable var vm = vm NavigationStack { ScrollView { VStack(spacing: 33) { HStack { NavigationLink { SettginsView(naviPath: .constant(NavigationPath()), isLogin: false) } label: { Image(systemName: "gearshape") } Button { focusedField = nil//不聚焦光标 -> 收起键盘 } label: { Image(systemName: "xmark") } }.push(to: .trailing).font(.title3).secondary() Text("手机号登陆").tc().font(.title).bold() .kerning(1.2)//字间距,默认1 VStack(spacing: 14) { HStack { TextField("请输入手机号", text: $vm.phoneNum).tc().kerning(1.2) .keyboardType(.numberPad)//数字键盘 .focused($focusedField, equals: .phoneNum) if !vm.phoneNum.isEmpty { Image(systemName: "xmark.circle.fill").font(.title2).secondary().onTapGesture { vm.phoneNum = "" } } } exDivider() HStack { //797687 TextField("请输入验证码", text: $vm.verCode).tc().tc().kerning(1.2).keyboardType(.numberPad) .focused($focusedField, equals: .verCode) if vm.phoneNum.isPhoneNum || !showVerBtn {//是否为中国的手机号 if showVerBtn { Button("获取验证码") { timeRemain = totalTime focusedField = .verCode//光标聚焦在case为verCode的区域 timer = Timer.publish(every: 1, on: .main, in: .common).autoconnect()//每隔一秒,在主循环,自动执行 showVerBtn = false Task { await getVerCode() } }.headline() } else { if let timer { Text("重新发送(\(timeRemain)s)").headline().secondary() //监听异步任务 .onReceive(timer) { _ in timeRemain -= 1 if timeRemain <= 0 { stopTimer() showVerBtn = true } } } } } } exDivider() Button { preLogin() } label: { if vm.isLogining { ProgressView().appleStyleP() } else { Text("登录").appleStyle(disabled: !enableLoginBtn) } }.disabled(!enableLoginBtn || vm.isLogining) HStack(spacing: 3) { Button { isAgree.toggle()} label: { Image(systemName: isAgree ? "checkmark.circle.fill" : "circle") }.tint(isAgree ? .accent : .secondary).size16() agreeTextView.size14() } }.font(.title3) }.padding(33) } .scrollDismissesKeyboard(.immediately).bg()//滚动时立刻收起键盘 .fullScreenCover(isPresented: $showAgreeView, content: { AgreeView(agreeType: $agreeType) }) .sheet(isPresented: $showAgreeSheet) {//弹窗 VStack(spacing: 18) { agreeTextView.size15() Button { isAgree = true//同意全部协议 showAgreeSheet = false//关闭弹窗 Task { await login() }//登录 } label: { Text("同意并登录").font(.body).push(to: .center).padding(.vertical, 6) }.buttonStyle(.borderedProminent) }.padding(33) .presentationDetents([.height(200)])//指定弹窗展示多少 // .presentationDetents([.medium, .large])//可以拖动弹窗展示多少 } } .onChange(of: totalNumCount) { if totalNumCount == 17 { preLogin() } } } var agreeTextView: some View { HStack(spacing: 3) { Text("我已阅读并同意").secondary() Button("用户协议") { agreeType = 0 showAgreeView = true } Text("和").secondary() Button("隐私政策") { agreeType = 1 showAgreeView = true } } } }