SwiftUI open another view on button click - ios

Am trying to navigate to another view page. I tried many methods but none worked. If there is a way to navigate to another page without navigationtool... then it will be much help full
struct ContentView: View {
#State var fname: String = ""
#State var lname: String = ""
#State var num: String = ""
#State var pass: String = ""
#State private var registerionAlert = false
#State private var registerionAlertActive: ActiveAlert = .success
var body: some View {
VStack(alignment: .center) {
Form {
Text("Registeration Form")
.font(.title)
.fontWeight(.bold)
.foregroundColor(Color.blue)
.multilineTextAlignment(.center)
.padding(.leading, 25.0)
TextField("Enter your first name...", text: $fname)
.padding()
.border(/*#START_MENU_TOKEN#*/Color.blue/*#END_MENU_TOKEN#*/, width: /*#START_MENU_TOKEN#*/1/*#END_MENU_TOKEN#*/)
TextField("Enter your last name...", text: $lname)
.padding()
.border(/*#START_MENU_TOKEN#*/Color.blue/*#END_MENU_TOKEN#*/, width: /*#START_MENU_TOKEN#*/1/*#END_MENU_TOKEN#*/)
TextField("Enter your phone number...", text: $num)
.padding()
.border(/*#START_MENU_TOKEN#*/Color.blue/*#END_MENU_TOKEN#*/, width: /*#START_MENU_TOKEN#*/1/*#END_MENU_TOKEN#*/)
TextField("Enter a password...", text: $pass)
.padding()
.border(/*#START_MENU_TOKEN#*/Color.blue/*#END_MENU_TOKEN#*/, width: /*#START_MENU_TOKEN#*/1/*#END_MENU_TOKEN#*/)
Button(action: {
self.registerTapped()
}) {
Text("Register")
.multilineTextAlignment(.center)
}
.padding(0.0)
.frame(width: 150.0, height: 30.0)
.background(/*#START_MENU_TOKEN#*/Color.green/*#END_MENU_TOKEN#*/)
.accentColor(/*#START_MENU_TOKEN#*/.white/*#END_MENU_TOKEN#*/)
.cornerRadius(/*#START_MENU_TOKEN#*/5.0/*#END_MENU_TOKEN#*/)
.offset(x: 95.0, y: /*#START_MENU_TOKEN#*/0.0/*#END_MENU_TOKEN#*/)
.alert(isPresented: $registerionAlert){
switch registerionAlertActive {
case .success:
return Alert(
title: Text("Register"),
message: Text("Registered Successfully"),
dismissButton: .default(
Text("Got it!"),
action: {
loginView()
print("success")
}
)
)
case .failed:
return Alert(
title: Text("Register"),
message: Text("Registered Unsuccessful, Try again later."),
dismissButton: .default(
Text("Got it!"))
)
}
}
}
.padding(10.0)
.background(/*#START_MENU_TOKEN#*/Color.blue/*#END_MENU_TOKEN#*/)
}
.padding()
.background(/*#START_MENU_TOKEN#*/Color.orange/*#END_MENU_TOKEN#*/)
}
func registerTapped() {
let params: [String: Any] = [
"first_name": "\(fname)",
"last_name": "\(lname)",
"phone": "\(num)",
"password": "\(pass)"
]
AF.request(
"http://192.168.0.9/mobile-api/public/register",
method: .post,
parameters: params,
encoding: JSONEncoding.default
).validate().responseString() {
response in
switch response.result {
case .success(_):
self.registerionAlertActive = .success
break
case .failure(_):
self.registerionAlertActive = .failed
break
}
self.registerionAlert = true
}
}
}
my another page is given below in which i haven't add much of things is only sample views
import SwiftUI
struct loginView: View {
var body: some View {
Text(/*#START_MENU_TOKEN#*/"Hello, World!"/*#END_MENU_TOKEN#*/)
}
}
struct loginView_Previews: PreviewProvider {
static var previews: some View {
loginView()
}
}
please help me navigate to another page

you can do this
struct ContentView: View {
#State var isOpen: Bool = false
var body: some View {
ZStack {
VStack {
Button(action: {
self.isOpen = true
}, label: {
Text("Tap me")
.foregroundColor(obj_saved_color.set_color)
}).sheet(isPresented: $isOpen, content: {
SecondView()
})
}
}
}}
struct SecondView: View {
#Environment(\.presentationMode) var presentationMode: Binding<PresentationMode>
var body: some View {
VStack {
Text("Second View")
}
}}

Related

Animation not working inside sheet for swiftui

I am using a sheet to present a list of options and on click of the option I want to change the view with the animation of sliding from trailing. As per my understanding and what I have read on various sites I have written this code but I am not sure why it is not working the way intended. I just want to know where exactly this code went wrong.
struct XYZ: App {
let persistenceController = PersistenceController.shared
#State var isPresented : Bool = false
#State var isSwiped : Bool = false
var body: some Scene {
WindowGroup {
optionList(isPresented: $isPresented)
.sheet(isPresented: $isPresented, content: {
Text("This is from modal view!")
.onTapGesture {
withAnimation(Animation.easeIn(duration: 10)){
isSwiped.toggle()
}
}
if isSwiped {
checkedList()
.transition(.move(edge: .trailing))
}
})
}
}
}
struct optionList : View {
#Binding var isPresented : Bool
var body: some View {
Text("This is a testView")
.onTapGesture {
withAnimation{
isPresented.toggle()
}
}
}
}
struct checkedList : View {
#State var name : String = "WatchList"
var arr = ["First", "Second", "Third", "Fourth", "Fifth", "Sixth", "Seventh"]
#State var mp : [Int:Int] = [:]
var body: some View {
VStack{
HStack{
TextField("WatchlistName", text: $name)
.padding(.all)
Image(systemName: "trash.fill")
.padding(.all)
.onTapGesture {
print("Delete watchList!!")
}
}
ScrollView{
ForEach(arr.indices) { item in
HStack (spacing: 0) {
Image(systemName: mp.keys.contains(item) ? "checkmark.square" : "square")
.padding(.horizontal)
Text(arr[item])
}
.padding(.bottom)
.frame(width: UIScreen.main.bounds.width, alignment: .leading)
.onTapGesture {
if mp.keys.contains(item) {
mp[item] = nil
} else {
mp[item] = 1
}
}
}
}
Button {
print("Remove Ticked Elements!")
deleteWatchListItem(arr: Array(mp.keys))
} label: {
Text("Save")
}
}
}
func deleteWatchList(ind: Int){
print(ind)
}
func deleteWatchListItem(arr : [Int]) {
print(arr)
}
}
I tried to create a view and with the animation using withanimation with a bool variable tried to change the view.
It sounds like what you want is to push the checkedList on to a NavigationStack…
struct ContentView: View {
#State var isPresented : Bool = false
var body: some View {
Text("This is a testView")
.onTapGesture {
isPresented.toggle()
}
.sheet(isPresented: $isPresented, content: {
NavigationStack {
NavigationLink("This is from modal view!") {
checkedList()
}
}
})
}
}

SwiftUI NavigationLink doesn't trigger with onReceive method

I'm trying to learn how to do basic login in swift with Firebase and it's causing me to lose my mind over navigating to the main page of the app once the login is complete. I have a ViewModel that manages the login, and in the view I have added an onReceive property to listen on a viewModel's boolean to detect sign in and trigger the navigation. I don't understand why it's not working, any help would be greatly appreciated!
ViewModel:
class LoginViewModel: ObservableObject {
let auth = Auth.auth()
var userInfo: User?
#Published var isSignedIn = false
func signIn(email: String, password: String) {
auth.signIn(withEmail: email, password: password) { _, error in
if let error = error as? NSError {
print("Error happened on login"+error.description)
} else {
print("Login successful")
if let user = self.auth.currentUser {
print("We have a user")
self.userInfo = user
self.isSignedIn.toggle()
}
}
}
}
}
View:
struct LoginPage: View {
#State var email = ""
#State var password = ""
#ObservedObject var viewModel = LoginViewModel()
var body: some View {
NavigationView {
ZStack {
VStack {
TextField("Email", text: $email).padding()
.background(Color(.secondarySystemBackground))
.padding()
TextField("Password", text: $password).padding()
.background(Color(.secondarySystemBackground))
.padding()
Button(action: {
guard !email.isEmpty, !password.isEmpty else {
return
}
viewModel.signIn(email: email, password: password)
}, label: {
Text("Sign in")
.padding(EdgeInsets(top: 12, leading: 35, bottom: 12, trailing: 35))
.foregroundColor(.white)
.background(Color.blue)
.cornerRadius(15)
})
}
}.navigationTitle("Login")
}.onReceive(viewModel.$isSignedIn) { isSignedIn in
if isSignedIn {
print("ok damm")
NavigationLink(destination: HomePage()) {
EmptyView()
}
}
}
}
}
Note that "ok damm" prints every time.
Try the below code it will help you to solve your issue:
NavigationLink(destination: Text("Second View"), isActive: $isSignedIn){
EmptyView()
}
View:
struct LoginPage: View {
#State var email = ""
#State var password = ""
#ObservedObject var viewModel = LoginViewModel()
var body: some View {
NavigationView {
ZStack {
VStack {
TextField("Email", text: $email).padding()
.background(Color(.secondarySystemBackground))
.padding()
TextField("Password", text: $password).padding()
.background(Color(.secondarySystemBackground))
.padding()
Button(action: {
guard !email.isEmpty, !password.isEmpty else {
return
}
viewModel.signIn(email: email, password: password)
}, label: {
Text("Sign in")
.padding(EdgeInsets(top: 12, leading: 35, bottom: 12, trailing: 35))
.foregroundColor(.white)
.background(Color.blue)
.cornerRadius(15)
})
}
NavigationLink(destination: HomePage()), isActive: $isSignedIn){
EmptyView()
}
}.navigationTitle("Login")
}
}
}
Answer by Jatin Bhuva works but is deprecated for iOS 16. Here's the solution for new iOS versions:
struct LoginPage: View {
#State var email = ""
#State var password = ""
#ObservedObject var viewModel = LoginViewModel()
var body: some View {
NavigationStack {
ZStack {
VStack {
TextField("Email", text: $email).padding()
.background(Color(.secondarySystemBackground))
.padding()
TextField("Password", text: $password).padding()
.background(Color(.secondarySystemBackground))
.padding()
Button(action: {
guard !email.isEmpty, !password.isEmpty else {
return
}
viewModel.signIn(email: email, password: password)
}, label: {
Text("Sign in")
.padding(EdgeInsets(top: 12, leading: 35, bottom: 12, trailing: 35))
.foregroundColor(.white)
.background(Color.blue)
.cornerRadius(15)
})
Text("Don't have an account yet ?").padding(EdgeInsets(top: 20, leading: 0, bottom: 10, trailing: 0))
Button(action: {
guard !email.isEmpty, !password.isEmpty else {
return
}
viewModel.signUp(email: email, password: password)
}, label: {
Text("Create an account")
.padding(EdgeInsets(top: 12, leading: 35, bottom: 12, trailing: 35))
.foregroundColor(.white)
.background(Color.blue)
.cornerRadius(15)
})
}
}.navigationTitle("Login")
.navigationDestination(isPresented: $viewModel.isSignedIn) {
HomePage()
}
}
}
}
Notice how I replaced the NavigationView with a NavigationStack and added .navigationDestination(isPresented:) that listens for changes on my model's isSignedIn published property.
switch statement in swiftui can handle that easily. I use it all the time;
//Added code
enum LoginStages {
case preLogin, postLogin
}
class LoginViewModel: ObservableObject {
let auth = Auth.auth()
var userInfo: User?
#Published var loginStage: LoginStages = LoginStages.preLogin
//Added code
#Published var isSignedIn = false
func signIn(email: String, password: String) {
auth.signIn(withEmail: email, password: password) { _, error in
if let error = error as? NSError {
print("Error happened on login"+error.description)
} else {
print("Login successful")
if let user = self.auth.currentUser {
print("We have a user")
self.userInfo = user
self.isSignedIn.toggle()
loginStage = .postLogin //Added code
}
}
}
}
}
And your view should look like this;
struct LoginPage: View {
#State var email = ""
#State var password = ""
#ObservedObject var viewModel = LoginViewModel()
var body: some View {
switch viewModel.loginStage {
case .preLogin:
//This is ur initial code which the user sees before login.
// the same as the code you have above.
case .postLogin:
//Here, just call the post login view after a successful login;
// I am assuming it is called home view for this example.
HomeView()
}
}
}

Swiftui items get duplicated in all views when added to a single custom view

I'm struggling with the following issue: I'm trying to build a very simple app that lets you add items in a dedicated view that can be collapsed. I managed to write a simple function that lets me add multiple of these custom collapsable views. It's my first app so I wanted to follow the MVVM protocol. I think I got confused along the way because now every item I add gets automatically added to all the custom collapsable views I made. Is there any way to fix this? I thought using the UUID would solve this issue.. I'm guessing that I have to customise the "saveButtonPressed" function, but I don't know how to tell it to only add the item to the view where I pressed the "plus" button..
Here are the Models for the individual items and the collapsable view:
struct ItemModel: Identifiable, Equatable {
let id: String
let title: String
init(id: String = UUID().uuidString, title: String) {
self.id = id
self.title = title
}
func updateCompletion() -> ItemModel {
return ItemModel(id: id, title: title)
}
}
--
import Foundation
struct CollapsableItem: Equatable, Identifiable, Hashable {
let id: String
var title: String
init(id: String = UUID().uuidString, title: String) {
self.id = id
self.title = title
}
func updateCompletion() -> CollapsableItem {
return CollapsableItem(id: id, title: title)
}
}
These are my two ViewModels:
class ListViewModel: ObservableObject {
#Published var items: [ItemModel] = []
init() {
getItems()
}
func getItems() {
let newItems = [
ItemModel(title: "List Item1"),
ItemModel(title: "List Item2"),
ItemModel(title: "List Item3"),
]
items.append(contentsOf: newItems)
}
func addItem(title: String) {
let newItem = ItemModel(title: title)
items.append(newItem)
}
func updateItem(item: ItemModel) {
if let index = items.firstIndex(where: { $0.id == item.id}) {
items[index] = item.updateCompletion()
}
}
}
--
class CollapsedViewModel: ObservableObject {
#Published var collapsableItems: [CollapsableItem] = []
#Published var id = UUID().uuidString
init() {
getCollapsableItems()
}
func getCollapsableItems() {
let newCollapsableItems = [
CollapsableItem(title: "Wake up")
]
collapsableItems.append(contentsOf: newCollapsableItems)
}
func addCollapsableItem(title: String) {
let newCollapsableItem = CollapsableItem(title: title)
collapsableItems.append(newCollapsableItem)
}
func updateCollapsableItem(collapsableItem: CollapsableItem) {
if let index = collapsableItems.firstIndex(where: { $0.id ==
collapsableItem.id}) {
collapsableItems[index] =
collapsableItem.updateCompletion()
}
}
}
The item view:
struct ListRowView: View {
#EnvironmentObject var listViewModel: ListViewModel
let item: ItemModel
var body: some View {
HStack() {
Text(item.title)
.font(.body)
.fontWeight(.bold)
.foregroundColor(.white)
.multilineTextAlignment(.center)
.lineLimit(1)
.frame(width: 232, height: 16)
}
.padding( )
.frame(width: 396, height: 56)
.background(.gray)
.cornerRadius(12.0)
}
}
The collapsable view:
struct CollapsedView2<Content: View>: View {
#State var collapsableItem: CollapsableItem
#EnvironmentObject var collapsedViewModel: CollapsedViewModel
#State private var collapsed: Bool = true
#EnvironmentObject var listViewModel: ListViewModel
#State var label: () -> Text
#State var content: () -> Content
#State private var show = true
var body: some View {
ZStack{
VStack {
HStack{
Button(
action: { self.collapsed.toggle() },
label: {
HStack() {
Text("Hello")
.font(.title2.bold())
Spacer()
Image(systemName: self.collapsed ? "chevron.down" :
"chevron.up")
}
.padding(.bottom, 1)
.background(Color.white.opacity(0.01))
}
)
.buttonStyle(PlainButtonStyle())
Button(action: saveButtonPressed, label: {
Image(systemName: "plus")
.font(.title2)
.foregroundColor(.white)
})
}
VStack {
self.content()
}
ForEach(listViewModel.items) { item in ListRowView (item: item)
}
.lineLimit(1)
.fixedSize(horizontal: true, vertical: true)
.frame(minWidth: 396, maxWidth: 396, minHeight: 0, maxHeight: collapsed ?
0 : .none)
.animation(.easeInOut(duration: 1.0), value: show)
.clipped()
.transition(.slide)
}
}
}
func saveButtonPressed() {
listViewModel.addItem(title: "Hello")
}
}
and finally the main view:
struct ListView: View {
#EnvironmentObject var listViewModel: ListViewModel
#EnvironmentObject var collapsedViewModel: CollapsedViewModel
var body: some View {
ZStack{
ScrollView{
VStack{
HStack{
Text("MyFirstApp")
.font(.largeTitle.bold())
Button(action: newCollapsablePressed, label: {
Image(systemName: "plus")
.font(.title2)
.foregroundColor(.white)
})
}
.padding()
.padding(.leading)
ForEach(collapsedViewModel.collapsableItems) { collapsableItem in
CollapsedView2 (collapsableItem: collapsableItem,
label: { Text("") .font(.title2.bold()) },
content: {
HStack {
Text("")
Spacer() }
.frame(maxWidth: .infinity)
})
}
.padding()
}
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
.statusBar(hidden: false)
.navigationBarHidden(true)
}
}
func newCollapsablePressed() {
collapsedViewModel.addCollapsableItem(title: "hello2")
}
}
Would love to understand how I could fix this!
There is the anwser for your comment about add item in each CollapsedView2.
Because ListViewModel is not ObservableObject (ListViewModel is difference from each CollapsableItem). You should use "#State var items: [ItemModel]".
struct CollapsedView2<Content: View>: View {
#State var collapsableItem: CollapsableItem
// #State var listViewModel = ListViewModel()
#State var collapsed: Bool = true
#State var label: () -> Text
#State var content: () -> Content
#State private var show = true
#State var items: [ItemModel] = []
#State var count = 1
var body: some View {
VStack {
HStack{
Text("Hello")
.font(.title2.bold())
Spacer()
Button( action: { self.collapsed.toggle() },
label: {
Image(systemName: self.collapsed ? "chevron.down" : "chevron.up")
}
)
.buttonStyle(PlainButtonStyle())
Button(action: saveButtonPressed, label: {
Image(systemName: "plus")
.font(.title2)
// .foregroundColor(.white)
})
}
VStack {
self.content()
}
ForEach(items) { item in
ListRowView (item: item)
}
.lineLimit(1)
.fixedSize(horizontal: true, vertical: true)
.frame(minHeight: 0, maxHeight: collapsed ? 0 : .none)
.animation(.easeInOut(duration: 1.0), value: show)
.clipped()
.transition(.slide)
}
}
func saveButtonPressed() {
addItem(title: "Hello \(count)")
count += 1
}
func addItem(title: String) {
let newItem = ItemModel(title: title)
items.append(newItem)
}
func updateItem(item: ItemModel) {
if let index = items.firstIndex(where: { $0.id == item.id}) {
items[index] = item.updateCompletion()
}
}
}
There is the anwser. Ask me if you have some questions
struct ListView: View {
#StateObject var collapsedViewModel = CollapsedViewModel()
var body: some View {
ScrollView{
VStack{
HStack{
Text("MyFirstApp")
.font(.largeTitle.bold())
Button(action: newCollapsablePressed, label: {
Image(systemName: "plus")
.font(.title2)
// .foregroundColor(.white)
})
}
ForEach(collapsedViewModel.collapsableItems) { collapsableItem in
CollapsedView2 (collapsableItem: collapsableItem,
label: { Text("") .font(.title2.bold()) },
content: {
HStack {
Text("")
Spacer()
}
})
}
}
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
.statusBar(hidden: false)
.navigationBarHidden(true)
}
func newCollapsablePressed() {
collapsedViewModel.addCollapsableItem(title: "hello2")
}
}
struct CollapsedView2<Content: View>: View {
#State var collapsableItem: CollapsableItem
#State var listViewModel = ListViewModel()
#State var collapsed: Bool = true
#State var label: () -> Text
#State var content: () -> Content
#State private var show = true
var body: some View {
VStack {
HStack{
Button( action: { self.collapsed.toggle() },
label: {
HStack() {
Text("Hello")
.font(.title2.bold())
Spacer()
Image(systemName: self.collapsed ? "chevron.down" : "chevron.up")
}
.padding(.bottom, 1)
.background(Color.white.opacity(0.01))
}
)
.buttonStyle(PlainButtonStyle())
Button(action: saveButtonPressed, label: {
Image(systemName: "plus")
.font(.title2)
.foregroundColor(.white)
})
}
VStack {
self.content()
}
ForEach(listViewModel.items) { item in
ListRowView (item: item)
}
.lineLimit(1)
.fixedSize(horizontal: true, vertical: true)
.frame(minHeight: 0, maxHeight: collapsed ? 0 : .none)
.animation(.easeInOut(duration: 1.0), value: show)
.clipped()
.transition(.slide)
}
}
func saveButtonPressed() {
listViewModel.addItem(title: "Hello")
}
}
struct ListRowView: View {
let item: ItemModel
var body: some View {
HStack() {
Text(item.title)
.font(.body)
.fontWeight(.bold)
.foregroundColor(.white)
.multilineTextAlignment(.center)
.lineLimit(1)
.frame(width: 232, height: 16)
}
.padding( )
.frame(width: 396, height: 56)
.background(.gray)
.cornerRadius(12.0)
}
}
class ListViewModel {
var items: [ItemModel] = []
init() {
getItems()
}
func getItems() {
let newItems = [
ItemModel(title: "List Item1"),
ItemModel(title: "List Item2"),
ItemModel(title: "List Item3"),
]
items.append(contentsOf: newItems)
}
func addItem(title: String) {
let newItem = ItemModel(title: title)
items.append(newItem)
}
func updateItem(item: ItemModel) {
if let index = items.firstIndex(where: { $0.id == item.id}) {
items[index] = item.updateCompletion()
}
}
}
class CollapsedViewModel: ObservableObject {
#Published var collapsableItems: [CollapsableItem] = []
#Published var id = UUID().uuidString
init() {
getCollapsableItems()
}
func getCollapsableItems() {
let newCollapsableItems = [
CollapsableItem(title: "Wake up")
]
collapsableItems.append(contentsOf: newCollapsableItems)
}
func addCollapsableItem(title: String) {
let newCollapsableItem = CollapsableItem(title: title)
collapsableItems.append(newCollapsableItem)
}
func updateCollapsableItem(collapsableItem: CollapsableItem) {
if let index = collapsableItems.firstIndex(where: { $0.id ==
collapsableItem.id}) {
collapsableItems[index] =
collapsableItem.updateCompletion()
}
}
}
struct CollapsableItem: Equatable, Identifiable, Hashable {
let id: String
var title: String
init(id: String = UUID().uuidString, title: String) {
self.id = id
self.title = title
}
func updateCompletion() -> CollapsableItem {
return CollapsableItem(id: id, title: title)
}
}
struct ItemModel: Identifiable, Equatable {
let id: String
let title: String
init(id: String = UUID().uuidString, title: String) {
self.id = id
self.title = title
}
func updateCompletion() -> ItemModel {
return ItemModel(id: id, title: title)
}
}

I'm having an issue changing Views in SwiftUI using an if/else statement for a login view

I'm having trouble changing views when a username and password is successfully entered. I know the username and password works because the print statement is executed. Its a basic login type form.
import SwiftUI
struct LoginView: View {
#ObservedObject var networkManager: NetworkManager = NetworkManager()
#State var username: String = ""
#State var password: String = ""
var body: some View {
NavigationView {
VStack {
Image("dashlogovert")
.resizable()
.scaledToFit()
.frame(width: 280.0, height: 280.0, alignment: .top)
Form {
TextField("Username", text: $username)
SecureField("Password", text: $password)
Button(action: {
self.AttempLogin()
})
{
Text("Login")
.fontWeight(.bold)
.frame(width: 300, height: 30, alignment: .center)
.font(.title)
.padding()
.background(Color(red: 132/255, green: 203/255, blue: 161/255))
.foregroundColor(.white)
}
}
}
.navigationBarTitle("Login")
}
}
func AttempLogin(){
self.networkManager.loginFunction(username: self.username, password: self.password){
if self.networkManager.loggedIn {
print("You are logging in");
Dashboard()
} else {
print("You aren't logging in");
FailedLogin()
}
}
}
}
struct LoginView_Previews: PreviewProvider {
static var previews: some View {
LoginView()
}
}
Dashboard() is the new view I will take the user to when they successful logon, and fail is also a view.
Also what is the best way to do this if I have done it a really bad way.
You can pass back your logged in state from loginFunction() using #escaping closure if it’s asynchronous task, and once you have that state you can pass it back to your view body by using closure again as a parameter in AttemptLogin() function, and assign that new state value to #State property wrapper, which will call body refresh and check for updated state.
Check below code-:
import SwiftUI
enum LoginState:String {
case failed
case success
case unknownState
}
class NetworkManager:ObservableObject{
func loginFunction(username:String,password:String,closure: #escaping (String)->()){
closure(LoginState.failed.rawValue)
}
}
struct LoginView: View {
#ObservedObject var networkManager: NetworkManager = NetworkManager()
#State var username: String = ""
#State var password: String = ""
#State var isLoggedIn:String = LoginState.unknownState.rawValue
var body: some View {
NavigationView {
VStack {
Image("dashlogovert")
.resizable()
.scaledToFit()
.frame(width: 280.0, height: 280.0, alignment: .top)
Form {
TextField("Username", text: $username)
SecureField("Password", text: $password)
Button(action: {
self.AttempLogin { state in
isLoggedIn = state
}
})
{
Text("Login")
.fontWeight(.bold)
.frame(width: 300, height: 30, alignment: .center)
.font(.title)
.padding()
.background(Color(red: 132/255, green: 203/255, blue: 161/255))
.foregroundColor(.white)
}
if isLoggedIn == LoginState.success.rawValue{
Dashboard()
}
if isLoggedIn == LoginState.failed.rawValue{
FailedLogin()
}
}
}
.navigationBarTitle("Login")
}
}
func AttempLogin(_ closure: #escaping (String)->()){
self.networkManager.loginFunction(username: self.username, password: self.password){ loginState in
if loginState == LoginState.success.rawValue {
print("You are logging in");
closure(loginState)
}
if loginState == LoginState.failed.rawValue {
print("You aren't logging in");
closure(loginState)
}
}
}
}
struct Dashboard:View{
var body: some View{
Text("Dashboard")
}
}
struct FailedLogin:View{
var body: some View{
Text("Login")
}
}
struct LoginView_Previews: PreviewProvider {
static var previews: some View {
LoginView()
}
}
I have taken NetworkManager and other classes as dummy on my side.You need to write your logic accordingly.

How to push many views into a NavigationView in SwiftUI [duplicate]

In my navigation, I want to be able to go from ContentView -> ModelListView -> ModelEditView OR ModelAddView.
Got this working, my issue now being that when I hit the Back button from ModelAddView, the intermediate view is omitted and it pops back to ContentView; a behaviour that
ModelEditView does not have.
There's a reason for that I guess – how can I get back to ModelListView when dismissing ModelAddView?
Here's the code:
import SwiftUI
struct ContentView: View {
var body: some View {
NavigationView {
List{
NavigationLink(
destination: ModelListView(),
label: {
Text("1. Model")
})
Text("2. Model")
Text("3. Model")
}
.padding()
.navigationTitle("Test App")
}
}
}
struct ModelListView: View {
#State var modelViewModel = ModelViewModel()
var body: some View {
List(modelViewModel.modelValues.indices) { index in
NavigationLink(
destination: ModelEditView(model: $modelViewModel.modelValues[index]),
label: {
Text(modelViewModel.modelValues[index].titel)
})
}
.navigationBarTitleDisplayMode(.inline)
.navigationBarItems(
trailing:
NavigationLink(
destination: ModelAddView(modelViewModel: $modelViewModel), label: {
Image(systemName: "plus")
})
)
}
}
struct ModelEditView: View {
#Binding var model: Model
var body: some View {
TextField("Titel", text: $model.titel)
}
}
struct ModelAddView: View {
#Binding var modelViewModel: ModelViewModel
#State var model = Model(id: UUID(), titel: "")
var body: some View {
TextField("Titel", text: $model.titel)
}
}
struct ModelViewModel {
var modelValues: [Model]
init() {
self.modelValues = [ //mock data
Model(id: UUID(), titel: "Foo"),
Model(id: UUID(), titel: "Bar"),
Model(id: UUID(), titel: "Buzz")
]
}
}
struct Model: Identifiable, Equatable {
let id: UUID
var titel: String
}
Currently placing a NavigationLink in the .navigationBarItems may cause some issues.
A possible solution is to move the NavigationLink to the view body and only toggle a variable in the navigation bar button:
struct ModelListView: View {
#State var modelViewModel = ModelViewModel()
#State var isAddLinkActive = false // add a `#State` variable
var body: some View {
List(modelViewModel.modelValues.indices) { index in
NavigationLink(
destination: ModelEditView(model: $modelViewModel.modelValues[index]),
label: {
Text(modelViewModel.modelValues[index].titel)
}
)
}
.background( // move the `NavigationLink` to the `body`
NavigationLink(destination: ModelAddView(modelViewModel: $modelViewModel), isActive: $isAddLinkActive) {
EmptyView()
}
.hidden()
)
.navigationBarTitleDisplayMode(.inline)
.navigationBarItems(trailing: trailingButton)
}
// use a Button to activate the `NavigationLink`
var trailingButton: some View {
Button(action: {
self.isAddLinkActive = true
}) {
Image(systemName: "plus")
}
}
}

Resources