onChange 在附加到 Picker 时不触发

onChange not firing when attached to a Picker

我有一个由 3 部分组成的选择器,我正在尝试使一个选择器的值基于另一个选择器的值。具体来说 adding/removing“天”、“周”等末尾的 s。我已经阅读了关于此类情况的类似 post (),但针对 IOS 14+ 部署提出的 Apple 解决方案不起作用。鉴于另一个问题主要关注 14 岁之前的解决方案,我认为开始一个新问题会更有帮助。

谁能解释为什么 .onChange 永远不会被调用? 我在那里设置了一个断点,当中间轮的值在 1 和应有的任何其他值。

非常规的 init 只是为了我可以封装从更大的项目中删除的代码。 另外,我在下面的代码中注释掉了第三个选择器的 .id,但是如果剩下的唯一问题是第三个选择器更新更改,我可以取消注释。

import SwiftUI

enum EveryType:String, Codable, CaseIterable, Identifiable {

    case every="Every"
    case onceIn="Once in"

    var id: EveryType {self}
    var description:String {
        get {
            return self.rawValue
        }
    }
}

enum EveryInterval:String, Codable, CaseIterable, Identifiable {
    case days = "Day"
    case weeks = "Week"
    case months = "Month"
    case years = "Year"

    var id: EveryInterval {self}
    var description:String {
        get {
            return self.rawValue
        }
    }
}

struct EventItem {
    var everyType:EveryType = .onceIn
    var everyInterval:EveryInterval = .days
    var everyNumber:Int = Int.random(in:1...3)
}

struct ContentView: View {

    init(eventItem:Binding<EventItem> = .constant(EventItem())) {
        _eventItem = eventItem
    }

    @Binding var eventItem:EventItem 
    @State var intervalId:UUID = UUID()

    var body: some View {
        GeometryReader { geometry in
            HStack {
                Picker("", selection: self.$eventItem.everyType) {
                    ForEach(EveryType.allCases)
                    { type in Text(type.description)
                    }
                }
                .pickerStyle(WheelPickerStyle())
                .frame(width: geometry.size.width * 0.3, height:100)
                .compositingGroup()
                .padding(0)
                .clipped()

                Picker("", selection: self.$eventItem.everyNumber
                ) {
                    ForEach(1..<180, id: \.self) { number in
                        Text(String(number)).tag(number)
                    }
                }
                //The purpase of the == 1 below is to only fire if the
                // everyNumber values changes between being a 1 and
                // any other value.
                .onChange(of: self.eventItem.everyNumber == 1) { _ in
                    intervalId = UUID() //Why won't this ever happen?
                }
                .pickerStyle(WheelPickerStyle())
                .frame(width: geometry.size.width * 0.25, height:100)
                .compositingGroup()
                .padding(0)
                .clipped()

                Picker("", selection: self.$eventItem.everyInterval) {
                    ForEach(EveryInterval.allCases) { interval in
                            Text("\(interval.description)\(self.eventItem.everyNumber == 1 ? "" : "s")")
                    }
                }
                .pickerStyle(WheelPickerStyle())
                .frame(width: geometry.size.width * 0.4, height:100)
                .compositingGroup()
                .clipped()
                //.id(self.intervalId)
            }
        }
        .frame(height:100)
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView(eventItem: .constant(EventItem()))
    }
}

尝试以下方法

.onChange(of: self.eventItem.everyNumber) { newValue in
    if newValue  == 1 {
       intervalId = UUID()
    }
}

但这也可能取决于您如何使用此视图,因为使用 .constant 绑定什么都不会改变。

对于Picker来说,它的item数据类型必须符合Identifiable,我们必须将一个属性的item传给“tag”修饰符作为“id”让Picker触发选择,return那个属性 在带选择的绑定变量中。

例如:

Picker(selection: $selected, label: Text("")){
            
            ForEach(data){item in //data's item type must conform Identifiable
                
                HStack{
                    
                    //item view
                    
                
                }
                .tag(item.property)
                
            }
            
        }
        
        .onChange(of: selected, perform: { value in
            
            //handle value of selected here (selected = item.property when user change selection)
            
        })

//天哪!我花了整整一天才弄明白这个

上面 Thang Dang 的回答对我很有帮助。我不知道如何使我的标签符合 Identifiable,但将我的标签从 tag(1) 更改为字符串,如下面的 SwiftUI 代码所示。 The tag with a mere number in it caused nothing to happen when the Picker was set to Icosahedron (my breakpoint on setShape was never triggered), but the other three caused the correct shape to be passed in to setShape.

//设置当前形状

func setShape(值:字符串){ 打印(值) }

@State var shapeSelected = "Cube"

    VStack {
         Picker(selection: $shapeSelected, label: Text("$\(shapeSelected)")) {
             Text("Cube").tag("Cube")
             Text("Simplex").tag("Simplex")
             Text("Pentagon (3D)").tag("Pentagon")
             Text("Icosahedron").tag(1)
         }.onChange(of: shapeSelected, perform: { tag in
             setShape(value: "\(tag)")
            })
      }