【问题标题】:Change State with DocumentPicker SwiftUI使用 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...

【问题讨论】:

    标签: view binding swiftui uidocumentpickervc


    【解决方案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 提出的解决方案。我希望有人可以编辑代码以使其正常工作,而无需创建隐藏视图。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2023-02-01
        • 2021-05-04
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多