Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SwiftUI: prevent View from refreshing when presenting a sheet

Tags:

ios

swift

swiftui

I have noticed that SwiftUI completely refresh view when adding sheetmodifier.

Let's say I have View that displays random number. I expect that this value would be independent and not connected to the sheet logic (not changing every time I open/close sheet), but every time sheet presented/dismissed Text is changing.

Is it supposed to work so? Am I wrong that main point of @Sateis to update only connected Views but not all stack? How can I prevent my View from refreshing itself when presenting a modal?

struct ContentView: View {

    @State var active = false

    var body: some View {
        VStack {
            Text("Random text: \(Int.random(in: 0...100))")

            Button(action: { self.active.toggle() }) {
                Text("Show pop up")
            }
        }
        .sheet(isPresented: $active) {
            Text("POP UP")
        }
    }
}

P.S. ContentView calls onAppear()/onDisappear() and init() only ones.

like image 942
Dmytro Pashkov Avatar asked Feb 07 '20 10:02

Dmytro Pashkov


2 Answers

It needs to make separated condition-independent view to achieve behavior as you wish, like below

struct RandomView: View {
    var body: some View {
        Text("Random text: \(Int.random(in: 0...100))")
    }
}

struct ContentView: View {

    @State var active = false

    var body: some View {
        VStack {
            RandomView()

            Button(action: { self.active.toggle() }) {
                Text("Show pop up")
            }
        }
        .sheet(isPresented: $active) {
            Text("POP UP")
        }
    }
}

In this case RandomView is not rebuilt because is not dependent on active state.

like image 166
Asperi Avatar answered Oct 09 '22 05:10

Asperi


Asperi sad :

View is struct, value type, if any part of it changed then entire value changed

He is absolutely right! But for that we have state properties. When the view is recreated, the value of state doesn't change.

This should work, as you expected

struct ContentView: View {

    @State var active = false
    @State var number = Int.random(in: 0 ... 100)
    var body: some View {
        VStack {
            Text("Random text: \(number)")

            Button(action: { self.active.toggle() }) {
                Text("Show pop up")
            }
        }
        .sheet(isPresented: $active) {
            Text("POP UP")
        }
    }
}

What is the advantage? For simple things, the state / binding is the best solution, without any doubt.

import SwiftUI

struct SheetView: View {
    @Binding var randomnumber: Int
    var body: some View {
        Button(action: {
            self.randomnumber = Int.random(in: 0 ... 100)
        }) {
            Text("Generate new random number")
        }
    }
}
struct ContentView: View {

    @State var active = false
    @State var number = Int.random(in: 0 ... 100)
    var body: some View {
        VStack {
            Text("Random text: \(number)")

            Button(action: { self.active.toggle() }) {
                Text("Show pop up")
            }
        }
        .sheet(isPresented: $active) {
            SheetView(randomnumber: self.$number)
        }
    }
}
struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

Now you can dismiss the sheet with or without generating new random number. No external model is required ...

like image 5
user3441734 Avatar answered Oct 09 '22 04:10

user3441734