简体   繁体   中英

How can I make a State wrapper outside of View in SwiftUI?

I know that State wrappers are for View and they designed for this goal, but I wanted to try build and test some code if it is possible, my goal is just for learning purpose,

I have 2 big issues with my code!

  1. Xcode is unable to find T .

  2. How can I initialize my state?

import SwiftUI

var state: State<T> where T: StringProtocol = State(get: { state }, set: { newValue in state = newValue })

struct ContentView: View {
    var body: some View {
        Text(state)
    }
}

Update: I could do samething for Binding here, Now I want do it for State as well with up code

import SwiftUI

var state2: String = String() { didSet { print(state2) } }
var binding: Binding = Binding.init(get: { state2 }, set: { newValue in state2 = newValue })


struct ContentView: View {

    var body: some View {
        
        TextField("Enter your text", text: binding)

    }
 
}

If I could find the answer of my issue then, i can define my State and Binding both outside of View, 50% of this work done and it need another 50% for State Wrapper.


New Update:

import SwiftUI

var state: State<String> = State.init(initialValue: "Hello") { didSet { print(state.wrappedValue) } }
var binding: Binding = Binding.init(get: { state.wrappedValue }, set: { newValue in state = State(wrappedValue: newValue) })


struct ContentView: View {

    var body: some View {
        
        Text(state)                                     // <<: Here is the issue!
        
        TextField("Enter your text", text: binding)

    }
 
}

在此处输入图像描述

Even if you create a State wrapper outside a view, how will the view know when to refresh its body?

Without a way to notify the view, your code will do the same as:

struct ContentView: View {
    var body: some View {
        Text("Hello")
    }
}

What you can do next depends on what you want to achieve.

If all you need is a way to replicate the State behaviour outside the view, I recommend you take a closer look at the Combine framework.

An interesting example is CurrentValueSubject :

var state = CurrentValueSubject<String, Never>("state1")

It stores the current value and also acts as a Publisher.

What will happen if we use it in a view that doesn't observe anything?

struct ContentView: View {
    var body: some View {
        Text(state.value)
            .onAppear {
                DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
                    state.value = "state2"
                }
            }
    }
}

The answer is: nothing. The view is drawn once and, even if the state changes, the view won't be re-drawn.

You need a way to notify the view about the changes. In theory you could do something like:

var state = CurrentValueSubject<String, Never>("state1")
struct ContentView: View {
    @State var internalState = ""

    var body: some View {
        Text(internalState)
            .onAppear {
                DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
                    state.value = "state2"
                }
            }
            .onReceive(state) {
                internalState = $0
            }
    }
}

But this is neither elegant nor clean. In these cases we should probably use @State :

struct ContentView: View {
    @State var state = "state1"

    var body: some View {
        Text(state)
            .onAppear {
                DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
                    state = "state2"
                }
            }
    }
}

To sum up, if you need a view to be refreshed, just use the native SwiftUI property wrappers (like @State ). And if you need to declare state values outside the view, use ObservableObject + @Published .

Otherwise there is a huge Combine framework which does exactly what you want. I recommend you take a look at these links:

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