简体   繁体   English

如何在 SwiftUI 的视图之外制作 State 包装器?

[英]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,我知道 State 包装器是为 View 设计的,它们是为这个目标而设计的,但如果可能的话,我想尝试构建和测试一些代码,我的目标只是为了学习目的,

I have 2 big issues with my code!我的代码有两个大问题!

  1. Xcode is unable to find T . Xcode 无法找到T

  2. How can I initialize my state?如何初始化我的 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更新:我可以在这里为 Binding 做同样的事情,现在我想为 State 以及 up 代码做同样的事情

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.如果我能找到我的问题的答案,我可以定义我的 State 并在视图之外进行绑定,完成了 50% 的工作,State Wrapper 还需要另外 50%。


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?即使您在视图之外创建了 State 包装器,视图如何知道何时刷新其主体?

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.如果您只需要一种在视图外复制 State 行为的方法,我建议您仔细查看 Combine 框架。

An interesting example is CurrentValueSubject :一个有趣的例子是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.视图只绘制一次,即使state发生变化,也不会重新绘制视图。

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 :在这些情况下,我们可能应该使用@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 ).总而言之,如果您需要刷新视图,只需使用本机 SwiftUI 属性包装器(如@State )。 And if you need to declare state values outside the view, use ObservableObject + @Published .如果您需要在视图外声明 state 值,请使用ObservableObject + @Published

Otherwise there is a huge Combine framework which does exactly what you want.否则,有一个巨大的组合框架可以完全满足您的需求。 I recommend you take a look at these links:我建议你看看这些链接:

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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