【问题标题】:onChange not firing when attached to a Picker附加到 Picker 时,onChange 不会触发
【发布时间】:2021-02-22 15:05:10
【问题描述】:

我有一个由 3 部分组成的选择器,我试图让一个选择器的值基于另一个选择器的值。特别是在“Days”、“Weeks”等末尾添加/删除 s。我已经阅读了关于这种情况的类似帖子 (here),但是针对 IOS 14+ 部署的建议 Apple 解决方案不起作用。鉴于另一个问题主要关注 14 岁之前的解决方案,我认为开始一个新问题会更有帮助。

谁能解释为什么永远不会调用 .onChange应该。

非常规的初始化只是为了封装从更大项目中删除的代码。 此外,我在下面的代码中注释掉了第三个选择器的 .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()))
    }
}

【问题讨论】:

    标签: swiftui onchange


    【解决方案1】:

    试试下面的

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

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

    【讨论】:

    • 我的问题是我试图在预览中调试它。正如您所评论的,预览是在提供的 eventItem 周围使用 .constant() 生成的,因此 onChange 没有触发。
    【解决方案2】:

    对于 Picker,它的 item 数据类型必须符合 Identifiable 并且我们必须将 item 的属性作为 "id" 传递给 "tag" 修饰符,以让 Picker 触发选择并在 Binding 变量中返回该属性与选择。

    例如:

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

    //天哪!我花了整整一天的时间才发现这个

    【讨论】:

      【解决方案3】:

      上面 Thang Dang 的回答对我很有帮助。我不知道如何使我的标签符合 Identifiable,但将我的标签从 tag(1) 更改为字符串,如下面的 SwiftUI 代码所示。当 Picker 设置为 Icosahedron(我在 setShape 上的断点从未触发)时,其中只有一个数字的标签不会导致任何事情发生,但其他三个导致正确的形状被传递给 setShape。

      //设置当前Shape

      func setShape(value: String) { 打印(值) }

      @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)")
                  })
            }
      

      【讨论】:

        猜你喜欢
        • 2012-06-12
        • 1970-01-01
        • 1970-01-01
        • 2020-05-22
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多