Present screen based on 2 values - swift

I have 2 Integers: Xcode and Ycode. These are bindings from previous screens.
Now what I want is to present a new view based on these integers.
The app is a small quiz. So the Xcode and Ycode are the score.
But I want to present a new view when you click on the button "Click me" based on the Xcode and Ycode.
For example:
Value X = between 8-15 and value Y = between 8-23 -> present screen1
Value X = between 8-15 and value Y = between 24-40 -> present screen2
Value X = between 16-23 and value Y = between 8-17 -> present screen3
And so on......
This is my Code:
#Binding var Xcode: Int
#Binding var Ycode: Int
#State var ShowButton: Bool = false
#State var ButtonYes: Bool = false
#State var ButtonNo: Bool = false
#State var ButtonSometimes: Bool = false
var body: some View {
ZStack{
Image("Zebras")
.resizable()
.ignoresSafeArea()
.navigationBarHidden(true)
VStack{
Text("Wat ben ik?")
.font(.largeTitle)
.fontWeight(.heavy)
.padding()
.foregroundColor(.white)
.background(Color(red: 0.493, green: 0.184, blue: 0.487))
.cornerRadius(20)
Spacer()
Text("Je heb alle vragen beantwoord. Nu is de vraag: Welk dier ben ik?")
.foregroundColor(Color.white)
.font(.headline)
.padding()
.background(Color(red: 0.493, green: 0.184, blue: 0.487))
.cornerRadius(20)
Spacer()
Text("Your score:")
.foregroundColor(Color.white)
.font(.headline)
.padding()
.background(Color(red: 0.493, green: 0.184, blue: 0.487))
.cornerRadius(20)
HStack (spacing:0){
Text("X = ")
.foregroundColor(.white)
.font(.largeTitle)
.padding()
.background(Color(red: 0.493, green: 0.184, blue: 0.487))
.cornerRadius(20)
Text(String(Xcode))
.foregroundColor(.white)
.font(.largeTitle)
.padding()
.background(Color(red: 0.493, green: 0.184, blue: 0.487))
.cornerRadius(20)
}
HStack (spacing:0){
Text("Y = ")
.foregroundColor(.white)
.font(.largeTitle)
.padding()
.background(Color(red: 0.493, green: 0.184, blue: 0.487))
.cornerRadius(20)
Text(String(Ycode))
.foregroundColor(.white)
.font(.largeTitle)
.padding()
.background(Color(red: 0.493, green: 0.184, blue: 0.487))
.cornerRadius(20)
}
Spacer()
Button("Click here!") {
}
.frame(width: 100, height: 50, alignment: .center)
.font(.headline)
.foregroundColor(.white)
.padding()
.background(ButtonYes ? Color(red: 0.272, green: 0.471, blue: 0.262) : Color(red: 0.493, green: 0.184, blue: 0.487))
.cornerRadius(20)
.shadow(color: .black, radius: 10, x: 10, y: 10)
Spacer()
}
}
}
}
How could I create that?

you could use this approach, using a tuple, a switch and some NavigationLinks:
struct ContentView: View {
#State var xy = (5.0,7.0) // <-- use a tuple
var body: some View {
NavigationView {
QuizView(xy: $xy)
}.navigationViewStyle(.stack)
}
}
struct QuizView: View {
#Binding var xy: (Double, Double)
// set of ranges of your scores for each screen
let screen1X = 1.0..<4.0
let screen1Y = 2.0..<4.0
let screen2X = 3.0..<4.0
let screen2Y = 4.0..<8.0
let screen3X = 5.0..<9.0
let screen3Y = 6.0..<8.0
#State private var action: Int? = 0
var body: some View {
VStack {
Button(action: {
switch xy {
case (screen1X,screen1Y): action = 1
case (screen2X,screen2Y): action = 2
case (screen3X,screen3Y): action = 3
default:
print("---> default")
}
}) {
Text("Click me")
}
NavigationLink(destination: Text("screen1X"), tag: 1, selection: $action) {EmptyView()}
NavigationLink(destination: Text("screen2X"), tag: 2, selection: $action) {EmptyView()}
NavigationLink(destination: Text("screen3X"), tag: 3, selection: $action) {EmptyView()}
}
}
}

ContentView with 2 values: valueX and valueY. Then the ranges you need. Then a computed property to decide to which screen should navigate. The button is only created if the computed property doesn't return nil. And the destination of the navigation link has a switch which decides the screen to show and a label that is the button to be clicked.
import SwiftUI
struct MContentView: View {
#State var valueX = 17
#State var valueY = 15
#State var isNextViewActive = false
private let range8_15 = 8...15
private let range16_23 = 16...23
private let range8_23 = 8...23
private let range24_40 = 24...40
private let range8_17 = 8...17
private var screenToPresent: Int? {
if range8_15.contains(valueX) && range8_23.contains(valueY) {
return 1
} else if range8_15.contains(valueX) && range24_40.contains(valueY) {
return 2
} else if range16_23.contains(valueX) && range8_17.contains(valueY) {
return 3
}
return nil
}
var body: some View {
NavigationView {
if let screen = self.screenToPresent {
NavigationLink(isActive: self.$isNextViewActive, destination: {
switch screen {
case 1:
MView1()
case 2:
MView2()
case 3:
MView3()
default:
EmptyView()
}
}) {
Button(action: {
self.isNextViewActive = true
}) {
Text("Click me!")
}
}
}
}
}
}
struct MView1: View {
var body: some View {
Text("My View 1")
}
}
struct MView2: View {
var body: some View {
Text("My View 2")
}
}
struct MView3: View {
var body: some View {
Text("My View 3")
}
}
struct MExample_Previews: PreviewProvider {
static var previews: some View {
MContentView()
}
}
Hope is what you are looking for!

You could have hidden navigation links like these:
NavigationLink(destination: View1(), isActive: $condition1, label: { EmptyView() })
NavigationLink(destination: View2(), isActive: $condition2, label: { EmptyView() })
NavigationLink(destination: View3(), isActive: $condition3, label: { EmptyView() })
// note that having empty views for your links will keep them hidden on your layout
for each of your X and Y conditions.
So, when you check for X and Y values, you could verify them like this:
condition1 = Xcode >= 8 && Xcode <= 15 && Ycode >= 8 && Code <= 23
condition2 = Xcode >= 8 && Xcode <= 15 && Ycode >= 24 && Code <= 40
condition3 = Xcode >= 16 && Xcode <= 23 && Ycode >= 8 && Code <= 17
and that would activate the link you want and present the screen you need.

Related

In SwiftUI, How do I make it so that when I click a button, the button background color changes to reflect if the answer is right or wrong?

I am working on developing a quiz app. I have the following code, in two SwiftUI Views. Right now, the entire screen background color changes if you get an answer correct(to green)/incorrect(to red) but I want only the button background color to change, and the background of the screen to remain white. How do I implement this in the code?
Content View Swift:
import SwiftUI
struct ContentView: View {
let question = Question(questionText: "What was the first computer bug?", possibleAnswers: ["Ant", "Beetle", "Moth", "Fly"], correctAnswerIndex: 2)
#State var mainColor = Color(red: 255/255, green: 255/255, blue: 255/255)
#State var textBackgroundColor = Color.white
var body: some View {
ZStack {
mainColor.ignoresSafeArea()
VStack{
Text("Question 1 of 10")
.font(.callout)
.foregroundColor(.gray)
.padding(.bottom, 1)
.padding(.trailing, 170)
.multilineTextAlignment(.leading)
Text(question.questionText)
.font(.largeTitle)
.multilineTextAlignment(.leading)
.bold()
.background(textBackgroundColor)
Spacer()
.frame(height: 200)
VStack(spacing: 20){
ForEach(0..<question.possibleAnswers.count) { answerIndex in
Button(action: {
print("Tapped on option with the text: \(question.possibleAnswers[answerIndex])")
mainColor = answerIndex == question.correctAnswerIndex ? .green : .red
textBackgroundColor = answerIndex == question.correctAnswerIndex ? .green : .red
print(textBackgroundColor)
}, label: {
ChoiceTextView(choiceText: question.possibleAnswers[answerIndex])
})
}
}
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
ChoiceTextView:
import SwiftUI
struct ChoiceTextView: View {
let choiceText: String
let accentColor = Color(red: 48/255, green: 105/255, blue: 240/255)
#State var textBackgroundColor = Color.white
var body: some View {
Text(choiceText)
.frame(width:250)
.foregroundColor(.black)
.padding()
// make background white to put shadow later
.background(textBackgroundColor)
.cornerRadius(10)
.shadow(color: Color(hue: 1.0, saturation: 0.0, brightness: 0.869), radius: 5, x: 0, y: 5)
}
}
struct ChoiceTextView_Previews: PreviewProvider {
static var previews: some View {
ChoiceTextView(choiceText: "Choice Text!", textBackgroundColor: Color.white)
}
}
Please help me figure this out!
I think this code does what you are trying to achieve.
Basically, avoid using the color to the main view's background and instead, put it in the button background modifier.
Also, instead of using the index of the array I use the value. I think it's easier to identify plus it avoids the ForEach warning that shows up for using a non fixed Int value.
struct Question {
let questionText: String
let possibleAnswers: [String]
let correctAnswer: String
}
struct ContentView: View {
let question = Question(
questionText: "What was the first computer bug?",
possibleAnswers: ["Ant", "Beetle", "Moth", "Fly"],
correctAnswer: "Beetle"
)
var body: some View {
ZStack {
VStack{
Text("Question 1 of 10")
.font(.callout)
.foregroundColor(.gray)
.padding(.bottom, 1)
.padding(.trailing, 170)
.multilineTextAlignment(.leading)
Text(question.questionText)
.font(.largeTitle)
.multilineTextAlignment(.leading)
.bold()
Spacer()
.frame(height: 200)
VStack(spacing: 20){
ForEach(question.possibleAnswers, id: \.self) { answer in
ChoiceButton(
answer: answer,
question: question
)
}
}
}
}
}
}
struct ChoiceButton: View {
let answer: String
let question: Question
#State private var mainColor = Color(red: 255/255, green: 255/255, blue: 255/255)
var body: some View {
Button {
print("Tapped on option with the text: \(answer)")
mainColor = answer == question.correctAnswer ? .green : .red
} label: {
Text(answer)
.frame(width:250)
.foregroundColor(.black)
.padding()
// make background white to put shadow later
.background(mainColor)
.cornerRadius(10)
.shadow(color: Color(hue: 1.0, saturation: 0.0, brightness: 0.869), radius: 5, x: 0, y: 5)
}
}
}

UI element with onTapGesture function does not work Swift

I use a systemImage, with a onTapGesture function attached to it, to switch a boolean Variable to true. When that boolean variable is true, the view is changed. I positioned that systemImage at the top left part of the screen, using position(x:,y:) function. However, onTapGesture does not work when the value for "y" is bellow 100.
The code:
import SwiftUI
import FirebaseFirestoreSwift
import Firebase
struct ChatView: View {
#Environment(\.presentationMode) var presentationMode
#StateObject var homeData:HomeModel
#State var queryView:Bool = false
#EnvironmentObject var model:ContentModel
var user = Auth.auth().currentUser
let db = Firestore.firestore()
//If it is the first time when user scrolls
#State var scrolled = false
// #GestureState var isLongPressed = false
var body: some View {
if queryView == false {
VStack(spacing: 0) {
Text("\(homeData.query) Global Chat").foregroundColor(Color(#colorLiteral(red: 0.5951357484, green: 0.5694860816, blue: 1, alpha: 1))).font(.title3).padding(.top, 30)
Text("Welcome \(model.firstName) \(model.secondName) !").foregroundColor(Color(#colorLiteral(red: 0.5951357484, green: 0.5694860816, blue: 1, alpha: 1))).font(.callout)
Image(systemName: "arrow.backward.square")
.position(x: 30, y: 0)
.foregroundColor(Color(#colorLiteral(red: 0.5951357484, green: 0.5694860816, blue: 1, alpha: 1)))
.font(.system(size: 30, weight: .regular))
.onTapGesture {
withAnimation(.easeOut) {
queryView = true
}
print("TAPPED")
}
ScrollViewReader { reader in
ScrollView{
VStack(spacing: 15) {
ForEach(homeData.msgs) { msg in
ChatRow(chatData: msg, firstName: model.firstName, secondName: model.secondName).onAppear(){
if msg.id == self.homeData.msgs.last!.id && !scrolled {
reader.scrollTo(homeData.msgs.last!.id, anchor: .bottom)
scrolled = true
}
// print(model.firstName)
// print(model.secondName)
}
}.onChange(of: homeData.msgs) { value in
reader.scrollTo(homeData.msgs.last!.id, anchor: .bottom)
}
}
}.padding(.vertical)
}.frame(width: UIScreen.main.bounds.size.width, height: UIScreen.main.bounds.size.height - 135)
HStack(spacing:15) {
TextField("Enter message", text: $homeData.txt)
.padding(.horizontal)
//Fixed height for animation...
.frame(height: 45)
.foregroundColor(Color(.black))
.background(Color(#colorLiteral(red: 0.5951357484, green: 0.5694860816, blue: 1, alpha: 1)).opacity(1.0))
.clipShape(Capsule())
if homeData.txt != "" {
Button {
homeData.writeAllMessages()
} label: {
Image(systemName: "paperplane.fill")
.font(.system(size: 22))
.foregroundColor(.white)
.frame(width: 45, height: 45)
.background(Color(#colorLiteral(red: 0.5951357484, green: 0.5694860816, blue: 1, alpha: 1)))
.clipShape(Circle())
}
}
}.animation(.default)
.padding()
Spacer().frame(height: 100)
}.background(Color(.black).scaledToFill().frame(width: UIScreen.main.bounds.size.width, height: UIScreen.main.bounds.size.height).ignoresSafeArea())
.navigationBarBackButtonHidden(true)
} else {
QueryView(query: homeData.query)
}
}
}
What shall I do in order to make that TapGesture work anywhere on the screen?
To provide more info,I use the systemImage with that tapgesture function because when I use the NavigationLink back button the transition to its parent view is too slow and laggy.
It's probably because the NavigationBar is still at the top of your View even though the Back button is hidden.
Try adding .navigationBarHidden(true) instead of .navigationBarBackButtonHidden(true).

SwiftUI Problem GeometryReader with Zstack

I'm trying to create my own PhoneTextField in and I have found a tutorial in here it's working fine if don't call it inside ZStack let's see the code example as below
countryCode View
struct CountryCodes: View {
#Binding var countryCode : String
#Binding var countryFlag : String
#Binding var y : CGFloat
var body: some View {
GeometryReader { geo in
List(self.countryDictionary.sorted(by: <), id: \.key) { key , value in
HStack {
Text("\(self.flag(country: key))")
Text("\(self.countryName(countryCode: key) ?? key)")
Spacer()
Text("+\(value)").foregroundColor(.secondary)
}.background(Color.white)
.font(.system(size: 20))
.onTapGesture {
self.countryCode = value
self.countryFlag = self.flag(country: key)
withAnimation(.spring()) {
self.y = 350
}
}
}
.padding(.bottom)
.frame(width: geo.size.width, height: 500)
.position(x: geo.frame(in: .global).midX, y: geo.frame(in: .global).maxY - -200)
}
}
let countryDictionary = ["AF":"93","AL":"355","DZ":"213","US":"1",
"AD":"376","AO":"244","AI":"1","AG":"1","AR":"54",
"AM":"374","AW":"297","AU":"61","AT":"43","AZ":"994",
"BS":"1","BH":"973","BD":"880","BB":"1","BY":"375",
"BE":"32","BZ":"501","BJ":"229","BM":"1","BT":"975",
"BA":"387","BW":"267","BR":"55","IO":"246","BG":"359",
"BF":"226","BI":"257","KH":"855","CM":"237","CA":"1",
"CV":"238","KY":"345","CF":"236","TD":"235","CL":"56","CN":"86",
"CX":"61","CO":"57","KM":"269","CG":"242","CK":"682","CR":"506",
"HR":"385","CU":"53","CY":"537","CZ":"420","DK":"45","DJ":"253",
"DM":"1","DO":"1","EC":"593","EG":"20","SV":"503","GQ":"240",
"ER":"291","EE":"372","ET":"251","FO":"298","FJ":"679","FI":"358",
"FR":"33","GF":"594","PF":"689","GA":"241","GM":"220","GE":"995",
"DE":"49","GH":"233","GI":"350","GR":"30","GL":"299","GD":"1",
"GP":"590","GU":"1","GT":"502","GN":"224","GW":"245","GY":"595","HT":"509",
"HN":"504","HU":"36","IS":"354","IN":"91","ID":"62","IQ":"964",
"IE":"353","IL":"972","IT":"39","JM":"1","JP":"81","JO":"962",
"KZ":"77","KE":"254","KI":"686","KW":"965","KG":"996","LV":"371",
"LB":"961","LS":"266","LR":"231","LI":"423","LT":"370","LU":"352",
"MG":"261","MW":"265","MY":"60","MV":"960","ML":"223",
"MT":"356","MH":"692","MQ":"596","MR":"222","MU":"230","YT":"262",
"MX":"52","MC":"377","MN":"976", "ME":"382","MS":"1","MA":"212",
"MM":"95","NA":"264","NR":"674","NP":"977","NL":"31","NC":"687",
"NZ":"64","NI":"505","NE":"227","NG":"234","NU":"683",
"NF":"672","MP":"1","NO":"47","OM":"968","PK":"92","PW":"680",
"PA":"507","PG":"675","PY":"595","PE":"51","PH":"63","PL":"48",
"PT":"351","PR":"1","QA":"974","RO":"40","RW":"250","WS":"685",
"SM":"378","SA":"966","SN":"221","RS":"381","SC":"248",
"SL":"232","SG":"65","SK":"421","SI":"386","SB":"677",
"ZA":"27","GS":"500","ES":"34","LK":"94","SD":"249","SR":"597",
"SZ":"268","SE":"46","CH":"41","TJ":"992","TH":"66","TG":"228",
"TK":"690","TO":"676","TT":"1","TN":"216","TR":"90",
"TM":"993","TC":"1","TV":"688","UG":"256","UA":"380",
"AE":"971","GB":"44","AS":"1","UY":"598","UZ":"998",
"VU":"678","WF":"681","YE":"967","ZM":"260",
"ZW":"263","BO":"591","BN":"673","CC":"61",
"CD":"243","CI":"225","FK":"500","GG":"44",
"VA":"379","HK":"852","IR":"98","IM":"44",
"JE":"44","KP":"850","KR":"82","LA":"856",
"LY":"218","MO":"853","MK":"389","FM":"691",
"MD":"373","MZ":"258","PS":"970","PN":"872",
"RE":"262","RU":"7","BL":"590","SH":"290","KN":"1",
"LC":"1","MF":"590","PM":"508","VC":"1","ST":"239",
"SO":"252","SJ":"47","SY":"963","TW":"886","TZ":"255",
"TL":"670","VE":"58","VN":"84","VG":"284","VI":"340"]
func countryName(countryCode: String) -> String? {
let current = Locale(identifier: "en_US")
return current.localizedString(forRegionCode: countryCode)
}
func flag(country:String) -> String {
let base : UInt32 = 127397
var flag = ""
for v in country.unicodeScalars {
flag.unicodeScalars.append(UnicodeScalar(base + v.value)!)
}
return flag
}
}
RegistrationView
//
// RegisterView.swift
// Instagram
//
// Created by Admin on 4/4/21.
//
import SwiftUI
import Combine
struct RegistrationView: View {
#State var isEditing: Bool = false
// navigate to verification view
#State private var selection: String? = nil
#State private var email:String = ""
#State private var phone:String = ""
#State private var fullname = ""
#State private var username = ""
#State private var password = ""
#State private var selectedImage:UIImage?
#State private var image:Image?
#Environment(\.presentationMode) var mode
#State var imagePickerPresented = false
#EnvironmentObject var viewModel: AuthViewModel
#State var isVerification = false
#State var phoneNumber = ""
#State var y : CGFloat = 350
#State var countryCode = ""
#State var countryFlag = ""
#State var isInputPhoneNumber = true
#State var isInputEmail = false
#State var tempData = DataTempo()
#State var isShowSignInButton = true
// var successClosure: (Bool) -> Void {
//
// }
#EnvironmentObject var mobileVerificationViewModel: MobileVerificationNvm
var body: some View {
ZStack {
LinearGradient(gradient: Gradient(colors: [Color.purple, Color("PrimaryColor")]), startPoint: .top, endPoint: .bottom)
.ignoresSafeArea()
VStack{
// Spacer()
VStack(spacing:20){
VStack(alignment:.leading){
Text("Sign Up")
.font(.largeTitle)
.bold()
.foregroundColor(.white)
}.padding(.horizontal,32).frame(width: UIScreen.main.bounds.width, alignment: .leading)
Toggle( isInputEmail ? "Use Email" : "Use Phone Number", isOn: $isInputEmail)
.foregroundColor(.white)
.toggleStyle(SwitchToggleStyle(tint: Color(#colorLiteral(red: 0.5568627715, green: 0.3529411852, blue: 0.9686274529, alpha: 1))))
.padding(.horizontal,32)
if isInputEmail == true {
CustomTextField(text: $email, placeholder: Text("Email"), imageName: "envelope")
.padding()
.background(Color(.init(white: 1, alpha: 0.15)))
.cornerRadius(10)
.foregroundColor(.white)
.padding(.horizontal,32)
.onChange(of: email) {
// self.autocomplete($0) // like this
if viewModel.isValidEmail(testStr: $0){
print("email")
//phone number
}
}
}
if isInputEmail == false{
ZStack(alignment:.center) {
HStack (spacing: 0) {
Text(countryCode.isEmpty ? "🇹🇭 +66" : "\(countryFlag) +\(countryCode)")
.frame(width: 100, height: 50)
// .background(Color.secondary.opacity(0.2))
.background(Color(.init(white: 1, alpha: 0.15)))
.cornerRadius(10)
// .foregroundColor(countryCode.isEmpty ? .secondary : .black)
.foregroundColor(.white)
.onTapGesture {
isShowSignInButton = false
withAnimation (.spring()) {
self.y = 0
}
}
TextField("Phone Number", text: $phone)
.onChange(of: phone) { newValue in
if !phone.isEmpty {
self.isShowSignInButton = true
if viewModel.validatePhoneNumber(value:newValue){
print("phone")
//phone number
self.isInputPhoneNumber = true
self.isInputEmail = false
}
}
else{
self.isShowSignInButton = false
}
}
.frame(width: 250, height: 50)
.keyboardType(.phonePad)
.background(Color(.init(white: 1, alpha: 0.15)))
.foregroundColor(.white)
}
CountryCodes(countryCode: $countryCode, countryFlag: $countryFlag, y: $y)
.offset(y: y)
RoundedRectangle(cornerRadius: 10).stroke()
.frame(width: 350, height: 50)
.foregroundColor(.white)
} .zIndex(1).frame( height: 50, alignment: .center)
}
CustomTextField(text: $username, placeholder: Text("Username"), imageName: "person")
.padding()
.background(Color(.init(white: 1, alpha: 0.15)))
.cornerRadius(10)
.foregroundColor(.white)
.padding(.horizontal,32)
CustomTextField(text: $fullname, placeholder: Text("Full Name"), imageName: "person")
.padding()
.background(Color(.init(white: 1, alpha: 0.15)))
.cornerRadius(10)
.foregroundColor(.white)
.padding(.horizontal,32)
CustomSecureField(text: $password, placeholder: Text("Password"))
.padding()
.background(Color(.init(white: 1, alpha: 0.15)))
.cornerRadius(10)
.foregroundColor(.white)
.padding(.horizontal,32)
}
.alert(isPresented: $isInputPhoneNumber, content: {
Alert(title: Text("Message"), message: Text("you're using phone number to register"), dismissButton: .default(Text("Bye alert!"), action: {
isInputPhoneNumber = true
}))
})
.alert(isPresented: mobileVerificationViewModel.alertBinding) {
Alert(title: Text(mobileVerificationViewModel.errorMessage ?? "(unknown)"), message: nil, dismissButton: .cancel())
}
HStack{
Spacer()
Button(action: /*#START_MENU_TOKEN#*/{}/*#END_MENU_TOKEN#*/, label: {
Text("Forgot Password")
})
.font(.system(size: 13, weight: .semibold))
.foregroundColor(.white)
.padding(.top)
.padding(.trailing,28)
}
ZStack{
NavigationLink(destination:VerificationView { (pin, successClosure) in
print(pin)
mobileVerificationViewModel.codeVerification(phoneNumber: isInputEmail ? tempData.email : tempData.phone, pinCode: pin, email: email, password: password, fullname: fullname, username: username)
//navigation back
self.mode.wrappedValue.dismiss()
// print(successClosure)
}, tag: "verification", selection: $selection) { EmptyView() }
Button(action: {
self.selection = "verification"
var phoneWithCountryCode = ""
// if countryCode == "" {
// phoneWithCountryCode = "+66\(phone)"
// }
// if countryCode != ""{
// phoneWithCountryCode = "+\(countryCode)\(phone)"
// }
//
// // "+\(countryCode == "" ? "66" : countryCode + phone)"
// if (!email.isEmpty || !phone.isEmpty) && !password.isEmpty{
//
// if isInputEmail {
// MobileVerificationNvm.shared.sendVerification(phoneNumber:email)
// isVerification = true
// tempData.email = email
// }
//
// else{
// DispatchQueue.main.async {
// // print("phone:\(phoneWithCountryCode)")
// MobileVerificationNvm.shared.sendVerification(phoneNumber: phoneWithCountryCode)
// isVerification = true
// tempData.phone = phone
// }
//
// }
//
// }
// viewModel.register(withEmail: email, password: password,image: selectedImage,fullname: fullname,username: username)
}, label: {
Text("Sign Up")
}).zIndex(0)
// Spacer()
}
.font(.headline)
.frame(width: 360, height: 50)
.foregroundColor(.white)
.background(Color(#colorLiteral(red: 0.5568627715, green: 0.3529411852, blue: 0.9686274529, alpha: 1)))
// .clipShape(Capsule())
.clipShape(RoundedRectangle(cornerRadius: 16, style: .continuous))
.padding()
.alert(isPresented: $isInputEmail, content: {
Alert(title: Text("Message"), message: Text("Now! you're using Email to register but you also register with email just swtich on togle button"), dismissButton: .default(Text("Bye alert!"), action: {
isInputEmail = true
}))
})
Button(action: {
mode.wrappedValue.dismiss()
// isVerification = true
}, label: {
Text("Already have an account? ")
.font(.system(size: 14))
+ Text("Sign In")
.font(.system(size: 14, weight: .bold))
})
.foregroundColor(.white)
.padding(.bottom,32)
}
}
.ignoresSafeArea()
}
}
extension RegistrationView{
func loadImage() {
guard let selectedImage = selectedImage else {
return
}
image = Image(uiImage: selectedImage)
}
}
//
struct RegistrationView_Previews: PreviewProvider {
static var previews: some View {
RegistrationView()
}
}
this is my issue when I tap on country code to choose the country code you will see the button sign in over countries code list
you also can check from [here]:github.com/PhanithNoch/SwiftUIPhoneTextField this is the repo that I tried to separate code from my real project as an example
I stuck with this a few days ago hope anyone can help, please.

How do I dismiss the TabView in SwiftUI?

I'm stuck trying to figure out how to dismiss an existing TabView in my SwiftUI application. Basically, it works like this:
The SceneDelegate launches a view called MainScreenView()
struct MainScreenView: View {
#State var isLoadingViewEnabled = false
#State var isSuccessViewEnabled = false
#State var showLoopingBlobs = false
#State var showTabView = false
#ObservedObject var user = UsrAccount()
#ObservedObject var authManager = FirebaseAuthManager()
#ObservedObject var keyboardResponder = KeyboardResponder()
var body: some View {
Group {
if !showTabView {
ZStack {
Color(#colorLiteral(red: 0.1647058824, green: 0.6862745098, blue: 0.9490196078, alpha: 1))
.edgesIgnoringSafeArea(.all)
ZStack {
VStack {
LogoView()
LoginView(user: user)
.padding(.bottom, 100)
LoginButton(
isLoadingViewEnabled: $isLoadingViewEnabled,
isSuccessViewEnabled: $isSuccessViewEnabled,
showTabView: $showTabView,
user: user,
authManager: authManager
)
ForgotPassword(user: user, authManager: authManager)
.padding(.top, 20)
RegisterNewUserView(authManager: authManager, user: user)
.padding(.top, 20)
Spacer()
}
.background(
Color(#colorLiteral(red: 0.1647058824, green: 0.6862745098, blue: 0.9490196078, alpha: 1))
.edgesIgnoringSafeArea(.all)
// BlobRotationView(showLoopingBlobs: $showLoopingBlobs)
)
if isLoadingViewEnabled {
LoadingView()
}
if isSuccessViewEnabled {
LoginSuccessView()
}
}
.frame(maxWidth: .infinity)
.frame(maxHeight: .infinity)
}
.offset(y: -keyboardResponder.currentHeight*0.20)
.animation(.easeInOut(duration: 0.85))
.onTapGesture { KeyboardInteraction.hideKeyboard() }
} else {
TabBar(user: user, authManager: authManager, keyboardResponder: keyboardResponder)
.animation(.easeInOut)
}
}
}
}
Once there is a successful login to Firebase (code not shown), a tab view will get shown:
import SwiftUI
struct TabBar: View {
#State private var selectedTab = 0
#ObservedObject var user: UsrAccount
#ObservedObject var authManager: FirebaseAuthManager
#ObservedObject var keyboardResponder: KeyboardResponder
var body: some View {
TabView(selection: $selectedTab) {
HomeView(user: user, authManager: authManager, keyboardResponder: keyboardResponder)
.onTapGesture {
self.selectedTab = 0
}
.tabItem {
Image(systemName: "house")
.font(.title)
Text("Home")
}
.tag(0)
ScheduleLessonsView()
.onTapGesture {
self.selectedTab = 1
}
.tabItem {
Image(systemName: "calendar.badge.clock")
.font(.title)
Text("Schedule")
}
.tag(1)
PurchaseLessonsView()
.onTapGesture {
self.selectedTab = 2
}
.tabItem {
Image(systemName: "cart.fill.badge.plus")
.font(.title)
Text("Purchase")
}
.tag(2)
UpdateProfileView(authManager: authManager, user: user)
.onTapGesture {
self.selectedTab = 3
}
.tabItem {
Image(systemName: "person.fill")
.font(.title)
Text("Profile")
}
.tag(3)
AboutUsView()
.onTapGesture {
self.selectedTab = 4
}
.tabItem {
Image(systemName: "questionmark.circle")
.font(.title)
Text("About Us")
}
.tag(4)
}
.edgesIgnoringSafeArea(.top)
}
}
struct TabBar_Previews: PreviewProvider {
static var previews: some View {
TabBar(user: UsrAccount(), authManager: FirebaseAuthManager(), keyboardResponder: KeyboardResponder())
}
}
As you can see from the TabView, the HomeView is the initial tab that is shown. I have setup an exit button in that view to log the user out of Firebase and send the user back to the login screen (MainScreenView).
struct HomeView: View {
#ObservedObject var user: UsrAccount
#ObservedObject var authManager: FirebaseAuthManager
#ObservedObject var keyboardResponder: KeyboardResponder
#State var isCardSelected = false
#State var didSignOutUser = false
var body: some View {
Group {
if didSignOutUser {
MainScreenView()
} else {
ZStack {
Color(#colorLiteral(red: 1, green: 1, blue: 1, alpha: 1))
.edgesIgnoringSafeArea(.all)
VStack {
HStack {
Spacer()
Button(action: {
didSignOutUser = authManager.signOut()
print("**** Sign Out status: \(didSignOutUser)")
}) {
Text("X")
.foregroundColor(Color(#colorLiteral(red: 0.08235294118, green: 0.2274509804, blue: 0.4078431373, alpha: 1)))
.fontWeight(.bold)
.font(.system(size: 40))
.frame(width: 44, height: 44)
.background(Color.white)
.clipShape(RoundedRectangle(cornerRadius: 8, style: .continuous))
.shadow(color: Color.black.opacity(0.15), radius: 5, x: 0, y: 5)
.padding(.leading)
}
.animation(.easeInOut)
}
.padding(.horizontal)
HStack {
VStack {
Text("Welcome!")
.font(.system(.title))
.bold()
.foregroundColor(Color(#colorLiteral(red: 0.07843137255, green: 0.3294117647, blue: 0.4078431373, alpha: 1)))
}
Spacer()
}
.padding(.horizontal, 16)
.padding(.top, 25)
.padding(.bottom, 10)
ScrollView(.horizontal, showsIndicators: false) {
HStack(spacing: 20) {
ForEach(homeCardData) { item in
GeometryReader { geometry in
HomeCardView(homeCard: item, isCardSelected: self.$isCardSelected)
.rotation3DEffect(Angle(degrees:
Double(geometry.frame(in: .global).minX - 30) / -30
), axis: (x: 0, y: 10, z: 0))
}
.frame(width: 200, height: 200)
}
}
.padding(30)
.padding(.bottom, 30)
}
HStack {
VStack {
Text("Here are your upcoming classes:")
.font(.system(.body))
.bold()
.foregroundColor(Color(#colorLiteral(red: 0.07843137255, green: 0.3294117647, blue: 0.4078431373, alpha: 1)))
}
Spacer()
}
.padding(.horizontal, 16)
.padding(.bottom, 5)
NoClassView()
Spacer()
}
}
}
}
}
}
The problem is that the TabView still shows up at the bottom of the MainScreenView, which is wrong. It also seems that the user isn't even logged out.
The SignOut method is called on my FirebaseAuthManager class:
func signOut() -> Bool {
do {
try Auth.auth().signOut()
self.user = nil
return true
} catch {
return false
}
}
The above method is returning "true", so it seems that it is signing the user out.
So.. 2 questions:
How can I confirm the user is signed out when the user is sent back to MainScreenView?
How can I make sure the TabView gets destroyed when the user chooses to exit from one of the screens in the TabView?

How to enable button when multiple selection is done and how to disable other choices?

I have 10 different choices in my view. I want to enable "Devam Et" button when any of 10 choices are made. It sounds easy but critical part is as following...When I click any of the first 8 buttons, I want to disable the last 2 buttons if they are selected and also if I select any of the last two options, I want to disable all the other 8 options if they are selected.
The code of the first 3 button configuration lines is as following...Remaining ones are same as these.
VStack{
HStack {
Button(action: {
self.tap1.toggle()
}) {
ZStack {
Rectangle()
.fill(self.tap1 ? Color(#colorLiteral(red: 0.9254902005, green: 0.2352941185, blue: 0.1019607857, alpha: 1)) : Color(#colorLiteral(red: 0.936548737, green: 0.936548737, blue: 0.936548737, alpha: 1)))
.frame(width: 25, height: 25)
if self.tap1 {
Image(systemName: "checkmark")
}
}.padding(.leading, 40)
}
Spacer()
Text("Diyabet")
.font(.system(size: 20, weight: .regular, design: .rounded))
.padding(.trailing, 200)
Spacer()
}.padding(.bottom, 10)
HStack {
Button(action: {
self.tap2.toggle()
}) {
ZStack {
Rectangle()
.fill(self.tap2 ? Color(#colorLiteral(red: 0.9254902005, green: 0.2352941185, blue: 0.1019607857, alpha: 1)) : Color(#colorLiteral(red: 0.936548737, green: 0.936548737, blue: 0.936548737, alpha: 1)))
.frame(width: 25, height: 25)
if self.tap2 {
Image(systemName: "checkmark")
}
}.padding(.leading, 40)
}
Spacer()
Text("Yüksek Tansiyon")
.font(.system(size: 20, weight: .regular, design: .rounded))
.padding(.trailing, 130)
Spacer()
}.padding(.bottom, 10)
HStack {
Button(action: {
self.tap3.toggle()
}) {
ZStack {
Rectangle()
.fill(self.tap3 ? Color(#colorLiteral(red: 0.9254902005, green: 0.2352941185, blue: 0.1019607857, alpha: 1)) : Color(#colorLiteral(red: 0.936548737, green: 0.936548737, blue: 0.936548737, alpha: 1)))
.frame(width: 25, height: 25)
if self.tap3 {
Image(systemName: "checkmark")
}
}.padding(.leading, 40)
}
button1
}
The code for "Devam Et" button is as following...
var button1: some View{
return Button(action: {
if self.tap1 == true || self.tap2 == true || self.tap3 == true || self.tap4 == true || self.tap5 == true || self.tap6 == true || self.tap7 == true || self.tap8 == true {
self.tap11.toggle()
}
else if self.tap9 == true {
self.tap11.toggle()
}
else if self.tap10 == true {
self.tap11.toggle()
}
}) {
Text("Devam Et")
.font(.system(size: 20, weight: .regular, design: .rounded))
.foregroundColor(Color.white)
.frame(width: 200, height: 30)
.padding()
.background(Color(#colorLiteral(red: 0.3101329505, green: 0.193462044, blue: 0.3823927939, alpha: 1)))
.cornerRadius(40)
.shadow(color: .gray, radius: 20.0, x: 20, y: 10)
.padding(.bottom, 70)
}.background(
NavigationLink(destination: destinationView, isActive: $tap11) {
EmptyView()
}
.hidden()
)
}
#ViewBuilder
var destinationView: some View {
if tap1 || tap2 || tap3 || tap4 || tap5 || tap6 || tap7 || tap8 || tap9 || tap10{
entrance5()
}
}
TL;DR: Result video: https://imgur.com/wRx2Ezg
Result output: https://imgur.com/H8fWwg0
Alright, this is a bit of a lengthy answer, before that I'll comment on some of your previous code:
It's not really a good choice to keep every boolean in their respective field, as you noticed it's really hard to keep track of them. Instead, you could have some sort of a struct that keeps track of every choice that you are providing.
What you also need is a proper state tracking, currently you need 3 type of state:
Standard state: Nothing has selected, yet.
Multiple choice state: There can be multiple selections, except the exclusive selections.
Exclusive choice state: Only one can be selected, in their respective group.
You also need to integrate it to your choices, to determine which kind you have selected in the list.
Last but not least, it should be handled outside of your current view, like in an interactor.
Here is the full code, ready to be tested in Playgrounds:
import SwiftUI
import PlaygroundSupport
enum ContentType {
case standard
case exclusive
}
enum ContentState {
case none
case multipleChoice
case exclusiveChoice
}
struct ContentChoice: Identifiable {
var id: String { title }
let title: String
let type: ContentType
var isSelected: Bool = false
var isDisabled: Bool = false
}
class ContentInteractor: ObservableObject, ContentChoiceViewDelegate {
#Published var choices: [ContentChoice] = []
#Published var state: ContentState = .none {
didSet {
print("state is now: \(state)")
switch state {
case .none:
exclusiveChoices.forEach { choices[$0].isDisabled = false }
standardChoices.forEach { choices[$0].isDisabled = false }
case .multipleChoice:
exclusiveChoices.forEach { choices[$0].isDisabled = true }
case .exclusiveChoice:
standardChoices.forEach { choices[$0].isDisabled = true }
}
}
}
private var exclusiveChoices: [Int] {
choices.indices.filter { choices[$0].type == .exclusive }
}
private var standardChoices: [Int] {
choices.indices.filter { choices[$0].type == .standard }
}
private var isExclusiveChoiceSelected: Bool {
choices.filter { $0.type == .standard && $0.isSelected }.count > 0
}
private var selectedMultipleChoiceCount: Int {
choices.filter { $0.type == .standard && $0.isSelected }.count
}
func didToggleChoice(_ choice: ContentChoice) {
guard let index = choices.firstIndex(where: { $0.id == choice.id }) else {
fatalError("No choice found with the given id.")
}
// This is where the whole algorithm lies.
switch state {
// Phase 1:
// If the user has not made any choice (state == .none),
// Enabling a `.standard` choice should lock the `.exclusive` choices.
// And vice versa.
case .none:
choices[index].isSelected.toggle()
switch choice.type {
case .standard:
state = .multipleChoice
case .exclusive:
state = .exclusiveChoice
}
// Phase 2:
// If the user is in multiple choice state,
// They can only select multiple choices. If any of the multiple choice
// is still selected, it should stay as is.
// If every choice is deselected, it should return the state to `.none`.
case .multipleChoice:
choices[index].isSelected.toggle()
switch choice.type {
case .standard:
if selectedMultipleChoiceCount == 0 {
state = .none
}
case .exclusive:
preconditionFailure("Unexpected choice selection.")
}
// Phase 3:
// If the user is in a not-answering state,
// They can only change it within themselves.
// If every choice is deselected, it should return the state to `.none`.
// Also, every exclusive choice is, exclusive.
// Hence, if one of them is selected, the others should be deselected.
case .exclusiveChoice:
switch choice.type {
case .standard:
preconditionFailure("Unexpected choice selection.")
case .exclusive:
let isSelecting = !choices[index].isSelected
if isSelecting {
exclusiveChoices.forEach { choices[$0].isSelected = false }
} else {
state = .none
}
}
choices[index].isSelected.toggle()
}
}
func didSelectSubmit() {
print("Current selection:", choices.filter { $0.isSelected }.map { $0.title })
}
}
protocol ContentChoiceViewDelegate: AnyObject {
func didToggleChoice(_ choice: ContentChoice)
func didSelectSubmit()
}
struct ContentChoiceView: View {
let choice: ContentChoice
weak var delegate: ContentChoiceViewDelegate!
init(choice: ContentChoice, delegate: ContentChoiceViewDelegate) {
self.choice = choice
self.delegate = delegate
}
var body: some View {
HStack {
Button(action: {
self.delegate.didToggleChoice(self.choice)
}) {
ZStack {
Rectangle()
.fill(self.choice.isSelected ? Color(#colorLiteral(red: 0.9254902005, green: 0.2352941185, blue: 0.1019607857, alpha: 1)) : Color(#colorLiteral(red: 0.936548737, green: 0.936548737, blue: 0.936548737, alpha: 1)))
.frame(width: 25, height: 25)
if self.choice.isSelected {
Image(systemName: "checkmark")
}
}.padding(.leading, 40)
}.disabled(self.choice.isDisabled)
Spacer()
Text(choice.title)
.font(.system(size: 20, weight: .regular, design: .rounded))
// .padding(.trailing, 200)
Spacer()
}.padding(.bottom, 10)
}
}
struct ContentView: View {
#ObservedObject var interactor: ContentInteractor
var body: some View {
VStack {
ForEach(interactor.choices) { choice in
ContentChoiceView(choice: choice, delegate: self.interactor)
}
Spacer()
submitButton
}.padding(.vertical, 70)
}
var submitButton: some View {
Button(action: {
self.interactor.didSelectSubmit()
}) {
Text("Devam Et")
.font(.system(size: 20, weight: .regular, design: .rounded))
.foregroundColor(Color.white)
.frame(width: 200, height: 30)
.padding()
.background(Color(#colorLiteral(red: 0.3101329505, green: 0.193462044, blue: 0.3823927939, alpha: 1)))
.cornerRadius(40)
.shadow(color: .gray, radius: 20.0, x: 20, y: 10)
}
}
}
let interactor = ContentInteractor()
["Diyabet", "Yuksek Tansiyon", "Astim"].forEach { title in
interactor.choices.append(ContentChoice(title: title, type: .standard))
}
["Soylememeyi Tercih Ederim", "Hicbirini Gecirmedim"].forEach { title in
interactor.choices.append(ContentChoice(title: title, type: .exclusive))
}
let contentView = ContentView(interactor: interactor)
PlaygroundPage.current.setLiveView(contentView)