【问题标题】:SwiftUI: Slider in List/ForEach behaves strangelySwiftUI:List/ForEach 中的滑块行为异常
【发布时间】:2021-12-30 22:58:33
【问题描述】:

如果没有来自我没有的第二台设备的录音很难解释,但是当我尝试滑动滑块时,当我的手指肯定还在移动时它会停止。

我在下面发布了我的代码。我很乐意回答任何问题并进行解释。我确信这是我应该知道的非常简单的事情。任何帮助将不胜感激,谢谢!

import SwiftUI
    
    class SettingsViewModel: ObservableObject {
        @Published var selectedTips = [
            10.0,
            15.0,
            18.0,
            20.0,
            25.0
        ]
        
        func addTip() {
            selectedTips.append(0.0)
            selectedTips.sort()
        }
        
        func removeTip(index: Int) {
            selectedTips.remove(at: index)
            selectedTips = selectedTips.compactMap{ $0 }
        }
    }
    
    struct SettingsTipsView: View {
        @StateObject var model = SettingsViewModel()
        
        var body: some View {
            List {
                HStack {
                    Text("Edit Suggested Tips")
                        .font(.title2)
                        .fontWeight(.semibold)
                    
                    Spacer()
                    
                    if(model.selectedTips.count < 5) {
                        Button(action: { model.addTip() }, label: {
                            Image(systemName: "plus.circle.fill")
                                .renderingMode(.original)
                                .font(.title3)
                                .padding(.horizontal, 10)
                        })
                            .buttonStyle(BorderlessButtonStyle())
                    }
                }
                
                ForEach(model.selectedTips, id: \.self) { tip in
                    let i = model.selectedTips.firstIndex(of: tip)!
                    
                    //If I don't have this debug line here then the LAST slider in the list tries to force the value to 1 constantly, even if I remove the last one, the new last slider does the same. It's from a separate file but it's pretty much the same as the array above. An explanation would be great.
                    Text("\(CalculatorViewModel.suggestedTips[i])")
    
                    HStack {
                        Text("\(tip, specifier: "%.0f")%")
                        Slider(value: $model.selectedTips[i], in: 1...99, label: { Text("Label") })
                        
                        if(model.selectedTips.count > 1) {
                            Button(action: { model.removeTip(index: i) }, label: {
                                Image(systemName: "minus.circle.fill")
                                    .renderingMode(.original)
                                    .font(.title3)
                                    .padding(.horizontal, 10)
                            })
                                .buttonStyle(BorderlessButtonStyle())
                        }
                    }
                }
            }
        }
    }

【问题讨论】:

  • 看起来你可能需要在 ForEach 中使用 iOS15 的基于列表的绑定 - 在 hackingwithswift.com/quick-start/swiftui/… 使用 Swift 的技巧进行黑客攻击应该会给你一些线索吗?
  • 我实际上也允许 iOS 14 上的设备使用该应用程序,您还有什么可以推荐的吗?

标签: ios swift swiftui slider


【解决方案1】:

ListForEach 中使用 id: \.self 在 SwiftUI 中是一个危险的想法。系统使用它来识别它期望的独特元素。但是,只要您移动滑块,您就会改变最终的小费值等于列表中的另一个值。然后,SwiftUI 会混淆哪个元素是哪个。

要解决此问题,您可以使用具有真正唯一 ID 的项目。您还应该尽量避免使用索引来引用列表中的某些项目。我已经使用列表绑定来避免这个问题。

struct Tip : Identifiable {
    var id = UUID()
    var tip : Double
}

class SettingsViewModel: ObservableObject {
    @Published var selectedTips : [Tip] = [
        .init(tip:10.0),
        .init(tip:15.0),
        .init(tip:18.0),
        .init(tip:20.0),
        .init(tip:25.0)
    ]
    
    func addTip() {
        selectedTips.append(.init(tip:0.0))
        selectedTips = selectedTips.sorted(by: { a, b in
            a.tip < b.tip
        })
    }
    
    func removeTip(id: UUID) {
        selectedTips = selectedTips.filter { $0.id != id }
    }
}

struct SettingsTipsView: View {
    @StateObject var model = SettingsViewModel()
    
    var body: some View {
        List {
            HStack {
                Text("Edit Suggested Tips")
                    .font(.title2)
                    .fontWeight(.semibold)
                
                Spacer()
                
                if(model.selectedTips.count < 5) {
                    Button(action: { model.addTip() }, label: {
                        Image(systemName: "plus.circle.fill")
                            .renderingMode(.original)
                            .font(.title3)
                            .padding(.horizontal, 10)
                    })
                        .buttonStyle(BorderlessButtonStyle())
                }
            }
            
            ForEach($model.selectedTips, id: \.id) { $tip in
                HStack {
                    Text("\(tip.tip, specifier: "%.0f")%")
                        .frame(width: 50) //Otherwise, the width changes while moving the slider. You could get fancier and try to use alignment guides for a more robust solution
                    Slider(value: $tip.tip, in: 1...99, label: { Text("Label") })
                    
                    if(model.selectedTips.count > 1) {
                        Button(action: { model.removeTip(id: tip.id) }, label: {
                            Image(systemName: "minus.circle.fill")
                                .renderingMode(.original)
                                .font(.title3)
                                .padding(.horizontal, 10)
                        })
                            .buttonStyle(BorderlessButtonStyle())
                    }
                }
            }
        }
    }
}

【讨论】:

  • 感谢您的回复!当我有时间在我的笔记本电脑上试用它时,我会告诉你它是如何工作的。
  • 工作就像一个魅力!谢谢!
猜你喜欢
  • 1970-01-01
  • 2020-07-10
  • 1970-01-01
  • 2011-06-24
  • 1970-01-01
  • 2017-11-24
  • 2021-08-29
  • 2015-02-18
  • 1970-01-01
相关资源
最近更新 更多