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
Turn for in Loops Local Variables into Mutable Variables
Swiftui Out of Index When Deleting an Array Element in Foreach
How to Make a Button Have a Rounded Border in Swift
Converting Url to String and Back Again
Default Optional Parameter in Swift Function
A Switch Bug in Swift? - "Switch Must Be Exhaustive, Consider Adding a Default Clause."
Firebase Uid VS Document-Id and Firestore Rules
How to Declare Swift Implicitly Unwrapped Optional as a Constant
Swift: Providing a Default Protocol Implementation in a Protocol Extension
Uitextview Font to Always Be Fixed Size
How to Use a Variadic Closure in Swift
Programatic Constraints Not Obeyed
Swift 3 Optional Trouble. Can't Unwrap Url with Passed in String
How to Implement iOServicematchingcallback in Swift