在SwiftUI中使用.ontap手势切换@State变量

在SwiftUI中使用.ontap手势切换@State变量,swift,swiftui,state,Swift,Swiftui,State,有人能告诉我为什么这个逻辑不起作用吗?我试图创建一个视图实例并将其存储在变量中。然后我使用这个变量返回var body中的一个视图。我的目标是在点击时切换view对象的isActive变量,以便显示选中标记图像 当我将ontapsignature放在自定义视图对象中时,我可以实现这一点,但是当我从父视图切换变量时,我无法获得状态的更改。我希望这是有道理的 struct SensorFamilyView: View { @State var analogView = FamilyItem

有人能告诉我为什么这个逻辑不起作用吗?我试图创建一个视图实例并将其存储在变量中。然后我使用这个变量返回var body中的一个视图。我的目标是在点击时切换view对象的isActive变量,以便显示选中标记图像

当我将ontapsignature放在自定义视图对象中时,我可以实现这一点,但是当我从父视图切换变量时,我无法获得状态的更改。我希望这是有道理的

struct SensorFamilyView: View {

    @State var analogView = FamilyItemView(title: "Analog", isActive: false)

    var body: some View {

        VStack(alignment: .leading, spacing: 0) {

            analogView                                   // Show view instance

                .onTapGesture {                          // I want this tap gesture to work
                    self.analogView.isActive.toggle()
            }

        }
    }
}

struct FamilyItemView: View {                            // Custom View

    @State var title: String
    @State var isActive = false

    var body: some View {


        HStack {

            if ( isActive )                              // isActive toggles a checkmark image
            {   
                Image(systemName: "checkmark.circle")
            }
            else
            {
                Image(systemName: "circle")
            }

            Text("\(title)")
        }

        .padding()
        .onTapGesture {                            // This Tap works, but not what I want
            //self.isActive.toggle()
        }
    }
}
为什么不起作用? 您不能持有
FamilyItemView
的实例。为什么?因为它是一个
结构
,而不是
。当您切换
isActive
属性时,将重新创建视图(因为它使用的是
@State

如何解决这个问题? 使用
@Binding
。创建绑定意味着当
SensorFamilyView
isActive
属性更改时,
FamilyItemView
将被更新。它可以按如下方式使用:

struct SensorFamilyView: View {
    
    @State private var isActive = false
    
    var body: some View {
        VStack(alignment: .leading, spacing: 0) {
            FamilyItemView(title: "Analog", isActive: $isActive)
                .onTapGesture {
                    self.isActive.toggle()
                }
        }
    }
}

struct FamilyItemView: View {
    
    @State var title: String
    @Binding var isActive: Bool
    
    var body: some View {
        HStack {
            if isActive {
                Image(systemName: "checkmark.circle")
            } else {
                Image(systemName: "circle")
            }
            
            Text("\(title)")
        }.padding()
    }
}
旁注:对于现在的代码,
title
不需要是
@State

代码的额外清除 @陈述 要理解这一点,我们需要首先接触
@State
。这是什么

SwiftUI管理您声明为状态的任何属性的存储。当状态值更改时,视图将使其外观无效并重新计算主体。

状态实例不是值本身;这是一种读写价值观的方法。要访问状态的基础值,请使用其变量名,该变量名返回wrappedValue属性值。
参考:

但是为什么我们需要
@State
?好。。。结构是值类型,默认情况下它的变量是不变异的,因此为了避免这种情况,提供了
@State
PropertyRapper
,它基本上包装了一个值,并将其存储和维护在SwiftUI框架内的一些Persistent*存储中。
*有关更多详细信息,请参阅本网站上的WWDC:

@State
属性在声明该属性的
视图
结构体中发生更改时,SwiftUI的引擎会自动重新呈现该属性体。但是如果它是从视图外部修改的,SwiftUI不会注意到这一点

那么,现在怎么办?
这就是可以使用
@Binding
创建双向绑定的地方


@装订 使用绑定可以在存储数据的属性与显示和更改数据的视图之间创建双向连接。绑定将属性连接到存储在别处的真理源,而不是直接存储数据。例如,在“播放”和“暂停”之间切换的按钮可以使用@binding property包装器创建到其父视图属性的绑定。
参考:

解决方案:
  • SensorFamilyView
    具有状态属性
    isActive
  • FamilyItemView
    具有绑定属性
    isActive

它们之间有一个双向绑定,所以当一个发生变化时,另一个也会发生变化。此外,这一切都在Combine框架内(SwiftUI主要基于该框架),因此会触发正确的事件序列,从而导致主体渲染。

非常感谢您花时间向我解释这一点。非常感谢!非常有用的指导。
struct FamilyItemView: View {
    
    let title: String
    @Binding var isActive: Bool
    
    var body: some View {
        HStack {
            Image(systemName: isActive ? "checkmark.circle" : "circle")
            Text(title)
        }.padding()
    }
}
struct SensorFamilyView: View {
    @State var isActive: Bool = false

    var body: some View {
        VStack(alignment: .leading, spacing: 0) {
            FamilyItemView(title: "Title", isActive: $isActive)
                .onTapGesture {
                    self.isActive.toggle()
            }
        }
    }
}

struct FamilyItemView: View {
    @State var title: String
    @Binding var isActive: Bool

    var body: some View {
        HStack {
            if (isActive) {
                Image(systemName: "checkmark.circle")
            } else {
                Image(systemName: "circle")
            }
            Text("\(title)")
        }
    }
}