繁体   English   中英

为什么当我通过@AppStorage 存储列表项目时添加项目后,我的选择有时不会在 macOS SwiftUI 列表中突出显示

[英]Why sometimes my selection doesn't get highlighted in macOS SwiftUI List after I've added an item when List items are stored through @AppStorage

我似乎找不到我做错了什么。

这是设置:

  1. 通过 AppStorage 存储为 Set 的捆绑标识符
  2. 通过 drop 添加的新包标识符

问题如下:

  1. 正常情况:项目被选中、突出显示和上下文删除可用(显示项目被选中)

在此处输入图像描述

  1. 正常情况:项目未选中、未突出显示、上下文删除不可用

在此处输入图像描述

  1. 问题:项目被选中但未突出显示,并且上下文可用(显示项目已被选中)

在此处输入图像描述

这个问题可能发生在 80% 的时间里,并非总是如此。 当我将新项目添加到列表中时会发生这种情况,并且我选择了该项目而没有先单击列表中的任何其他位置。 如果我先单击另一个项目,或者在列表中没有项目的空间中单击,则在之后选择新项目将正常工作,并且该项目将突出显示。 这让我觉得这可能是一个国家问题? 但我找不到我做错的地方。

这是代码:

import SwiftUI


// to save Sets through AppStorage
extension Set: RawRepresentable where Element: Codable {

    public init?(rawValue: String) {
        guard let data = rawValue.data(using: .utf8) else { return nil }
        guard let result = try? JSONDecoder().decode(Set<Element>.self, from: data) else { return nil}
        
        self = result
    }

    public var rawValue: String {
        guard let data = try? JSONEncoder().encode(self) else { return "[]" }
        guard let result = String(data: data, encoding: .utf8) else { return "[]" }
        
        return result
    }
    
}


struct ContentView: View {
    
    @AppStorage("apps") var apps: Set<String> = []
    @State private var appsSelection: Set<String> = []
    
    var body: some View {
        
        Form {
            List(Array(apps), id: \.self, selection: $appsSelection) { app in
                Text(app)
            }
            .contextMenu {
                Button("Delete") {
                    for selection in appsSelection {
                        apps.remove(selection)
                    }
                }
                .disabled(appsSelection.isEmpty)
            }
            .onDeleteCommand {
                for selection in appsSelection {
                    apps.remove(selection)
                }
            }
            .listStyle(.bordered(alternatesRowBackgrounds: true))
            .onDrop(of: [.fileURL], delegate: AppsDropDelegate(apps: $apps))
        }

    }
        
}


// the DropDelegate
private struct AppsDropDelegate: DropDelegate {

    @Binding var apps: Set<String>


    func validateDrop(info: DropInfo) -> Bool {
        guard info.hasItemsConforming(to: [.fileURL]) else { return false }

        let providers = info.itemProviders(for: [.fileURL])
        var result = false

        for provider in providers {
            if provider.canLoadObject(ofClass: URL.self) {
                let group = DispatchGroup()
                group.enter()

                _ = provider.loadObject(ofClass: URL.self) { url, _ in
                    let itemIsAnApplicationBundle = try? url?.resourceValues(forKeys: [.contentTypeKey]).contentType == .applicationBundle
                    result = result || (itemIsAnApplicationBundle ?? false)
                    group.leave()
                }
                                
                _ = group.wait(timeout: .now() + 0.5)
            }
        }

        return result
    }

    func performDrop(info: DropInfo) -> Bool {
        let providers = info.itemProviders(for: [.fileURL])
        var result = false

        for provider in providers {
            if provider.canLoadObject(ofClass: URL.self) {
                let group = DispatchGroup()
                group.enter()

                _ = provider.loadObject(ofClass: URL.self) { url, _ in
                    let itemIsAnApplicationBundle = (try? url?.resourceValues(forKeys: [.contentTypeKey]).contentType == .applicationBundle) ?? false

                    if itemIsAnApplicationBundle, let url = url, let app = Bundle(url: url), let bundleIdentifier = app.bundleIdentifier {
                        DispatchQueue.main.async {
                            apps.insert(bundleIdentifier)
                        }
                        
                        result = result || true
                    }
                                        
                    group.leave()
                }

                _ = group.wait(timeout: .now() + 0.5)
            }
        }
        
        return result
    }
    
}

提前致谢。

好的,经过更多测试,这似乎是一个 SwiftUI 错误。 可能与@AppStorage 的缓存机制有关,因为它不会即时从磁盘写入/读取。 SwiftUI 有时会发布更新,有时不会。 这已向 Apple 报告为 FB10029588。 此处的示例项目: https ://github.com/godbout/ListDrop。

解决这个问题的方法是手动更新选择。 为此,我创建了一个@ObservableObject ,它通过@AppStorage 处理项目列表,并通过@Published 属性处理选择。 删除选择时,我手动将属性设置为空。 这确实解决了问题。 下次添加项目时,它们会在选中时突出显示。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM