使用 DocumentPicker SwiftUI 更改状态
Posted
技术标签:
【中文标题】使用 DocumentPicker SwiftUI 更改状态【英文标题】:Change State with DocumentPicker SwiftUI 【发布时间】:2019-10-11 19:05:27 【问题描述】:我正在尝试在使用 documentPicker 选择文档后显示列表视图。收到以下错误...
Fatal error: No ObservableObject of type Switcher found.
A View.environmentObject(_:) for Switcher may be missing as an ancestor of this view.: file /BuildRoot/Library/Caches/com.apple.xbs/Sources/Monoceros/Monoceros-30.4/Core/EnvironmentObject.swift, line 55
似乎我应该使用 EnviromentObject 绑定来让所有视图都能够读取、访问和更新 Switcher 类。在 CSVDocumentPicker.swift 中的 Coordinator 类下似乎出了点问题。 我正在使用 @EnvironmentObject var switcher:Switcher 并使用 documentPicker 函数来切换切换器状态,以便显示列表视图。我被难住了。
SceneDelegate.swift
import UIKit
import SwiftUI
class SceneDelegate: UIResponder, UIWindowSceneDelegate
var window: UIWindow?
var switcher = Switcher()
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions)
let contentView = ContentView().environmentObject(Switcher())
if let windowScene = scene as? UIWindowScene
let window = UIWindow(windowScene: windowScene)
window.rootViewController = UIHostingController(rootView: contentView.environmentObject(switcher))
self.window = window
window.makeKeyAndVisible()
func sceneDidDisconnect(_ scene: UIScene)
func sceneDidBecomeActive(_ scene: UIScene)
func sceneWillResignActive(_ scene: UIScene)
func sceneWillEnterForeground(_ scene: UIScene)
func sceneDidEnterBackground(_ scene: UIScene)
CSVDocumentPicker.swift
import Combine
import SwiftUI
class Switcher: ObservableObject
var didChange = PassthroughSubject<Bool, Never>()
var isEnabled = false
didSet
didChange.send(self.isEnabled)
struct CSVDocumentPicker: View
@EnvironmentObject var switcher:Switcher
@State private var isPresented = false
var body: some View
VStack
Text("csvSearch")
Button(action: self.isPresented = true
)
Text("import")
Image(systemName: "folder").scaledToFit()
.sheet(isPresented: $isPresented)
() -> DocumentPickerViewController in
DocumentPickerViewController.init(onDismiss:
self.isPresented = false
)
if switcher.isEnabled
ListView()
struct CSVDocumentPicker_Previews: PreviewProvider
static var previews: some View
CSVDocumentPicker().environmentObject(Switcher())
/// Wrapper around the `UIDocumentPickerViewController`.
struct DocumentPickerViewController
private let supportedTypes: [String] = ["public.item"]
// Callback to be executed when users close the document picker.
private let onDismiss: () -> Void
init(onDismiss: @escaping () -> Void)
self.onDismiss = onDismiss
// MARK: - UIViewControllerRepresentable
extension DocumentPickerViewController: UIViewControllerRepresentable
typealias UIViewControllerType = UIDocumentPickerViewController
func makeUIViewController(context: Context) -> DocumentPickerViewController.UIViewControllerType
let documentPickerController = UIDocumentPickerViewController(documentTypes: supportedTypes, in: .import)
documentPickerController.allowsMultipleSelection = false
documentPickerController.delegate = context.coordinator
return documentPickerController
func updateUIViewController(_ uiViewController: DocumentPickerViewController.UIViewControllerType, context: Context)
// MARK: Coordinator
func makeCoordinator() -> Coordinator
Coordinator(self)
class Coordinator: NSObject, UIDocumentPickerDelegate, ObservableObject
@EnvironmentObject var switcher:Switcher
var parent: DocumentPickerViewController
init(_ documentPickerController: DocumentPickerViewController)
parent = documentPickerController
func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentAt url: URL)
globalPathToCsv = url
loadCSV()
switcher.isEnabled.toggle()
func documentPickerWasCancelled(_ controller: UIDocumentPickerViewController)
parent.onDismiss()
ContentView.swift
import SwiftUI
import UIKit
var globalPathToCsv:URL!
var csvArray = [[String:String]]()
var csv = CSVaccessability()
func loadCSV()
csv.csvToList()
// print(csvArray)
struct ContentView: View
@EnvironmentObject var switcher:Switcher
var body: some View
VStack
CSVDocumentPicker().environmentObject(Switcher())
struct ContentView_Previews: PreviewProvider
static var previews: some View
ContentView().environmentObject(Switcher())
ListView.swift
import SwiftUI
struct ListView: View
var body: some View
HStack
List
ForEach(csvArray, id:\.self) dict in Section DataList(dict: dict)
struct DataList : View
@State var dict = [String: String]()
var body: some View
let keys = dict.map$0.key
let values = dict.map $0.value
return ForEach(keys.indices) index in
HStack
Text(keys[index])
Text("\(values[index])")
struct ListView_Previews: PreviewProvider
static var previews: some View
ListView()
CSVaccessability.swift
import Foundation
import SwiftCSV
var csvData:[[String]]!
var headers:[String] = []
class CSVaccessability
var numberOfColumns:Int!
var masterList = [[String:Any]]()
func csvToList()
if let url = globalPathToCsv
do
print(url)
let csvFile: CSV = try CSV(url: globalPathToCsv)
let csv = csvFile
//print(stream)
//print(csvFile)
headers = csv.header
csvArray=csv.namedRows
catch print("contents could not be loaded")
else print("the URL was bad!")
我已经为这个项目导入了 SwiftCSV...
【问题讨论】:
【参考方案1】:创建了一个新类... ToggleView.swift
import Foundation
class ToggleView: ObservableObject
@Published var toggleView: Bool = false
将环境对象添加到 ContentView.swift @EnvironmentObject var viewToggle: ToggleView
还将 .environmentObject(ToggleView()) 添加到任何将被调用并导致崩溃的视图中,崩溃日志对此有所帮助...
Text("csvSearch")
Button(action: self.isPresented = true
self.viewToggle.toggleView.toggle()
// self.switcher = true
)
Text("import")
Image(systemName: "folder").scaledToFit()
.sheet(isPresented: $isPresented)
() -> DocumentPickerViewController in
DocumentPickerViewController.init()
if self.picker
DocumentPickerViewController().environmentObject(ToggleView())
if self.viewToggle.toggleView
ListView()
【讨论】:
【参考方案2】:你有没有得到这个工作?我发现的唯一问题是 ContentView 中的 var csv = CSVaccessability()
行。 CSV 可访问性不存在。
【讨论】:
我还没有让它工作。我在上面添加了 CSVaccessability。仍在寻找解决方案! 在我的情况下,协调器对象被释放,并且在调用documentPicker(didPickDocumentsAt: [URL])
方法之前调用了 didCancel 方法,我不知道如何为协调器提供强引用以防止其释放。跨度>
【参考方案3】:
这是我针对 Mac 的Catalyst
应用程序的解决方案,但为了避免再次按下Image (systemName: "book")
按钮来更新文本字段中的数据,我在GeoFolderReadFileView
中实现了一个隐藏视图来强制查看更新。
//文件:GeoFolderCodStruct
import Foundation
struct GeoFolderCodStruct:Codable
var isActive:Bool = true
var dataCreazione:Date = Date()
var geoFolderPath:String = ""
var nomeCartella:String = ""
var nomeCommittente:String = ""
var nomeArchivio:String = ""
var note:String = ""
var latitudine:Double? = nil
var longitudine:Double? = nil
var radiusCircle:Int16? = nil
//Roma 42.1234 13.1234
//文件:PickerForReadFile
import SwiftUI
final class PickerForReadFile: NSObject, UIViewControllerRepresentable, ObservableObject
@Published var geoFolder = GeoFolderCodStruct()
lazy var viewController:UIDocumentPickerViewController =
let vc = UIDocumentPickerViewController(documentTypes: ["geof"], in: .open)
vc.allowsMultipleSelection = false
vc.delegate = self
return vc
()
func makeUIViewController(context: UIViewControllerRepresentableContext<PickerForReadFile>) -> UIDocumentPickerViewController
viewController.delegate = self
return viewController
func updateUIViewController(_ uiViewController: UIDocumentPickerViewController, context: UIViewControllerRepresentableContext<PickerForReadFile>)
print("updateUIViewController")
extension PickerForReadFile: UIDocumentPickerDelegate
func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL])
if urls.count > 0
DispatchQueue.main.async
let url = urls[0]
do
let data = try Data(contentsOf: url)
let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .formatted(dateFormatter)
let jsonData = try decoder.decode(GeoFolderCodStruct.self, from: data)
self.geoFolder = jsonData
print("geoFolder: \(self.geoFolder.nomeArchivio)")
catch
print("error:\(error)")
func documentPickerWasCancelled(_ controller: UIDocumentPickerViewController)
controller.dismiss(animated: true)
print("Cancel from picker view controller")
//文件:GeoFolderReadFileView
import SwiftUI
struct GeoFolderReadFileView: View
@ObservedObject var picker = PickerForReadFile()
@Binding var geoFolder: GeoFolderCodStruct
var body: some View
VStack(alignment: .trailing)
Button(action:
#if targetEnvironment(macCatalyst)
print("Press open file")
let vc = UIApplication.shared.windows[0].rootViewController!
vc.present(self.picker.viewController, animated: true)
self.geoFolder = self.picker.geoFolder
#endif
)
Image(systemName: "book")
urlPickedView()
.hidden()
private func urlPickedView() -> some View
DispatchQueue.main.async
let geoF = self.picker.geoFolder
print("Committente: \(geoF.nomeCommittente) - Archivio: \(geoF.nomeArchivio)")
self.geoFolder = geoF
return TextField("", text: $geoFolder.geoFolderPath)
//文件:内容视图
import SwiftUI
struct ContentView: View
@State private var geoFolder = GeoFolderCodStruct()
var body: some View
VStack
HStack
Text("Open GeoFolder File:")
.padding()
Spacer()
GeoFolderReadFileView(geoFolder: $geoFolder)
.padding()
.padding()
Group
TextField("", text: $geoFolder.geoFolderPath)
.textFieldStyle(RoundedBorderTextFieldStyle())
.padding()
TextField("", text: $geoFolder.nomeCartella)
.textFieldStyle(RoundedBorderTextFieldStyle())
.padding()
TextField("", text: $geoFolder.nomeCommittente)
.textFieldStyle(RoundedBorderTextFieldStyle())
.padding()
TextField("", text: $geoFolder.nomeArchivio)
.textFieldStyle(RoundedBorderTextFieldStyle())
.padding()
TextField("", text: $geoFolder.note)
.textFieldStyle(RoundedBorderTextFieldStyle())
.padding()
.padding()
struct ContentView_Previews: PreviewProvider
static var previews: some View
ContentView()
最后,读取用于测试代码的 json 文件。
"nomeCommittente" : "Appple",
"note" : "Image from Cupertino (CA).",
"latitudine" : 37.332161,
"longitudine" : -122.030352,
"nomeCartella" : "Foto",
"geoFolderPath" : "\/Users\/cesare\/Desktop",
"radiusCircle" : 50,
"dataCreazione" : "20\/03\/2020",
"nomeArchivio" : "AppleCampus-Image",
"isActive" : true
我无法实施@Mdoyle1 提出的解决方案。我希望有人可以编辑代码以使其正常工作,而无需创建隐藏视图。
【讨论】:
以上是关于使用 DocumentPicker SwiftUI 更改状态的主要内容,如果未能解决你的问题,请参考以下文章
(Swift) 如何获取 documentPicker URL 的数据表示
SWIFT - NSCocoaErrorDomain,无法从 documentPicker 打开文档
Xamarin iOS DocumentPicker:如何导入大文件?
Expo大作战(三十九)--expo sdk api之 DocumentPicker,Contacts(获取手机联系人信息),Branch