skip to Main Content

The intent here is generate a new random number every time MyView loads while keeping that randomly generated number unaffected with any MyView refresh. However, none of the approaches below work. I will explain each approach and its outcome. Any ideas on how to properly accomplish what I am asking?

Approach #1: I assumed a new number is generated every time MyView loads. However, a random number is generated once for the entire app lifetime. I placed a button to force a view refresh, so when I press the button a new random number should not generate, which is what happens. What is wrong with this approach?

import SwiftUI

struct ContentView: View {

    var body: some View {
    
        NavigationView {
        
            NavigationLink(destination: MyView()) {
                Text("Link to MyView")
            }
        }
    }
}

struct MyView: View {

    @State var randomInt = Int.random(in: 1...100)
    @State var myMessage: String = ""

    var body: some View {

        Text(String(randomInt))

        Button("Press Me") { 
            myMessage = String(randomInt)
        }

        Text(myMessage)
    }

}

Approach #2: I tried to update randomInt variable via let inside the Body but this generates a new random number every time the button is pressed (which is forcing a view refresh). Again, not the intended outcome. What is wrong with this approach?

import SwiftUI

struct ContentView: View {

    var body: some View {
    
        NavigationView {
        
            NavigationLink(destination: MyView()) {
                Text("Link to MyView")
            }
        }
    }
}

struct MyView: View {

    @State var randomInt = 0
    @State var myMessage: String = ""

    var body: some View {
        
        let randomInt = Int.random(in: 1...100)
        
        Text(String(randomInt))

        Button("Press Me") { 
            myMessage = String(randomInt)
        }

        Text(myMessage)

    }

}

Approach #3: The idea here to pass a new randomly generated integer every time the "Link to MyView" is pressed. I assumed that Int.Random is ran and passed every time Content View loads. However, a random number is only generated the first-time the entire app runs. What is wrong with this approach?

import SwiftUI

struct ContentView: View {

    var body: some View {
    
        NavigationView {
        
            NavigationLink(destination: MyView(randomInt: Int.random(in: 1...100))) {
                Text("Link to MyView")
            }
        }
    }
}

struct MyView: View {

    @State var randomInt = 0
    @State var myMessage: String = ""

    var body: some View {
        Text(myMessage)

        Button("Press Me") { 
            myMessage = String(randomInt)
        }
    }

}

2

Answers


  1. Approach #1:

    MyView is created once, when ContentView renders.

    Inside MyView, randomInt is set when the view is first created and then never modified. When you press the button, myMessage is set, but randomInt is never changed. If you wanted randomInt to change, you’d want to say something like:

    randomInt = Int.random(in: 1...100)
    

    inside your button action:

    Approach #2:

    You’re creating a new randomInt variable in the local scope by declaring let randomInt = inside the view body.

    Instead, if I’m reading your initial question correctly, I think you’d want something using onAppear:

    struct MyView: View {
    
        @State var randomInt = 0
        @State var myMessage: String = ""
    
        var body: some View {
            VStack {
                Text(String(randomInt))
    
                Button("Press Me") {
                    myMessage = String(randomInt)
                }
    
                Text(myMessage)
            }.onAppear {
                randomInt = Int.random(in: 1...100)
            }
        }
    }
    
    

    You’ll see that with this, every time you go back in the navigation hierarchy and then revisit MyView, there’s a new value (since it appears again). The button triggering a re-render doesn’t re-trigger onAppear

    Approach #3:

    MyView gets created on the first render of the parent view (ContentView). Unless something triggers a refresh of ContentView, you wouldn’t generate a new random number here.


    In conclusion, I’m a little unclear on what the initial requirement is (what does it mean for a View to ‘load’? Does this just mean when it gets shown on the screen?), but hopefully this describes each scenario and maybe introduces the idea of onAppear, which seems like it might be what you’re looking for.


    Addendum: if you want the random number to be generated only when ContentView loads (as the title says), I’d create it in ContentView instead of your MyView and pass it as a parameter.

    
    struct ContentView: View {
        @State var randomInt = Int.random(in: 1...100)
        
        var body: some View {
            
            NavigationView {
                
                NavigationLink(destination: MyView(randomInt: $randomInt)) {
                    Text("Link to MyView")
                }
            }
        }
    }
    
    struct MyView: View {
        
        @Binding var randomInt : Int
        @State var myMessage: String = ""
        
        var body: some View {
            
            Text(String(randomInt))
            
            Button("Press me to refresh only myMessage") {
                myMessage = String(randomInt)
            }
            
            Button("Press me to change the randomInt as well") {
                randomInt = Int.random(in: 1...100)
                myMessage = String(randomInt)
            }
            
            Text(myMessage)
        }
        
    }
    
    Login or Signup to reply.
  2. I work on a way that always make a new random, your problem was you just used initialized State over and over! Without changing it.

    Maybe the most important thing that you miss understood was using a State wrapper and expecting it performance as computed property, as you can see in your 3 example all has the same issue in different name or form! So for your information computed property are not supported in SwiftUI until Now! That means our code or us should make an update to State Wrappers, and accessing them like you tried has not meaning at all to them.

    import SwiftUI
    
    struct ContentView: View {
    
        var body: some View {
        
            NavigationView {
            
                NavigationLink(destination: MyView()) {
                    Text("Link to MyView")
                }
            }
        }
    }
    
    struct MyView: View {
        
        @State private var randomInt: Int?
        
        var body: some View {
            
            if let unwrappedRandomInt: Int = randomInt { Text(unwrappedRandomInt.description) }
            
            Button("Press Me") { randomMakerFunction() }
                .onAppear() { randomMakerFunction() }
            
        }
        
        func randomMakerFunction() { randomInt = Int.random(in: 1...100) }
        
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search