Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
675 views
in Technique[技术] by (71.8m points)

swift - Multiple Bottom sheets - the content doesn't load SwiftUI

I have made a view with two possible bottom sheets. The action works, and Bottom Sheets do open. Crazy thing is they open without the view inside. I have to close the one I opened and open the other one. When I do and than come back to the first one I will see the content. The code builds without warnings:

LogInView - where the logic is:

import SwiftUI

struct LogInView: View {
    @EnvironmentObject var userInfo: UserInfo
    enum Action{
        case resetPW, signUp
    }
    @State private var showSheet = false
    @State private var action:Action?
    
    var body: some View {
        LoginEmailView(showSheet: $showSheet, action: $action)
            .sheet(isPresented: $showSheet){
                if self.action == .resetPW{
                    ModalResetPWView()
                }else if self.action == .signUp{
                    ModalSignUpView()
                }
                
            }
    }
}

The view from which actions come:

import SwiftUI

struct LoginEmailView: View {
    
    @EnvironmentObject var userInfo: UserInfo
    @StateObject var user:LogInViewModel = LogInViewModel()
  
    
// ----- > THERE IS BINDING
    @Binding var showSheet: Bool
    @Binding var action:LogInView.Action?
// ----- >
    
    var body: some View {
        VStack{
            Spacer()
        Image("logo")
            HStack{
                Text("Adres email:")
                    .padding(.horizontal, 10)
                    .font(.title)
                    .foregroundColor(.black)
                Spacer()
            }
            
            TextField("Enter e-mail adress", text: self.$user.email)
            .textFieldStyle(RoundedBorderTextFieldStyle())
            .font(.title)
            .padding(.horizontal, 10)
                .keyboardType(.emailAddress)
            
            HStack{
               Text("Password:")
                    .padding(.horizontal, 10)
                    .font(.title)
                    .foregroundColor(.black)
                Spacer()
            }
            SecureField("Enter password", text: self.$user.password)
            .textFieldStyle(RoundedBorderTextFieldStyle())
            .font(.title)
            .padding(.horizontal,10)
            HStack{
                
                Spacer()
                
// ----- > First Bottom sheet
                Button(action: {
                    self.action = .resetPW
                    self.showSheet = true
                }) {
                    Text("Forgot Password")
                }
                .padding(.top, 5)
                .padding(.trailing, 10)
// ----- >                

            }
       
            
            
            Button(action: {
                self.userInfo.isAuthenticated = .signedIn
            }) {
                Text("Log in")
            }
            .font(.title)
            .padding(5)
            .background(Color.blue)
            .foregroundColor(.white)
            .cornerRadius(10)
            .padding(.top, 10)
            .opacity(user.isLogInComplete ? 1 : 0.7)
            .disabled(!user.isLogInComplete)
           
// ----- > Second bottom sheet
            Button(action: {
                self.action = .signUp
                self.showSheet = true
            }) {
                Text("Sign Up")
            }
// ----- >
          
            .padding(.top, 35)
            
            Spacer()
        }
        
    }
    
}
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

The .sheet modifier will create the sheet view as soon as LogInView() is initialized. In your 'if.. else if..' statement, there is no logic to catch 'else' situations (situations where action == nil). Therefore, since action == nil on init(), the first .sheet that will present will fail your 'if..else if' and an EmptyView will present.

But don't worry! This is a common issue and can be easily solved. Here are 2 easy ways to implement methods to fix this (I prefer the 2nd method bc it's cleaner):

METHOD 1: Present a single view & change that view's content instead of switching between which view to present.

Instead of doing the 'if.. else if..' statement within the .sheet modifier, present a static view (I've called it SecondaryView ) that has a @Binding variable connected to your action. This way, when LogInView() appears, we can ensure that it will definitely render this view and then we can simply modify this view's content by changing the @Binding action.

import SwiftUI

struct LogInView: View {

    enum Action{
        case resetPW, signUp
    }

    @State private var showSheet = false
    @State private var action: Action?

    var body: some View {
        LoginEmailView(showSheet: $showSheet, action: $action)
            .sheet(isPresented: $showSheet) {
                SecondaryView(action: $action)
            }
    }
}

struct LoginEmailView: View {

    @Binding var showSheet: Bool
    @Binding var action: LogInView.Action?

    var body: some View {
        VStack(spacing: 40 ){

            Text("Forgot Password")
                .onTapGesture {
                    action = .resetPW
                    showSheet.toggle()
                }

            Text("Sign Up")
                .onTapGesture {
                    action = .signUp
                    showSheet.toggle()
                }
        }
    }
}

struct SecondaryView: View {

    @Binding var action: LogInView.Action?

    var body: some View {
        if action == .signUp {
            Text("SIGN UP VIEW HERE")
        } else {
            Text("FORGOT PASSWORD VIEW HERE")
        }
    }
}

METHOD 2: Make each Button it's own View, so that it can have it's own .sheet modifier.

In SwiftUI, we are limited to 1 .sheet() modifier per View. However, we can always add Views within Views and each subview is then allowed it's own .sheet() modifier as well. So the easy solution is to make each of your buttons their own view. I prefer this method because we no longer need to pass around the @State/@Binding variables between views.

struct LogInView: View {

    var body: some View {
        LoginEmailView()
    }
}

struct LoginEmailView: View {

    var body: some View {
        VStack(spacing: 40 ){
            ForgotPasswordButton()

            SignUpButton()
        }
    }
}

struct ForgotPasswordButton: View {

    @State var showSheet: Bool = false

    var body: some View {
        Text("Forgot Password")
            .onTapGesture {
                showSheet.toggle()
            }
            .sheet(isPresented: $showSheet, content: {
                Text("FORGOT PASSWORD VIEW HERE")
            })
    }
}

struct SignUpButton: View {

    @State var showSheet: Bool = false

    var body: some View {
        Text("Sign Up")
            .onTapGesture {
                showSheet.toggle()
            }
            .sheet(isPresented: $showSheet, content: {
                Text("SIGN UP VIEW HERE")
            })
    }
}

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...