AppStorage/应用数据存储, OnboardingView/引导页 的示例

该文展示了如何在SwiftUI应用中利用AppStorage进行简单数据存储,包括用户名、年龄和性别的保存。同时,文中详细介绍了创建OnboardingView引导页的步骤,包括不同阶段的内容切换、用户输入验证以及过渡动画。此外,还提到了ProfileView信息页的实现,用于显示用户已存储的信息,并提供签出功能。
摘要由CSDN通过智能技术生成

1. AppStorage 数据简单存储的实现

/// 应用程序数据简单存储
struct AppStorageBootcamp: View {
    
    //@State var currentUserName: String?
    @AppStorage("name") var currentUserName: String?
    
    var body: some View {
        VStack(spacing: 20) {
            Text(currentUserName ?? "Add Name Here")
            
            if let name = currentUserName {
                Text(name)
            }
            
            Button("Save".uppercased()) {
                let name:String  = "Emily"
                currentUserName = name
                // 用户默认值
                //UserDefaults.standard.set(name, forKey: "name")
            }
        }
//        .onAppear {
//            currentUserName = UserDefaults.standard.string(forKey: "name")
//        }
    }
}

2. OnboardingView 引导页的示例

  2.1 OnboardingView 引导页的实现

// 引导页 View
struct OnboardingView: View {
    
    // 引导页状态:
    /*
     0  — 欢迎进入
     1  — 添加名字
     2  — 添加年龄
     3  — 添加性别
     */
    @State var onboardingState: Int = 0
    
    let transition: AnyTransition = .asymmetric(
        insertion: .move(edge: .trailing),
        removal: .move(edge: .leading))
    
    /// 输入名字文本框
    @State var name: String = ""
    /// 年龄值
    @State var age: Double = 50
    /// 性别
    @State var gender: String = "Male"
    
    // 警告框
    @State var alertTitle: String = ""
    @State var showAlert: Bool = false
    
    // app 存储
    @AppStorage("name") var currentUserName: String?
    @AppStorage("age") var currentUserAge: Int?
    @AppStorage("gender") var currentUserGender: String?
    @AppStorage("signed_in") var currentUserSignedIn: Bool = false
    
    var body: some View {
        
        ZStack{
            // content
            ZStack{
                switch onboardingState{
                case 0:
                    welcomeSection
                        .transition(transition)
                case 1:
                    addNameSection
                        .transition(transition)
                case 2:
                    addAgeSection
                        .transition(transition)
                case 3:
                    addGenderSection
                        .transition(transition)
                default:
                    RoundedRectangle(cornerRadius: 25)
                        .foregroundColor(.green)
                }
            }
            
            // buttons
            VStack{
                Spacer()
                bottomButton
            }
            .padding(30)
        }
        .alert(isPresented: $showAlert) {
            return Alert(title: Text(alertTitle))
        }
    }
}

struct OnboardingView_Previews: PreviewProvider {
    static var previews: some View {
        OnboardingView()
            .background(Color.purple)
    }
}

// MARK: COMPONENTS 扩展类

extension OnboardingView{
    
    /// 登录按钮
    private var bottomButton: some View{
        Text(onboardingState == 0 ? "SIGN UP" :
                onboardingState == 3 ? "FINISH" :
                "NEXT")
        .font(.headline)
        .foregroundColor(.purple)
        .frame(height: 55)
        .frame(maxWidth: .infinity)
        .background(Color.white)
        .cornerRadius(10)
        .animation(nil) //去掉按钮动画
        .onTapGesture {
            handleNextButtonPressed()
        }
    }
    
    /// 欢迎页
    private var welcomeSection: some View{
        VStack(spacing: 40) {
            Spacer()
            Image(systemName: "heart.text.square.fill")
                .resizable()
                .scaledToFit()
                .frame(width: 200, height: 200)
                .foregroundColor(.white)
            
            Text("Find your match.")
                .font(.largeTitle)
                .fontWeight(.semibold)
                .foregroundColor(.white)
                .overlay(
                    Capsule(style: .continuous)
                        .frame(height: 3)
                        .offset(y: 5)
                        .foregroundColor(.white)
                    , alignment: .bottom
                )
            Text("This is the #1 app for finding your match online! In this tutotial, we are practicing using AppStorage and Swift UI techniques.")
                .foregroundColor(.white)
                .fontWeight(.medium)
            Spacer()
            Spacer()
        }
        .multilineTextAlignment(.center)
        .padding(30)
    }
    
    /// 添加名字
    private var addNameSection: some View{
        VStack(spacing: 20) {
            Spacer()
            Text("What's your name?")
                .font(.largeTitle)
                .fontWeight(.semibold)
                .foregroundColor(.white)
            TextField("Your name here...", text: $name)
                .font(.headline)
                .frame(height: 55)
                .padding(.horizontal)
                .background(Color.white)
                .cornerRadius(10)
            
            Spacer()
            Spacer()
        }
        .padding(30)
    }
    
    /// 添加年龄
    private var addAgeSection: some View{
        VStack(spacing: 20) {
            Spacer()
            Text("What's your age?")
                .font(.largeTitle)
                .fontWeight(.semibold)
                .foregroundColor(.white)
            
            Text("\(String(format: "%.0f", age))")
                .font(.largeTitle)
                .fontWeight(.semibold)
                .foregroundColor(.white)
            Slider(value: $age, in: 18...100, step: 1)
                .accentColor(.white)
            Spacer()
            Spacer()
        }
        .padding(30)
    }
    
    /// 添加性别
    private var addGenderSection: some View{
        VStack(spacing: 20) {
            Spacer()
            Text("What's your gender?")
                .font(.largeTitle)
                .fontWeight(.semibold)
                .foregroundColor(.white)
            
            Picker(selection: $gender) {
                Text("Male").tag("Male")
                Text("Female").tag("Female")
                Text("Non-Binary").tag("Non-Binary")
            } label: {
                Text(gender.count > 1 ? gender : "Select a gender")
                    .font(.headline)
                    .foregroundColor(.purple)
                    .frame(height: 55)
                    .frame(maxWidth: .infinity)
                    .cornerRadius(10)
            }
            .pickerStyle(.menu)
            .accentColor(.white)
            Spacer()
            Spacer()
        }
        .padding(30)
    }
}

// MARK: FUNCTIONS

extension OnboardingView{
    func handleNextButtonPressed(){
        // CHECK INPUTS
        switch onboardingState{
        case 1:
            guard name.count >= 3 else{
                showAlert(title: "你的名字必须要有3个字符的长度!😩")
                return
            }
        case 3:
            guard gender.count > 1 else{
                showAlert(title: "请在下一步之前选择一个性别!😳")
                return
            }
        default:
            break
        }
        // GO TO NEXT SECTION
        if onboardingState == 3{
            // sign in
            signIn()
        }else{
            // spring 弹簧
            withAnimation(.spring()) {
                onboardingState += 1
            }
        }
    }
    
    // 登录
    func signIn(){
        currentUserName = name
        currentUserAge = Int(age)
        currentUserGender = gender
        
        // 添加动画
        withAnimation(.spring()) {
            currentUserSignedIn = true
        }
    }
    
    /// 输出警告框
    func showAlert(title: String){
        alertTitle = title
        showAlert.toggle()
    }
}

  2.2 ProfileView 信息页的实现

/// 信息页 View
struct ProfileView: View {
    // app 存储
    @AppStorage("name") var currentUserName: String?
    @AppStorage("age") var currentUserAge: Int?
    @AppStorage("gender") var currentUserGender: String?
    @AppStorage("signed_in") var currentUserSignedIn: Bool = false
    
    var body: some View {
        VStack(spacing: 10){
            Image(systemName: "person.circle.fill")
                .resizable()
                .scaledToFit()
                .frame(width: 150, height: 150)
            Text(currentUserName ?? "Your name here")
            Text("This user is \(currentUserAge ?? 0) years old")
            Text("Their gender is \(currentUserGender ?? "unknown")")
            
            Text("SIGN OUT")
                .foregroundColor(.white)
                .font(.headline)
                .frame(height: 55)
                .frame(maxWidth: .infinity)
                .background(Color.black)
                .cornerRadius(10)
                .onTapGesture {
                    signOut()
                }
        }
        .font(.title)
        .foregroundColor(.purple)
        .padding()
        .padding(.vertical)
        .background(Color.white)
        .cornerRadius(10)
        .shadow(radius: 10)
    }
    
    /// 退出
    func signOut(){
        currentUserName = nil
        currentUserAge = nil
        currentUserGender = nil
        withAnimation(.spring()){
            currentUserSignedIn = false
        }
    }
}

  2.3 IntroView 简介视图

/// 简介视图
struct IntroView: View {
    @AppStorage("signed_in") var currentUserSignedIn: Bool = false
    
    var body: some View {
        ZStack{
            // background
            RadialGradient(
                gradient: Gradient(colors: [Color(#colorLiteral(red: 0.8446564078, green: 0.5145705342, blue: 1, alpha: 1)),Color(#colorLiteral(red: 0.3647058904, green: 0.06666667014, blue: 0.9686274529, alpha: 1))]),
                center: .topLeading,
                startRadius: 5,  // 开始半径
                endRadius: UIScreen.main.bounds.height) // 结束半径
            .ignoresSafeArea()
            
            if currentUserSignedIn {
                // 显示详情页
                ProfileView()
                // 添加动画
                    .transition(.asymmetric(insertion: .move(edge: .bottom), removal: .move(edge: .top)))
            }else{
                // 登记页
                OnboardingView()
                // 添加动画
                    .transition(.asymmetric(insertion: .move(edge: .top), removal: .move(edge: .bottom)))
            }
            // 用户是否已经登录
            // 显示个人资料视图
            // 否则
            // 填写引导视图
        }
    }
}

  2.4 效果图:

SwiftUI中,为了在启动屏幕显示加载状态,并在配置完成后再切换到引导面,你可以按照以下步骤操作: 1. **启动屏设置**: - 创建一个`LoadingView`,它将展示加载指示器。你可以使用`ProgressView`或者自定义视图来实现。 ```swift struct LoadingView: View { var body: some View { ProgressView(activityIndeterminate: true) // 或者使用CircularProgressIndicator等自定义进度视图 .padding() .background(Color.gray.opacity(0.7)) .foregroundColor(.white) .animation(.default) .onAppear(perform: loadConfig) } func loadConfig() { DispatchQueue.main.asyncAfter(deadline: .now() + 1.5) { self.preloadPreferences() self.isPreloaded = true // 设置一个布尔值标记加载完成 } } // 假设有个变量isPreloaded用于跟踪是否配置已完成 @State private var isPreloaded: Bool = false } ``` 2. **偏好配置**: - 通过UserDefaults或其他持久化存储方案,预先加载用户的偏好设置。 ```swift func preloadPreferences() { if let config = UserDefaults.standard.string(forKey: "preferenceKey") { handleConfiguration(config) } else { print("No preference data found, loading default settings.") } } // handleConfiguration方法负责解析配置并做相应调整 func handleConfiguration(config: String) { // 解析config字符串并应用应用程序的初始状态或导航栈 } ``` 3. **引导切换**: - 利用`isPreloaded`的状态,在配置完成后跳转到引导或主界面。 ```swift struct ContentView: View { @StateObject private var viewModel = ViewModel(isPreloaded: $isPreloaded) var body: some View { if !viewModel.isPreloaded { LoadingView() } else { // 引导或者其他主界面视图 NavigationView { // ... } } } } // ViewModel类 class ViewModel: ObservableObject { @Published var isPreloaded: Bool init(isPreloaded: Binding<Bool>) { _isPreloaded = isPreloaded } // 当isPreloaded变为true时,触发导航或界面变更 func onConfigLoaded() { withAnimation { isPreloaded = true navigateToMainPage() // 调用导航函数 } } func navigateToMainPage() { // 替换为实际的引导或者主界面的导航逻辑 } } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Hanyang Li

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值