Swift项目中调用Excel打开内置资源文件报错求助
解决iOS中用Excel打开应用内生成的摊销表失败的问题
看起来你遇到的核心问题有两个:Bundle文件的只读限制和跨应用文件访问的沙盒隔离,下面一步步帮你解决:
问题分析
- 直接修改Bundle内的文件是错误的:iOS应用的主Bundle是只读区域,你虽然能通过XlsxReaderWriter写入,但修改不会持久化(重启应用就会恢复),而且其他应用(比如Excel)没有权限访问这个路径下的文件。
- 自定义URL Scheme的路径无效:你用
ms-excel:scheme传递的是Bundle内的路径,Excel应用无法突破沙盒访问你的应用内部文件,所以会提示无法打开。
解决方案
我们需要把模板文件复制到应用的Documents目录(可读写且支持文件分享),然后用系统提供的UIDocumentInteractionController来让Excel打开这个文件,这是iOS跨应用打开文件的标准方式。
第一步:修改Excel写入逻辑,使用Documents目录
把原来直接操作Bundle文件的代码改成先复制模板到Documents,再修改:
func createExcel() { // 1. 获取Bundle里的模板文件路径 guard let templatePath = Bundle.main.path(forResource: "AmortizationTable", ofType: "xlsx") else { print("找不到模板文件") return } // 2. 定义Documents目录下的目标文件路径 let documentsDir = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] let targetPath = "\(documentsDir)/AmortizationTable.xlsx" // 3. 如果目标文件不存在,从Bundle复制模板 if !FileManager.default.fileExists(atPath: targetPath) { do { try FileManager.default.copyItem(atPath: templatePath, toPath: targetPath) } catch { print("复制模板失败: \(error.localizedDescription)") return } } // 4. 打开Documents里的文件进行修改 guard let spreadsheet = BRAOfficeDocumentPackage.open(targetPath) else { print("无法打开目标文件") return } let worksheet = spreadsheet.workbook.worksheets[0] as! BRAWorksheet let cell = worksheet.cell(forCellReference: "A1") cell.value = "It's working!" // 5. 保存修改到Documents目录 spreadsheet.save() print(cell.value) print(cell.stringValue()) }
第二步:用UIDocumentInteractionController打开Excel文件
替换原来的didSelectRowAt逻辑,使用系统的文件交互控制器:
首先在你的ViewController里添加一个属性:
var documentInteractionController: UIDocumentInteractionController?
然后修改表格点击逻辑:
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { if indexPath.section == 3 && indexPath.row == 0 { // 获取Documents里的文件路径 let documentsDir = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] let filePath = "\(documentsDir)/AmortizationTable.xlsx" let fileURL = URL(fileURLWithPath: filePath) // 检查文件是否存在 guard FileManager.default.fileExists(atPath: filePath) else { print("摊销表文件不存在,请先生成") return } // 初始化文件交互控制器 documentInteractionController = UIDocumentInteractionController(url: fileURL) documentInteractionController?.delegate = self // 指定文件类型UTI,确保Excel能识别 documentInteractionController?.uti = "com.microsoft.excel.xlsx" // 弹出打开菜单,让用户选择Excel打开 if !documentInteractionController!.presentOpenInMenu(from: tableView.cellForRow(at: indexPath)!.frame, in: view, animated: true) { // 如果没有安装Excel,跳转到App Store下载 if let excelAppUrl = URL(string: "https://itunes.apple.com/us/app/microsoft-excel/id586683407?mt=8&uo=4") { UIApplication.shared.open(excelAppUrl) } } } }
最后实现UIDocumentInteractionControllerDelegate的必要方法(添加到ViewController的扩展里):
extension YourViewController: UIDocumentInteractionControllerDelegate { func documentInteractionControllerViewControllerForPreview(_ controller: UIDocumentInteractionController) -> UIViewController { return self } }
为什么这个方案可行?
- Documents目录:属于应用的可读写沙盒区域,修改的文件会持久化,而且通过
UIDocumentInteractionController可以安全地分享给其他应用。 - UIDocumentInteractionController:是iOS官方提供的跨应用文件交互工具,它会处理文件的临时共享权限,让Excel能合法访问你的应用生成的文件,避免了自定义URL Scheme的路径权限问题。
内容的提问来源于stack exchange,提问作者Daniel Espina
相关产品推荐
相关产品推荐

