Can a button call a new view without using NavigationStack/Link/View?

Is it possible to switch to a new View without using NavigationStack or NavigationLink or NavigationView?

I know I can do it with a Bool, which either shows the second view, or the first, and then toggles the Bool.

But can't I do something like this? Which obviously doesn't work.

struct BasicButton: View {
	var buttonLabel = "Create User"
	
	var body: some View {
		Button {
			
			CreateUser() //another SwiftUI view, not a function
			
		} label: {
			Text(buttonLabel)
		}
	}
}

Accepted Reply

But can't I do something like this?

No, you can't, for reasons that are a bit more philosophical than technical.

In "normal" Swift code (non-SwifUI, non-result-builder), there is an execution flow sequence over time. One statement executes after another.

In SwifUI/result-builder "code", time isn't a factor. Instead, the code describes the arrangement and relationships between things in a time-neutral sense.

When you use a button to trigger an action or change of state, you're necessarily talking about time sequence: press the button, then do something. That's why the code inside Button's closure is regular Swift code, not result-builder code.

The only way to bridge the two worlds is the solution you already know: use (e.g.) a Boolean @state property to choose between 2 views.


Note that even declarative SwiftUI code can have time-related implications. Your Button usage outside of the action closure is declarative, but clearly the button action will happen "later", so time is a factor. You just don't control the "when" directly.

  • I accept your answer, but that shouldn't be the answer. As dangerous as certain things are, i.e. calling a null pointer, we should be allowed to design as we like.

Add a Comment

Replies

But can't I do something like this?

No, you can't, for reasons that are a bit more philosophical than technical.

In "normal" Swift code (non-SwifUI, non-result-builder), there is an execution flow sequence over time. One statement executes after another.

In SwifUI/result-builder "code", time isn't a factor. Instead, the code describes the arrangement and relationships between things in a time-neutral sense.

When you use a button to trigger an action or change of state, you're necessarily talking about time sequence: press the button, then do something. That's why the code inside Button's closure is regular Swift code, not result-builder code.

The only way to bridge the two worlds is the solution you already know: use (e.g.) a Boolean @state property to choose between 2 views.


Note that even declarative SwiftUI code can have time-related implications. Your Button usage outside of the action closure is declarative, but clearly the button action will happen "later", so time is a factor. You just don't control the "when" directly.

  • I accept your answer, but that shouldn't be the answer. As dangerous as certain things are, i.e. calling a null pointer, we should be allowed to design as we like.

Add a Comment

You just don't control the "when" directly.

Yes, but we can have some control with a dispatch:

        Button {
//            CreateUser() //another SwiftUI view, not a function
            DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
                showCreate.toggle()
            }
        } label: {
            Text(buttonLabel)
        }

I'd call that "indirectly". 😄

The distinction I was trying to make is that the use of Button is itself in a view-builder (aka SwiftUI declarative code), but the inside of the Button closure is procedural code, not declarative code, so yes it absolutely has a time sequence and the ability to control timing.

Keep in mind that the OP asked about handling the timing in SwiftUI code, without toggling the state variable "when…".