如何实现选取器以从本地文件 URL 数组中选取一个或多个项目

问题描述 投票:0回答:1

我正在使用 macOS 14 和 Xcode 15,并且是 Swift/SwiftUI 的新手。

过去几天我一直在尝试实现一个选择器,它允许以与 Finder 相同的方式选择多个项目,即用户可以取消、选择一个项目,或者使用 Shift 键选择两个或多个连续的项目。

在我尝试使用“onChange”之前,它有点有效,文本显示的数据有效,但没有更新,并且选择器不允许我选择多个项目。

我不知道如何实现 onChange,因为它最近更新了。我的网络搜索仅返回 IOS 的代码或旧代码。

我在尝试“onChange”修改时遇到了几个我不明白的错误。无论我如何表达我的问题,zzzcode.ai 都不会生成可编译的代码。

请注意,当显示 URL 时,我仅显示最后一个组件。

我想首先解决“onChange”问题,因为它可能会修复一些错误。

import SwiftUI

extension URL: Identifiable {
    public var id: Self { self }
}

struct PickTransectView: View {
    @Binding  var datList: [URL] // local file URLs w/.dat
    @Binding  var selectedURLs: [URL]? // subset of datList

    // only display last URL component in this view
    var body: some View {
        VStack {
            Text("Selected Transects:")
            if let selectedURLs = selectedURLs { // if nil take from datList
                if(selectedURLs.count > 1) {
                    Text("\(selectedURLs.first!.lastPathComponent) - \(selectedURLs.last!.lastPathComponent)")
                } else {
                    Text("\(selectedURLs.last!.lastPathComponent)")
                }
            } else if let firstURL = datList.first, let lastURL = datList.last {
                Text("\(firstURL.lastPathComponent) - \(lastURL.lastPathComponent)")
            }

            // expected return 1 or several contiguous URLs
            Picker(selection: $selectedURLs, label: Text("Detailed List")) {
                ForEach(datList, id: \.self) { url in
                    Text(url.lastPathComponent)
                }
            }
            .pickerStyle(MenuPickerStyle())
            .onChange(of: selectedURLs) { _ in // it fails here !
                // Update Text on change of selectedURLs
                if selectedURLs.count > 1 { // first & last URLs in selection
                    Text("\(selectedURLs.first!.lastPathComponent) - \(selectedURLs.last!.lastPathComponent)")
                } else if let firstURL = selectedURLs.first {
                    Text("\(firstURL.lastPathComponent)")
                }
            } 
        }
    }
}
xcode macos swiftui picker
1个回答
0
投票

当用户点击 TransectPickerView 中的按钮时,会出现一个弹出表来完成这项工作。

结构 TransectPickerView: 视图 { @Binding var selectedURLs:[URL] @Binding var datList: [URL] // 带有 .dat 的本地文件 URL

@State private var isPresentingSelection: Bool = false

var body: some View {
    VStack {
        Button(action: {
            isPresentingSelection = true
        }) {
            Text("Select Transects")
        }
        .buttonStyle(ButtonStyleControls())
        .padding()
        
    } // POPUP
    .sheet(isPresented: $isPresentingSelection) {
        FileURLSelectionSheetView(
            selectedURLs: $selectedURLs,
            datList: $datList)
    }
}

}

下面的代码显示本地文件 URL 数组的最后一个组成部分,并允许用户选择(和取消选择)文件。如果选择了第二个文件,则会构建两个选定文件之间(包括这两个文件)的文件数组。按钮还允许用户:选择所有文件、接受关闭视图的选择或关闭视图而不保存。

结构 FileURLSelectionSheetView: 查看 { @Binding var selectedURLs: [URL] // 待填充 @Binding var datList: [URL] // 带.dat 的本地文件 URL @Environment(.presentationMode) var 演示模式 // 当地的 @State var urlIndexSet = Set() // 消失 @State var firstURL:URL? @State var showSelectAll = true

var body: some View {
    VStack {
        Text(" Select Transects ")
            .font(.title)
            .padding()
        ScrollView {
            ForEach(datList.indices, id: \.self) { index in   // present all URLs
                let url = datList[index] // user selection
                Text("\(url.lastPathComponent)")
                    .background(urlIndexSet.contains(index) ? Color.yellow.opacity(0.4) : Color.clear)
                    .onTapGesture {  // tap logic tree starts here ...
                        if urlIndexSet.isEmpty {
                            urlIndexSet.insert(index) // first endpoint
                        } else if urlIndexSet.count == 1 { // endpoint
                            if urlIndexSet.contains(index) {  // edit
                                urlIndexSet.removeAll()
                            } else {  // 2nd end point, create list
                                if index > urlIndexSet.first! { // ascending
                                    for item in urlIndexSet.first!...index {
                                        urlIndexSet.insert(item)
                                    }
                                } else {  // descending
                                    for item in index...urlIndexSet.first!{
                                        urlIndexSet.insert(item)
                                    }
                                }
                            }
                        } // onTapGesture
                    } // 1st ForEach
            }  // ScrollView
            .frame(maxHeight: .infinity)
            
            Button(action: {
                if showSelectAll {
                    for item in 0..<datList.count {
                        urlIndexSet.insert(item)
                    }
                    showSelectAll = false
                } else {
                    urlIndexSet.removeAll()
                    showSelectAll = true
                }
            }) {
                if showSelectAll {
                    Text("Select All")
                        .padding(.top, 10)
                } else {
                    Text("Reset All")
                        .padding(.top, 10)
                }
                
            }
            .buttonStyle(ButtonStyleControls())
            
            
            HStack() {
                Button(action: {  // This appears to present correctly
                    selectedURLs.removeAll() //
                    presentationMode.wrappedValue.dismiss()
                }) {
                    Text("Cancel")
                }
                .buttonStyle(ButtonStyleControls())
                
                Spacer()
                // =============  populaate selectedURLs ===========
                Button(action: {
                    
                    selectedURLs.removeAll()
                    let indexList = urlIndexSet.sorted()
                    for item in indexList
                    {
                        selectedURLs.append(datList[item])
                    }
                    presentationMode.wrappedValue.dismiss()
                }) {
                    Text("Accept")
                }
                .buttonStyle(ButtonStyleControls())
                .disabled(urlIndexSet.isEmpty)
                .opacity(urlIndexSet.isEmpty ? 0.5 : 1.0) // Mute if disabled
                
            } // HStack
            .padding()
            Divider()
            Text("\(urlIndexSet.count) selected")
                .font(.title2)
            
        }  // VStack
        .frame(maxWidth: .infinity, maxHeight: .infinity) // Adjust the height of the VStack
    }
}  // end popup view struct

}

我已成功测试了最多 28 个 URL。如果它超出显示器的高度,我希望它能够滚动。

© www.soinside.com 2019 - 2024. All rights reserved.