在SwiftUI中使用.OnTapGesture切换@State变量

时间:2020-04-15 20:04:40

标签: swift swiftui state

有人可以告诉我为什么这种逻辑不起作用吗?我正在尝试创建视图的实例并将其存储在变量中。然后,我使用此变量返回var主体中的视图。我的目标是在水龙头上切换视图对象的isActive变量,以便显示选中标记图像。

当我将onTapGesture放入自定义视图对象中时,我可以使其工作,但是当我从父视图中切换变量时,我无法获得状态更改。我希望这是有道理的。

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()
        }
    }
}

2 个答案:

答案 0 :(得分:0)

为什么不起作用?

您无法保存FamilyItemView的实例。为什么?因为它是struct,而不是class。切换isActive属性时,将重新创建视图(因为它使用的是@State)。

如何解决?

使用@Binding。创建绑定意味着FamilyItemView的{​​{1}}属性更改时将更新SensorFamilyView。可以按以下方式使用它:

isActive

旁注:至于现在的代码,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

答案 1 :(得分:0)

@State

要了解这一点,我们需要先触摸@State。什么事?

SwiftUI管理您声明为状态的任何属性的存储。当状态值更改时,视图将使其外观无效并重新计算主体。
...
状态实例不是值本身;这是读写价值的一种手段。要访问州的基础值,请使用其变量名,该变量名返回wrappedValue属性值。
Ref:https://developer.apple.com/documentation/swiftui/state

但是我们为什么需要@State?好吧...结构是值类型,默认情况下它的变量是不变的,因此,为了解决这个问题,提供了@State propertyWrapper,它基本上包装了一个值,并在某种程度上为我们存储和维护它SwiftUI框架中的 * 存储。
*有关更多详细信息,请参见WWDC:https://developer.apple.com/videos/play/wwdc2019/226/

在声明@State的结构体中更改View属性时,SwiftUI的引擎会自动重新渲染该体。但是,如果从视图外部对其进行修改,SwiftUI将不会对此进行处理。

那现在呢?
这就是@Binding可以用来创建2向绑定的地方。


@Binding

使用绑定在存储数据的属性与显示和更改数据的视图之间创建双向连接。绑定将属性连接到存储在其他位置的事实来源,而不是直接存储数据。例如,在播放和暂停之间切换的按钮可以使用@Binding属性包装器为其父视图的属性创建绑定。
Ref:https://developer.apple.com/documentation/swiftui/binding

解决方案:

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)")
        }
    }
}
  • SensorFamilyView具有状态属性isActive
  • FamilyItemView具有绑定属性isActive

它们之间有2向绑定,因此当一个更改时,另一个也会更改。此外,所有这些都在Combine框架(SwiftUI高度依赖于此)内,因此触发了导致主体渲染的正确事件序列。