SwiftUI 静态列表奇怪的重用行为

Posted

技术标签:

【中文标题】SwiftUI 静态列表奇怪的重用行为【英文标题】:SwiftUI static List weird reuse behavior 【发布时间】:2020-05-29 11:44:30 【问题描述】:

我在 SwiftUI 中使用静态列表时遇到了一个奇怪的行为。我无法确定这是 SwiftUI 错误还是我做错了什么。我有一个非常简单的列表,如下所示:

var body: some View 
    List 
        SettingsPickerView<TrigonometryUnit>(title: "Trigonometry Units", selection: $viewModel.trigonometryUnitIndex, items: TrigonometryUnit.allCases)
        SettingsPickerView<DecimalSeparator>(title: "Decimal Separator", selection: $viewModel.decimalSeparatorIndex, items: DecimalSeparator.allCases)
        SettingsPickerView<GroupingSeparator>(title: "Grouping Separator", selection: $viewModel.groupingSeparatorIndex, items: GroupingSeparator.allCases)
        SettingsPickerView<ExponentSymbol>(title: "Exponent Symbol", selection: $viewModel.exponentSymbolIndex, items: ExponentSymbol.allCases)
    

列表的每个单元格如下所示:

struct SettingsPickerView<T: Segmentable>: View 

    let title: String
    @Binding var selection: Int
    let items: [T]

    var body: some View 
        Section(header: Text(title)) 
            ForEach(items.indices)  index in
                self.cell(for: self.items[index], index: index)
            
        
    

    private func cell(for item: T, index: Int) -> some View 
        print(title, item.title, items.map( $0.title ))
        return Button(action: 
            self.selection = index
        , label: 
            HStack 
                Text(item.title)
                Spacer()
                if index == self.selection 
                    Image(systemName: "checkmark")
                        .font(.headline)
                        .foregroundColor(.rpnCalculatorOrange)
                
            
        )
    


最后,这是 Segmentable 对象的样子:

enum GroupingSeparator: Int, CaseIterable 

    case defaultSeparator
    case space
    case comma



extension GroupingSeparator: Segmentable 

    var id: String 
        switch self 
        case .defaultSeparator:
            return "groupingSeparator.default"
        case .space:
            return "groupingSeparator.space"
        case .comma:
            return "groupingSeparator.comma"
        
    

    var title: String 
        switch self 
        case .defaultSeparator:
            return "Default"
        case .space:
            return "Space"
        case .comma:
            return "Comma"
        
    


SettingsView 被加载时。一切看起来都很好。但是,一旦我开始滚动并实例化其他一些单元格,就会显示一些单元格,但不是正确的单元格。这是一些截图和日志。

当视图被加载时,没有滚动,这是屏幕的样子:

但是,我在控制台上得到的东西很奇怪,并且不遵循主视图中写的SettingsPickerView 的顺序:

Trigonometry Units Radians ["Radians", "Degrees"] <-- Fine
Trigonometry Units Degrees ["Radians", "Degrees"] <-- Fine
Decimal Separator Default ["Default", "Dot", "Comma"] <-- Fine
Decimal Separator Default ["Default", "Dot", "Comma"] <-- Fine
Trigonometry Units Degrees ["Radians", "Degrees"] <-- Not expected. Should be Grouping Separator
Trigonometry Units Radians ["Radians", "Degrees"] <-- Not expected. Should be Grouping Separator

第二部分没问题,正常显示:

但是第三部分完全断了:

第三部分正确显示其标题,但显示第一部分的一些数据。我尝试向单元格中的按钮添加标识符,因为问题看起来像 SwiftUI 无法识别正确的数据。但是向按钮添加标识符会破坏绑定,并且复选框不再更改。

private func cell(for item: T, index: Int) -> some View 
    print(title, item.title, items.map( $0.title ))
    return Button(action: 
        self.selection = index
    , label: 
        HStack 
            Text(item.title)
            Spacer()
            if index == self.selection 
                Image(systemName: "checkmark")
                    .font(.headline)
                    .foregroundColor(.rpnCalculatorOrange)
            
        
    )
        .id(UUID().uuidString) // This solve the display issue but broke the binding.

以前有人经历过这样的事情吗?

提前感谢您的帮助。

【问题讨论】:

您能提供最小的可测试示例吗?由于缺少许多依赖项,无法复制代码? @Asperi 当然谢谢。您可以在此处查看我的存储库,其中包含一个带有我面临的错误的示例项目。 github.com/bpisano/Watch-app-test.git 【参考方案1】:

这里是固定的代码块(由于使用的索引只有 List 被混淆并重用行,所以解决方案是使行可以被项目识别)。

使用 Xcode 11.4 测试

struct PickerView<T: Segmentable>: View 

    // ... other code here

    var body: some View 
        Section(header: Text(title)) 
            // Corrected section construction !!
            ForEach(Array(items.enumerated()), id: \.element.id)  index, _ in
                self.cell(for: self.items[index], index: index)
            
        
    

    // ... other code here

【讨论】:

谢谢,这正是我想要的!我知道这是某种可识别的问题,但不知道如何处理 .enumerated() 和 SwiftUI Foreach

以上是关于SwiftUI 静态列表奇怪的重用行为的主要内容,如果未能解决你的问题,请参考以下文章

SwiftUI - 披露组扩展行为 - 保持顶部项目静态

SwiftUI:onAppear 的奇怪行为

SwiftUI:NavigationLink 奇怪的行为

SwiftUI TextField (MacOS) 的奇怪行为

SwiftUI:奇怪的行为 ScrollView

SwiftUI 奇怪的动画行为与 systemImage