【问题标题】:My iOS app crashes trying to display a UIActivityViewController [duplicate]我的 iOS 应用程序崩溃尝试显示 UIActivityViewController [重复]
【发布时间】:2019-06-26 01:36:03
【问题描述】:

基本上,我有一个应用程序,它接受用户输入(在警报控制器中的 5 个文本字段中),这些输入全部连接到一个字符串中,形成一个看起来像是 csv 文件中的一行并存储到 coredata 的形式一个实体。该实体随后将显示在表格视图中。我希望能够导出 csv 文件以通过电子邮件发送到其他地方。所有这些都将在 iOS 设备本身中完成。

我已参考 (How to Export Core Data to CSV in Swift 3?) 中的代码来执行导出 coredata 条目的代码。我的应用程序能够成功构建。从 tableview 添加和删除项目都可以正常工作。直到我点击我的导出按钮,应用程序才会崩溃。

class ViewController: UITableViewController, NSFetchedResultsControllerDelegate {

    let cellId = "cellId"

    fileprivate lazy var fetchedResultsController: NSFetchedResultsController<AlarmItem> = {
        //create fetch request
        let fetchRequest: NSFetchRequest<AlarmItem> = AlarmItem.fetchRequest()

        //configure fetch request
        fetchRequest.sortDescriptors = [NSSortDescriptor(key: "alarmAttributes", ascending: true)]
        let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext

        let fetchedResultsController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: context, sectionNameKeyPath: nil, cacheName: nil)

        let managedObjectContext = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext

        fetchedResultsController.delegate = self

        return fetchedResultsController
    }()

    func controllerWillChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
        tableView.beginUpdates()
    }

    func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
        tableView.endUpdates()
    }

    func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange anObject: Any, at indexPath: IndexPath?, for type: NSFetchedResultsChangeType, newIndexPath: IndexPath?) {

        switch(type) {
        case .insert:
            if let indexPath = newIndexPath {
                tableView.insertRows(at: [indexPath], with: .fade)
            }
            break;
        case .delete:
            if let indexPath = indexPath {
                tableView.deleteRows(at: [indexPath], with: .fade)
            }
            break;
        case .update:

            if let indexPath = indexPath, let cell = tableView.cellForRow(at: indexPath) {
                configureCell(cell, at: indexPath)
            }
            break;
        case .move:
            if let indexPath = indexPath {
                tableView.deleteRows(at: [indexPath], with: .fade)
            }
            if let newIndexPath = newIndexPath {
                tableView.insertRows(at: [newIndexPath], with: .fade)
            }
            break;
            @unknown default:
                print("Something odd is happening")
            }
        }


        override func viewDidLoad() {
            super.viewDidLoad()


        override func viewWillAppear(_ animated: Bool) {
            super.viewWillAppear(animated)
            do {
                try fetchedResultsController.performFetch()
        } catch let err as NSError {
            print("Failed to fetch items", err)
        }
    }

        @objc func addAlarmItem(_ sender: AnyObject) {

        let alertController = UIAlertController(title: "Add New Item", message: "Please fill in the blanks", preferredStyle: .alert)
        let saveAction = UIAlertAction(title: "Save", style: .default) { [unowned self] action in

            //combined string of attributes
            let myStrings: [String] = alertController.textFields!.compactMap { $0.text }
            let myText = myStrings.joined(separator: ", ")

            self.save(myText)
            self.tableView.reloadData()
        }

        let cancelAction = UIAlertAction(title: "Cancel", style: .destructive, handler: nil)

        alertController.addTextField { (textField) in
            textField.placeholder = "Enter Name of Engineer"
        }
        alertController.addTextField { (textField) in
            textField.placeholder = "Enter Date of Alarm in DD/MM/YYYY"
        }
        alertController.addTextField { (textField) in
            textField.placeholder = "Enter Time of Alarm in 24h (eg: 2300)"
        }
        alertController.addTextField { (textField) in
            textField.placeholder = "Please indicate True/False (type True or False)"
        }
        alertController.addTextField { (textField) in
            textField.placeholder = "Insert comments (if any), or NIL"
        }


    func save(_ itemName: String) {
        guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else { return }
        let managedContext = appDelegate.persistentContainer.viewContext
        let entity = NSEntityDescription.entity(forEntityName: "AlarmItem", in: managedContext)!
        let item = NSManagedObject(entity: entity, insertInto: managedContext)
        item.setValue(itemName, forKey: "alarmAttributes")

        do {
            try managedContext.save()
            tableView.reloadData()

        } catch let err as NSError {
            print("Failed to save an item", err)
        }
    }


 @objc func exportCSV(_ sender: AnyObject) {
        exportDatabase()
    }

    func exportDatabase() {
        let exportString = createExportString()
        saveAndExport(exportString: exportString)
    }

    func saveAndExport(exportString: String) {
        let exportFilePath = NSTemporaryDirectory() + "itemlist.csv"
        let exportFileUrl = NSURL(fileURLWithPath: exportFilePath)
        FileManager.default.createFile(atPath: exportFilePath, contents: NSData() as Data, attributes: nil)
        var fileHandle: FileHandle? = nil

        do {
            fileHandle = try FileHandle(forUpdating: exportFileUrl as URL)
        } catch {
            print("filehandle has error")
        }

        if fileHandle != nil {
            fileHandle!.seekToEndOfFile()
            let csvData = exportString.data(using: String.Encoding.utf8, allowLossyConversion: false)
            fileHandle!.write(csvData!)
            fileHandle!.closeFile()

            let firstActivityItem = NSURL(fileURLWithPath: exportFilePath)
            let activityViewController : UIActivityViewController = UIActivityViewController(activityItems: [firstActivityItem], applicationActivities: nil)

            activityViewController.excludedActivityTypes = [
                UIActivity.ActivityType.assignToContact,
                UIActivity.ActivityType.saveToCameraRoll,
                UIActivity.ActivityType.postToFlickr,
                UIActivity.ActivityType.postToVimeo,
                UIActivity.ActivityType.postToTencentWeibo
            ]
            self.present(activityViewController, animated: true, completion: nil)
        }
    }

    func createExportString() -> String {
        var alarmAttributes: String?
        var export: String = NSLocalizedString("Engineer Name,Date of Alarm,Time of Alarm,True or False,Engineer Comments \n", comment: "")

        for (index, AlarmItem) in fetchedStatsArray.enumerated() {
            if index <= fetchedStatsArray.count - 1 {
                alarmAttributes = AlarmItem.value(forKey: "alarmAttributes") as! String?
                let alarmAttributeStrings = alarmAttributes
                export += "\(alarmAttributeStrings ?? "0") \n"
            }
        }
        print("the app will now print: \(export) ")
        return export
    }

        override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int{
            let sectionInfo = fetchedResultsController.sections![section]
            return sectionInfo.numberOfObjects
        }

        override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
            let cell = tableView.dequeueReusableCell(withIdentifier: cellId, for: indexPath)
            let alarmItem = fetchedResultsController.object(at: indexPath) as NSManagedObject
            cell.textLabel?.text = alarmItem.value(forKeyPath: "alarmAttributes") as? String
        return cell
    }

    func tableView(_ tableView: UITableView!, canEditRowAtIndexPath indexPath: NSIndexPath!) -> Bool {
        return true
     }

    overr ide func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        tableView.deselectRow(at: indexPath, animated: true)
     }

    override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
        guard editingStyle == .delete else { return }

    //fetch
            let toBeDeleted = fetchedResultsController.object(at: indexPath)

    //delete
        fetchedResultsController.managedObjectContext.delete(toBeDeleted)

        do {
            try fetchedResultsController.managedObjectContext.save()
        } catch let err as NSError {
        print("failed to save item", err)
        }

    }

    func tableView(_tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "AlarmItem", for: indexPath)
        configureCell(cell, at: indexPath)

        return cell
    }

    func configureCell(_ cell: UITableViewCell, at indexPath: IndexPath) {
        let alarmItem = fetchedResultsController.object(at: indexPath)

        //configure cell
        cell.textLabel?.text = alarmItem.value(forKeyPath: "alarmAttributes") as? String
   }
}

这会导致“线程 1:信号 SIGABRT”错误,但我在代码中找不到任何拼写错误。

这是第一个调用抛出堆栈:

完整的错误代码:

2019-06-26 10:04:33.843955+0800 TrueFalseAlarmV3[913:13287] libMobileGestalt MobileGestalt.c:890:此平台不支持 MGIsDeviceOneOfType。 该应用程序现在将打印:工程师姓名、报警日期、报警时间、真或假、工程师评论 2019-06-26 10:31:56.087370+0800 TrueFalseAlarmV3[913:13287] [MC] systemgroup.com.apple.configurationprofiles 路径的系统组容器是 /Users/danialaqil/Library/Developer/CoreSimulator/Devices/BF0A3A59-A660 -4F1D-B0FE-F0D226479D8D/data/Containers/Shared/SystemGroup/systemgroup.com.apple.configurationprofiles 2019-06-26 10:31:56.087928+0800 TrueFalseAlarmV3[913:13287] [MC] 从私人有效用户设置中读取。 2019-06-26 10:31:56.088526+0800 TrueFalseAlarmV3[913:18176] [MC] 过滤捆绑 ID 的邮件表帐户:imdadsl.TrueFalseAlarmV3,源帐户管理:1 2019-06-26 10:31:56.100398+0800 TrueFalseAlarmV3[913:18176] [MC] 过滤捆绑 ID 的邮件表帐户:imdadsl.TrueFalseAlarmV3,源帐户管理:2 2019-06-26 10:31:56.445312+0800 TrueFalseAlarmV3[913:13287] *** 由于未捕获的异常“NSGenericException”而终止应用程序,原因:“您的应用程序已呈现 UIActivityViewController ()。在其当前的 trait 环境中,具有此样式的 UIActivityViewController 的 modalPresentationStyle 是 UIModalPresentationPopover。您必须通过视图控制器的 popoverPresentationController 提供此弹出框的位置信息。您必须提供 sourceView 和 sourceRect 或 barButtonItem。如果在呈现视图控制器时不知道此信息,则可以在 UIPopoverPresentationControllerDelegate 方法 -prepareForPopoverPresentation 中提供它。 *** 首先抛出调用堆栈: (

throw 调用栈在上图中

【问题讨论】:

  • 您的错误是在呈现视图控制器时。鉴于堆栈跟踪,应该有更多的错误消息,它可能是关于未设置 barButtonItem 或 sourceVIew。
  • 对不起,这是什么意思?我的按钮代码是这样的:navigationItem.leftBarButtonItem = UIBarButtonItem(title: "Export CSV", style: .plain, target: self, action: #selector(exportCSV))
  • 您需要显示出现在堆栈跟踪之前的完整错误消息。请将错误粘贴为文本,而不是图片。
  • 我已经添加了错误。非常感谢您的帮助

标签: ios swift uiactivityviewcontroller


【解决方案1】:

在 iPad 上,UIActivityViewController 需要在弹出窗口中显示,否则应用程序将崩溃,see this answer 了解详情。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-05-22
    • 1970-01-01
    • 1970-01-01
    • 2015-04-17
    • 1970-01-01
    • 2018-04-30
    • 1970-01-01
    相关资源
    最近更新 更多