简体   繁体   中英

Prevent SwiftUI from resetting @State

I don't often understand when SwiftUI resets the state of a view (ie all that is marked with @State). For example, take a look at this minimum example:

import SwiftUI

struct ContentView: View {
    @State private var isView1Active = true
    let view1 = View1()
    let view2 = View2()

    var body: some View {
        VStack {
            if isView1Active {
                view1
            } else {
                view2
            }

            Button(action: {
                self.isView1Active.toggle()
            }, label: {
                Text("TAP")
            })
        }
    }
}

struct View1: View {
    @State private var text = ""
    var body: some View {
        TextField("View1: type something...", text: $text)
    }
}

struct View2: View {
    @State private var text = ""
    var body: some View {
        TextField("View2: type something...", text: $text)
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

I'd want the two TextField to keep their content, but if you run this example some weird behaviours occur:

  • If you run the example on the preview only the View1 TextField content persists:

在此处输入图片说明

  • If you, instead, run the example on the simulator (or on an actual device ) neither the first textfield content, nor the second one persist:

在此处输入图片说明

So, what's happening here? Is there a way to tell SwiftUI not to reset @State for a view? Thanks.

The issue is that View1 and View2 are being recreated every time isView1Active is changed (because it is using @State which reloads the body of ContentView ).

A solution would be to keep the text properties of the TextField s in the ContentView as shown below and use @Binding :

struct ContentView: View {

    @State private var isView1Active = true
    @State private var view1Text = ""
    @State private var view2Text = ""

    var body: some View {
        VStack {
            if isView1Active {
                View1(text: $view1Text)
            } else {
                View2(text: $view2Text)
            }

            Button(action: {
                self.isView1Active.toggle()
            }, label: {
                Text("TAP")
            })
        }
    }
}

struct View1: View {

    @Binding var text: String

    var body: some View {
        TextField("View1: type something...", text: $text)
    }
}

struct View2: View {

    @Binding var text: String

    var body: some View {
        TextField("View2: type something...", text: $text)
    }
}

Shown in action:

动图

It view1 and view2 are completely independent and enclosure, like there is no contextmenu or sheet , you may use ZStack and opacity combinations.

 var body: some View {
    VStack {
        ZStack{
        if isView1Active {
            view1.opacity(1)
             view2.opacity(0)
        } else {
            view1.opacity(0)
            view2.opacity(1)

        }}

        Button(action: {
            self.isView1Active.toggle()
        }, label: {
            Text("TAP")
        })
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM