Swiftui Change View with Button

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

Update View on button click SwiftUI

model is not declared as StateObject, so any change in it will not be reflected in the UI.

EDIT : there is a logic problem in your code :

  • model is not used as a var but as a link to static class properties
  • the theme is choosen via static properties from MemoryGameClass

As you use static properties of a class, the values are only used when something that use this properties is updated, not when the property is updated.

The theme should be a published var of EmojisMemoryGame and declared as a struct. So when something changes in properties of the theme, it will cause a redraw of the view.

Edit 2 : what I meant :
Theme as a struct :

struct MemoryGameTheme {
// List of themes
static let allThemes: [ChoiseTheme: MemoryGameTheme] =
[.car: MemoryGameTheme(choiseTheme: .car, themeEmojis: ["quot;, "quot;, "quot;, "quot;, "quot;, "quot;, "quot;, "quot;, "quot;, "quot;, "quot;, "quot;, "quot;, "quot;, "quot;, "quot;, "quot;, "✈️", "quot;, "quot;, "quot;, "quot;, "⛵️", "quot;], themeName: "Cars", colorCard: .red),
.animal: MemoryGameTheme(choiseTheme: .animal, themeEmojis: ["quot;, "quot;, "quot;, "quot;, "quot;, "quot;, "quot;, "quot;, "quot;, "quot;, "quot;, "quot;, "quot;, "‍❄️", "quot;, "quot;, "quot;], themeName: "Animals", colorCard: .gray),
.item: MemoryGameTheme(choiseTheme: .item, themeEmojis: ["quot;, "⌚️", "quot;, "⌨️", "quot;, "quot;, "quot;, "quot;, "quot;, "quot;, "☎️", "quot;, "⏰", "quot;, "quot;, "quot;, "quot;], themeName: "Items", colorCard: .purple),
.food: MemoryGameTheme(choiseTheme: .food, themeEmojis: ["quot;, "quot;, "quot;, "quot;, "quot;, "quot;, "quot;, "quot;, "quot;, "quot;, "quot;, "quot;, "quot;, "quot;, "quot;, "quot;, "quot;], themeName: "Food", colorCard: .yellow),
.face: MemoryGameTheme(choiseTheme: .face, themeEmojis: ["quot;, "quot;, "quot;, "quot;, "quot;, "quot;, "quot;, "quot;, "quot;, "quot;, "quot;, "quot;, "quot;, "quot;, "☺️", "quot;, "quot;, "quot;], themeName: "Face", colorCard: .green)]

// Type of themes
enum ChoiseTheme: CaseIterable {
case car
case animal
case item
case food
case face
}
var choiseTheme: ChoiseTheme = .animal
var themeEmojis = ["quot;, "quot;, "quot;, "quot;, "quot;, "quot;, "quot;, "quot;, "quot;, "quot;, "quot;, "quot;, "quot;, "‍❄️", "quot;, "quot;, "quot;]
var themeName = "Animals"
var colorCard = Color.gray

mutating func changeTheme(_ choiseTheme: ChoiseTheme) {
let randomTheme = ChoiseTheme.allCases.randomElement()!
// Initialise the theme from static dictionnary
let theme = Self.allThemes[randomTheme]!
self.choiseTheme = theme.choiseTheme
self.themeEmojis = theme.themeEmojis
self.themeName = theme.themeName
self.colorCard = theme.colorCard

print(randomTheme)
print(choiseTheme)
}

// Mutating func to change theme
mutating func refreshTheme() {
changeTheme(ChoiseTheme.car)
}
}

EmojiGame including theme as Published var :

class EmojiMemoryGame: ObservableObject {
// Create a new memory game based on specific theme
static func createMemoreGame(theme: MemoryGameTheme) -> MemoryGame<String> {
return MemoryGame<String>(numberOfPairOfCards: theme.themeEmojis.count) { pairIndex in
theme.themeEmojis[pairIndex]
}
}

// This model is a struct
@Published private var model: MemoryGame<String> = EmojiMemoryGame.createMemoreGame(theme: MemoryGameTheme())
// This is the theme of the model
@Published private var theme: MemoryGameTheme = MemoryGameTheme()

// To acces model and theme comtents
var themeName: String {
theme.themeName
}
var colorCard: Color {
theme.colorCard
}

var cards: Array<MemoryGame<String>.Card> {
model.cards
}

// MARK: - Intens(s)
func choose(_ card: MemoryGame<String>.Card) {
model.choose(card)
}

// When refreshing theme, create a new game with teh new theme
func refreshTheme() {
theme.refreshTheme()
model = Self.createMemoreGame(theme: theme)
}
}

The Content view using ObservedObject Emoji game :

struct ContentView: View {
// emoji memory game must declared as Observed object
// to enable SwiftUI to update when something change in it
@ObservedObject var emojisGame: EmojiMemoryGame
var body: some View {
VStack {
ScrollView {
LazyVGrid(columns: [GridItem(.adaptive(minimum: 75))]) {
ForEach (emojisGame.cards) { card in
CardView(card: card)
.aspectRatio(2/3, contentMode: .fit)
.onTapGesture {
emojisGame.choose(card)
}
}
}
}
// Use theme color for cards
.foregroundColor(emojisGame.colorCard)
Spacer()
HStack {
newGame
Spacer()
nameTheme
}
}
.padding(.horizontal)
}

var newGame: some View {
Button(action: {
// Reset emoji game with theme change
emojisGame.refreshTheme()
self.onActivate()
}, label: {
Text("New Game")
.font(.title)
.fontWeight(.heavy)
})
}
var nameTheme: some View {
Text ("\(emojisGame.themeName)")
.font(.title3)
.fontWeight(.heavy)
}

func onActivate() {

}

}

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

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: Button inside function doesn't update view

You have @State defined inside a member function. Instead, it should be defined at the top level of your View -- that's the only way it will retain/update state. The smallest working change would be this:

struct TestView: View {
@State var testBool: Bool? //<-- Here

var body: some View {
buttonView()
}

func buttonView() -> some View {
Button(action: {
testBool = Bool.random()
}, label: {
VStack {
Text("click me")
Text(testBool == nil ? "nil" : String(testBool!))
}
})
}
}

A further refactor to get it to look a little Swift-ier might be:

struct TestView: View {
@State var testBool: Bool?

var body: some View {
Button(action: {
testBool = Bool.random()
}) {
VStack {
Text("click me")
if let testBool = testBool {
Text(testBool ? "true" : "false")
} else {
Text("nil")
}
}
}
}
}


Related Topics



Leave a reply



Submit