首页 > 解决方案 > 如何仅显示 SwiftUI 列表中的选定项目?

问题描述

当 editMode 为 .inactive 时,如何让 List 显示一组选定项目,而当 editMode 为 .active 时显示所有可选选项,以便用户可以更改选定项目集?这是我尝试过的:

import SwiftUI

struct SelectionView: View {
    @Environment(\.editMode) var editMode
    @State var selectedItems = Set<String>(["1-item", "2-item", "3-item", "4-item"])
    let allItems = ["1-item", "2-item", "3-item", "4-item"]
    var items: [String] {
        if editMode?.wrappedValue == .inactive {
            return Array(selectedItems)
        }
        else {
            return allItems
        }
    }

    var body: some View {
        NavigationView {
            List(items, id: \.self, selection: $selectedItems) { item in
                Text(item)
            }
            .navigationBarItems(trailing: EditButton())
            .navigationBarTitle("Items")
        }
    }
}

选定的数组现在是无序的,因为它是从一组生成的,我最终希望它以正确的顺序排列。但我首先尝试让列表按原样工作。

标签: iosswiftlistswiftuieditmode

解决方案


这是它如何为我工作的方法。不幸的是,由于List内部细节(或错误),它并不完全好......

想法是List根据选定的项目切换过滤其中一个的容器,而切换本身取决于手动跟踪EditMode

无论如何它都有效(使用 Xcode 11.2/iOS 13.2 测试,以及预览版)

struct SelectionView: View {
    @State var editMode: EditMode = .inactive // ! Needed manual to track states

    @State var selectedItems = Set<String>(["1-item", "2-item", "3-item", "4-item"])
    let allItems = ["1-item", "2-item", "3-item", "4-item"]
    var items: [String] {
        allItems.filter { self.selectedItems.contains($0) }
    }

    @State var applyFilter = false
    var body: some View {
        NavigationView {
            VStack {
                List(applyFilter ? items : allItems, id: \.self,
                     selection: $selectedItems) { item in
                        Text(item)
                }
                .navigationBarItems(trailing: EditButton())
                .navigationBarTitle("Items")
            }
//            .environment(\.editMode, $editMode) // << this is how it should be, but crash
            .environment(\.editMode, Binding<EditMode>(get: { self.editMode },
               set: {
                    self.editMode = $0

                    // !!! below is needed workaround, because applying filter directly
                    // on close EditMode result in crash due to cached List internals
                    // until switch EditMode animation is finished completely
                    if $0 == .inactive {
                        DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) {
                            self.applyFilter = true
                        }
                    } else {
                        self.applyFilter = false
                    }
            }))
        }
    }
}

推荐阅读