SwiftUI@Binding update不支持';刷新视图

SwiftUI@Binding update不支持';刷新视图,binding,swiftui,Binding,Swiftui,我觉得我缺少了一些非常基本的东西,但是这个示例SwiftUI代码在单击按钮时不会修改视图(尽管绑定更新) 我已经阅读了一些文章,认为这是使用绑定的正确方法,视图应该自动刷新 import SwiftUI struct ContentView: View { @Binding var isSelected: Bool var body: some View { Button(action: { self.isSelected.toggle

我觉得我缺少了一些非常基本的东西,但是这个示例SwiftUI代码在单击按钮时不会修改视图(尽管绑定更新)

我已经阅读了一些文章,认为这是使用绑定的正确方法,视图应该自动刷新

import SwiftUI

struct ContentView: View {
    @Binding var isSelected: Bool

    var body: some View {
        Button(action: {
            self.isSelected.toggle()
        }) {
            Text(isSelected ? "Selected" : "Not Selected")
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    @State static var selected: Bool = false

    static var previews: some View {
        ContentView(isSelected: $selected)
    }
}

您需要使用@State而不是@Binding

  • 如果UI应在其值更改时更新,则将变量指定为 @状态变量。这是真理的源泉

  • 当视图不拥有这些数据并且不是事实的来源时,可以使用@Binding而不是@State

这是您的变量:

@State var isSelected: Bool

再仔细研究一下,我想我明白发生了什么

在本例中,我希望在构建自定义控件时使用
@Binding
(比如SwiftUI的原生
切换
,它也绑定到
Bool

问题是
ContentView\u Previews
中的静态(即
@state static var selected:Bool=false
)不会在状态更改时触发预览的重新呈现,因此即使所选状态由于与控件的交互而更改,控件也会(
ContentView\u Previews
的子级)不会重新呈现自身

这使得在SwiftUI预览中单独测试控件变得很困难,但是将状态移动到虚拟
observeObject
实例中会正确运行。下面是代码:

import SwiftUI
import Combine

class SomeData: ObservableObject {
    @Published var isOn: Bool = false
}

struct MyButton: View {
    @Binding var isSelected: Bool

    var body: some View {
        Button(action: {
            self.isSelected.toggle()
        }) {
            Text(isSelected ? "Selected" : "Not Selected")
        }
    }
}

struct ContentView: View {
    @EnvironmentObject var data: SomeData

    var body: some View {
        MyButton(isSelected: $data.isOn)
    }
}

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

@State static var
中的更改似乎不会触发预览重新渲染。在上面的代码中,我的
@Binding
示例被移动到
MyButton
中,内容视图的虚拟环境实例被绑定到其
isSelected
属性。点击按钮会按照SwiftUI p中的预期更新视图查看。

在SwiftUI的顶层,@Binding无法刷新视图层次结构,除非手动添加@state或其他刷新触发器

struct ContentView: View {
    @Binding var isSelected : Bool
    @State var hiddenTrigger = false

    var body: some View {
        VStack {
            Text("\(hiddenTrigger ? "" : "")")
            Button(action: {
                self.isSelected.toggle()
                self.hiddenTrigger = self.isSelected
            }) {
                Text(self.isSelected? "Selected" : "not Selected")
            }
        }
    }
}

struct ContentView_Previews: PreviewProvider {
            
    static var selected: Bool = false
            
    static var previews: some View {
        ContentView(isSelected: Binding<Bool>(get: {selected}, set: { newValue in
        selected = newValue}))
    }
}
struct ContentView:View{
@绑定变量:Bool
@状态变量hiddenTrigger=false
var body:一些观点{
VStack{
文本(“\(hiddenTrigger?”:”))
按钮(操作:{
self.isSelected.toggle()
self.hiddenTrigger=self.isSelected
}) {
文本(self.isSelected?“Selected”:“not Selected”)
}
}
}
}
结构内容视图\u预览:PreviewProvider{
选择的静态变量:Bool=false
静态var预览:一些视图{
ContentView(isSelected:Binding(get:{selected},set:{newValue in
选定值=新值})
}
}

SwiftUI视图影响
@Binding
@State
影响SwiftUI视图。
@State
var会影响视图,但若要影响另一个
@State
,必须将前导的
$
添加到值名称中,将其用作绑定,并且它仅在SwiftUI中起作用

要从外部触发SwiftUI更改,即交付/更新图像,请使用如下所示的Publisher:

// Declare publisher in Swift (outside SwiftUI).
public let imagePublisher = PassthroughSubject<Image, Never>()
    
// It must be handled within SwiftUI.
struct ContentView: View {
    // Declare a @State that updates View.
    @State var image: Image = Image(systemName: "photo")
    var body: some View {
        // Use @State image declaration
        // and subscribe this value to publisher "imagePublisher".
        image.onReceive(imagePublisher, perform: { (output: Image) in
            self.image = output  // Whenever publisher sends new value, old one to be replaced
        })
    }
}

// And this is how to send value to update SwiftUI from Swift:
imagePublisher.send(Image(systemName: "photo"))
//在Swift中声明发布者(在SwiftUI之外)。
public let imagePublisher=PassthroughSubject()
//它必须在SwiftUI中处理。
结构ContentView:View{
//声明更新视图的@State。
@状态变量图像:图像=图像(系统名称:“照片”)
var body:一些观点{
//使用@State图像声明
//并将此值订阅给publisher“imagePublisher”。
onReceive(imagePublisher,在中执行:{(输出:image))
self.image=output//每当publisher发送新值时,都会替换旧值
})
}
}
//以下是如何从Swift向更新SwiftUI发送值:
发送(图像(系统名:“照片”))

您没有误解任何内容。使用@Binding的视图将在基础@State更改时更新,但@State必须在视图层次结构中定义。(否则,您可以绑定到发布服务器)

下面,我已将ContentView的名称更改为OriginalContentView,然后在包含原始内容视图的新ContentView中定义了@State

import SwiftUI

struct OriginalContentView: View {
    @Binding var isSelected: Bool

    var body: some View {
        Button(action: {
            self.isSelected.toggle()
        }) {
            Text(isSelected ? "Selected" : "Not Selected")
        }
    }
}

struct ContentView: View {
    @State private var selected = false

    var body: some View {
       OriginalContentView(isSelected: $selected)
    }
}



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

不确定,但请尝试此操作$isSelected.WrappedValue谢谢您的解释。
切换
之类的操作是如何工作的,它接受
绑定
-切换是否无法设置
绑定的值
?我的目标是构建一个可以操纵外部绑定值的控件。谢谢,这就是行为viour我误解了。我没有意识到绑定到全局变量(通过自定义get/set闭包)或者绑定到静态
@State
不会刷新视图层次结构您的代码正是我要查找的。我不知道如何让
@State
属性订阅发布者。谢谢:d使预览工作的关键是将属性从PreviewProvider推到包装器视图(在您的示例中为ContentView)。完成此操作后,
@State
连接到
@Binding
的效果与
@EnvironmentObject
转换一样好。至少Xcode 12(beta 3)就是这种情况。Ragnarius感谢您的回答。但您是否也可以给出一个示例,说明如何使用您提到的其他方法,即“否则您可以绑定到发布者”我需要另一种方式来处理视图,其中视图在其初始值设定项中接受绑定,如切换。谢谢:)