Go to a new view using SwiftUI
The key is to use a NavigationView and a NavigationLink:
import SwiftUI
struct ContentView : View {
var body: some View {
NavigationView {
VStack {
Text("Hello World")
NavigationLink(destination: DetailView()) {
Text("Do Something")
}
}
}
}
}
SwiftUI - Opening a new view on button click
A NavigationLink
must be in view hierarchy, so instead of putting it in action we need to put some model there.
A sketch of possible approach
- destination model
enum MenuDestination: String, CaseIterable, Hashable {
case set1(MenuItem), set2
@ViewBuilder var view: some View {
switch self {
case .set1(let item): View1(item: item)
case .set2: SettingsView()
}
}
}
- navigation link in view
@State private var selection: MenuDestination?
var isActive: Binding<Bool> {
Binding(get: { selection != nil }, set: { selection = nil } )
}
var body: some View {
NavigationView {
ScrollView {
// ...
}
.background(
if let selection = selection {
NavigationLink(isActive: isActive, destination: { selection.view }) {
EmptyView()
}})
}
}
- button action assigns corresponding value, say
MenuItemAction
take as argument binding to selection and internally assign destination to that binding
MenuItemCircularGridView(imageName: item.imageName, menuItemName: item.name,
action: (menuOptionsAction.menuActions.first {$0.id == item.id})?.action($selection) ?? { _ in })
and MenuItemAction
inited with case of corresponding MenuDestination
See also this post
Trying to move to a new view in swiftUI after a button press
to make the "move work", add this to "AddAssignment":
struct AddAssignment: View {
@State var toAssignment: Int? = nil
replace your Button("Create New Task", action: {...}, with
NavigationLink(destination: ViewAssignment(), tag: 1, selection: $toAssignment) {
Button("Create New Task") {
let task: [String] = [taskName, dueDate, subject, weighting, totalMarks]
print(task)
self.toAssignment = 1
}
}
make sure you wrap the whole thing in "NavigationView { ...}"
How To Switch To A New View On Simple Button Click?
The best way would be to use a NavigationLink
and wrapping the content inside a NavigationView
.
The NavigationView
is used to represent the view hierarchy.
For instance:
// Wrapper for LogIn
struct ContentView: View {
var body: some View {
NavigationView { LogIn() }
}
}
// LogIn
struct LogIn: View {
var body: some View {
VStack {
// Fields for log in
NavigationLink(destination: SignUp()) {
Text("Create New Account")
.foregroundColor(.white)
.font(.title)
.frame(maxWidth: .infinity, alignment: .center)
.background(Color(red: 0.22, green: 0.655, blue: 0.02))
.cornerRadius(8)
.padding(.horizontal, metrics.size.width*0.10)
}
}
}
}
You can find more info in the official docs:
- NavigationView
- NavigationLink
Also [Hacking With Swift] (https://www.hackingwithswift.com/quick-start/swiftui/displaying-a-detail-screen-with-navigationlink) is a great resource for SwiftUI
Programmatically navigate to new view in SwiftUI
You can replace the next view with your login view after a successful login. For example:
struct LoginView: View {
var body: some View {
...
}
}
struct NextView: View {
var body: some View {
...
}
}
// Your starting view
struct ContentView: View {
@EnvironmentObject var userAuth: UserAuth
var body: some View {
if !userAuth.isLoggedin {
LoginView()
} else {
NextView()
}
}
}
You should handle your login process in your data model and use bindings such as @EnvironmentObject
to pass isLoggedin
to your view.
Note: In Xcode Version 11.0 beta 4, to conform to protocol 'BindableObject' the willChange property has to be added
import Combine
class UserAuth: ObservableObject {
let didChange = PassthroughSubject<UserAuth,Never>()
// required to conform to protocol 'ObservableObject'
let willChange = PassthroughSubject<UserAuth,Never>()
func login() {
// login request... on success:
self.isLoggedin = true
}
var isLoggedin = false {
didSet {
didChange.send(self)
}
// willSet {
// willChange.send(self)
// }
}
}
Show a new View from Button press Swift UI
For simple example you can use something like below
import SwiftUI
struct ExampleFlag : View {
@State var flag = true
var body: some View {
ZStack {
if flag {
ExampleView().tapAction {
self.flag.toggle()
}
} else {
OtherExampleView().tapAction {
self.flag.toggle()
}
}
}
}
}
struct ExampleView: View {
var body: some View {
Text("some text")
}
}
struct OtherExampleView: View {
var body: some View {
Text("other text")
}
}
but if you want to present more view this way looks nasty
You can use stack to control view state without NavigationView
For Example:
class NavigationStack: BindableObject {
let didChange = PassthroughSubject<Void, Never>()
var list: [AuthState] = []
public func push(state: AuthState) {
list.append(state)
didChange.send()
}
public func pop() {
list.removeLast()
didChange.send()
}
}
enum AuthState {
case mainScreenState
case userNameScreen
case logginScreen
case emailScreen
case passwordScreen
}
struct NavigationRoot : View {
@EnvironmentObject var state: NavigationStack
@State private var aligment = Alignment.leading
fileprivate func CurrentView() -> some View {
switch state.list.last {
case .mainScreenState:
return AnyView(GalleryState())
case .none:
return AnyView(LoginScreen().environmentObject(state))
default:
return AnyView(AuthenticationView().environmentObject(state))
}
}
var body: some View {
GeometryReader { geometry in
self.CurrentView()
.background(Image("background")
.animation(.fluidSpring())
.edgesIgnoringSafeArea(.all)
.frame(width: geometry.size.width, height: geometry.size.height,
alignment: self.aligment))
.edgesIgnoringSafeArea(.all)
.onAppear {
withAnimation() {
switch self.state.list.last {
case .none:
self.aligment = Alignment.leading
case .passwordScreen:
self.aligment = Alignment.trailing
default:
self.aligment = Alignment.center
}
}
}
}
.background(Color.black)
}
}
struct ExampleOfAddingNewView: View {
@EnvironmentObject var state: NavigationStack
var body: some View {
VStack {
Button(action:{ self.state.push(state: .emailScreen) }){
Text("Tap me")
}
}
}
}
struct ExampleOfRemovingView: View {
@EnvironmentObject var state: NavigationStack
var body: some View {
VStack {
Button(action:{ self.state.pop() }){
Text("Tap me")
}
}
}
}
In my opinion this bad way, but navigation in SwiftUI much worse
iOS SwiftUI - NavigationLink open new view and close the current one
In order to do this, I would suggest skipping the NavigationView
altogether, see here for more info. An example for your situation:
//You need an `ObservedObject` to do this, and a overall holder view
enum ViewStates{
//Declare possible views
case ContentView
case Login
case CreateAccount
}
//Then use an observableObject
class viewControl: ObservableObject{
@Published var currentView: ViewStates = .ContentView
}
//Finally, pass this into your views. Take a look at the second part of the tutorial I posted below for more info
//such as using `EnvironmentObject` and adding animation. Example implimentation below:
struct ControllerView: View{
@StateObject var controller: viewControl
var body: some View{
switch controller.currentView{
case .ContentView:
ContentView(controller: controller)
case .Login:
Login(controller: controller)
case .CreateAccount:
CreateAccount(controller: controller)
}
}
}
Next, you need to have @ObservedObject var controller: viewControl
in all of your views. Note that you don't need a default statement in the switch clause, because the enum
declares all possible values. The following is an example CreateAccount view. You also no longer need the dismiss - in fact, that will no longer work.
struct CreateAccount: View{
@ObservedObject var controller: viewControl
var body: some View{
//Content
Button("Dismiss"){
controller.currentView = .ContentView
}
}
}
This will allow you to switch the view by clicking. Instead of a NavigationLink in ContentView, do this:
Button{
controller.currentView = .CreateAccount
} label: {
Text("Create Account")
}
To go back, you just set the value again. This can also be expanded to show more views.
Second part of the tutorial
Related Topics
How to Compare Two Dictionaries in Swift
Swift 3 For Loop With Increment
When Should I Access Properties With Self in Swift
How to Create Array of Unique Object List in Swift
How to Encode Enum Using Nscoder in Swift
Use Binding≪Int≫ With a Textfield Swiftui
Nsuserdefaults Not Working on Xcode Beta With Watch Os2
Ios 14 Swiftui Keyboard Lifts View Automatically
Check If 'Any' Value Is Object
Binary Operator * Cannot Be Applied to Operands of Type Int and Double
Swift Equality Operator on Nested Arrays
Swift Lazy Instantiating Using Self
Check Password String Strength Criteria in Swift
Why My Return Is Nil But If I Press the Url in Chrome/Safari, I Can Get Data
Choosing Coredata Entities from Form Picker