无法让 UIDocumentBrowserController 在基于文档的应用程序中打开文档
Having Trouble Getting the UIDocumentBrowserController to open docs in a Document based app
我一直在开发一个新的基于文档的应用程序,并且对新的 UIDocumentBrowserController 感到非常高兴...尝试为文档浏览器推出我自己的解决方案 UI棘手!
我在创建文档后让浏览器打开文档时遇到一些问题。
现在发生的情况是,当我选择在文档浏览器中创建一个新文档时,尽管记录了一条错误消息,但仍按预期创建并打开了该文档。但是,关闭文档后,我无法立即或在后续启动时重新打开文件,即使显示了文档。然而,这里有一个奇怪的线索是,如果我在创建文档后停止 运行 应用程序,但没有向它添加新信息(触发保存周期),并且再次 运行 项目,我可以正确打开文件。这让我觉得文件的保存方式存在问题。
(注意:在这个阶段,我正在努力让本地 non/icloud 实现工作,然后再继续 icloud 实现。)
以下是将文档保存到磁盘(或至少大部分时间!)时代码中任何一点的错误消息:
2017-06-20 13:21:58.254938-0500 布道设计 2 iOS[22454:5000138] [默认] [错误] 无法获取项目文件的属性值:///Users/stevenhovater/Library/Developer/CoreSimulator/Devices/9A4364F2-B3A1-4AD9-B680-FB4BC876C707/data/Containers/Data/Application/DD534ED8-C4A3-40FE-9777-AED961976878/Documents/Untitled-9。讲道。错误:Error Domain=NSFileProviderInternalErrorDomain Code=1 "The reader is not permitted to access the URL." UserInfo={NSLocalizedDescription=The reader is not permitted to access the URL.}
我怀疑问题出在我的文档类型 plist 中,我试图通过模仿 wwdc 2017 session 229 视频中的设置来设置它。
我的文档由 NSData 对象封装,使用我认为是 UIDocument 的非常标准的子类实现。 (我省略了生成缩略图的代码)
override func contents(forType typeName: String) throws -> Any {
print("Saving Document Changes")
if sermon != nil {
let newData = NSKeyedArchiver.archivedData(withRootObject: sermon!)
return newData
} else {
let newData = NSKeyedArchiver.archivedData(withRootObject: Sermon())
return newData
}
}
override func fileAttributesToWrite(to url: URL, for saveOperation: UIDocumentSaveOperation) throws -> [AnyHashable : Any] {
let thumbnail:UIImage = self.createThumbnail()
let thumbnaildict = [URLThumbnailDictionaryItem.NSThumbnail1024x1024SizeKey : thumbnail]
let dict = [URLResourceKey.thumbnailDictionaryKey:thumbnaildict]
return dict
}
override func load(fromContents contents: Any, ofType typeName: String?) throws {
guard let newSermon:Sermon = NSKeyedUnarchiver.unarchiveObject(with: contents as! Data) as? Sermon else{
throw documentErrors.invalidFile
}
self.sermon = newSermon
}
在我的 UIDocumentBrowserViewController 子类中,这是我获取本地文件名和创建新文档的代码。
func documentBrowser(_ controller: UIDocumentBrowserViewController, didRequestDocumentCreationWithHandler importHandler: @escaping (URL?, UIDocumentBrowserViewController.ImportMode) -> Void) {
var newDocumentURL: URL? = nil
print("creating new local document")
guard let target = self.newLocalFilename() else {
return
}
let targetSuffix = target.lastPathComponent
let tempURL = URL(fileURLWithPath: NSTemporaryDirectory() + targetSuffix)
let newDocument:SDDocument = SDDocument(fileURL: tempURL)
newDocument.sermon = Sermon()
/
newDocument.save(to: tempURL, for: .forCreating) { (saveSuccess) in
/
guard saveSuccess else {
/
importHandler(nil, .none)
return
}
/
newDocument.close(completionHandler: { (closeSuccess) in
/
guard closeSuccess else {
/
importHandler(nil, .none)
return
}
/
importHandler(tempURL, .move)
})
}
}
func newLocalFilename() -> URL? {
let fileManager = FileManager()
guard let baseURL = self.localDocumentsDirectoryURL.appendingPathComponent("Untitled")
else {return nil}
var target = baseURL.appendingPathExtension(DocumentBrowserViewController.documentExtension)
var nameSuffix = 2
while fileManager.fileExists(atPath: target.path) {
target = URL(fileURLWithPath: baseURL.path + "-\(nameSuffix).\(DocumentBrowserViewController.documentExtension)")
nameSuffix += 1
}
let targetSuffix = target.lastPathComponent
print("Target name: \(targetSuffix)")
print("new url: \(target)")
return target
}
我在尝试保存到 NSTemporaryDirectory()
时遇到了完全相同的问题。
如果您改为保存到文档目录 ([[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] firstObject]
),它似乎工作正常!
更新:看起来这个问题在 iOS 11 beta 3 中已修复,您现在可以将新创建的文档正确保存到 NSTemporaryDirectory()
。
这是我目前的理论。
这个错误
Error Domain=NSFileProviderInternalErrorDomain Code=1 "The reader is not permitted to access the URL."
首次使用 -initWithFileURL 在新 URL 创建 UIDocument 时显示。它基本上是在说 "this URL doesn't exist yet," 但在某种程度上让它听起来更像是一个权限问题。
据我所知,它不会阻止您保存、打开、编辑或关闭文件。所以我认为这只是一个多此一举的错误,Apple 应该剔除掉。
经过四五个小时的努力解决这个问题,我发现了一个简单的解决方案:不要在模拟器中测试。我切换到在我的设备上进行测试,一切立即开始按照广告宣传的那样工作。
[我不能根据这里的经验发表意见,但可能 "doesn't work in the Simulator" 问题仅限于 Sierra,但模拟器在 High Sierra 中确实有效。这可以解释为什么一些用户看到这个问题而其他人没有,尤其是为什么 Apple 在 WWDC 视频中似乎没有意识到这一点。]
我发现当 LSSupportsOpeningDocumentsInPlace
属性 在 info.plist 中设置为 YES 时,模拟器会发生错误。
将此 属性 设为 NO,然后它开始工作,以我为例。
在真实设备上,它仍然可以正常运行,没有错误。
有类似的问题,尝试了在Schemes设置中提供默认Core Location的方法,现在可以了。该答案中提到了该方法:
我一直在开发一个新的基于文档的应用程序,并且对新的 UIDocumentBrowserController 感到非常高兴...尝试为文档浏览器推出我自己的解决方案 UI棘手!
我在创建文档后让浏览器打开文档时遇到一些问题。
现在发生的情况是,当我选择在文档浏览器中创建一个新文档时,尽管记录了一条错误消息,但仍按预期创建并打开了该文档。但是,关闭文档后,我无法立即或在后续启动时重新打开文件,即使显示了文档。然而,这里有一个奇怪的线索是,如果我在创建文档后停止 运行 应用程序,但没有向它添加新信息(触发保存周期),并且再次 运行 项目,我可以正确打开文件。这让我觉得文件的保存方式存在问题。 (注意:在这个阶段,我正在努力让本地 non/icloud 实现工作,然后再继续 icloud 实现。)
以下是将文档保存到磁盘(或至少大部分时间!)时代码中任何一点的错误消息: 2017-06-20 13:21:58.254938-0500 布道设计 2 iOS[22454:5000138] [默认] [错误] 无法获取项目文件的属性值:///Users/stevenhovater/Library/Developer/CoreSimulator/Devices/9A4364F2-B3A1-4AD9-B680-FB4BC876C707/data/Containers/Data/Application/DD534ED8-C4A3-40FE-9777-AED961976878/Documents/Untitled-9。讲道。错误:Error Domain=NSFileProviderInternalErrorDomain Code=1 "The reader is not permitted to access the URL." UserInfo={NSLocalizedDescription=The reader is not permitted to access the URL.}
我怀疑问题出在我的文档类型 plist 中,我试图通过模仿 wwdc 2017 session 229 视频中的设置来设置它。
override func contents(forType typeName: String) throws -> Any {
print("Saving Document Changes")
if sermon != nil {
let newData = NSKeyedArchiver.archivedData(withRootObject: sermon!)
return newData
} else {
let newData = NSKeyedArchiver.archivedData(withRootObject: Sermon())
return newData
}
}
override func fileAttributesToWrite(to url: URL, for saveOperation: UIDocumentSaveOperation) throws -> [AnyHashable : Any] {
let thumbnail:UIImage = self.createThumbnail()
let thumbnaildict = [URLThumbnailDictionaryItem.NSThumbnail1024x1024SizeKey : thumbnail]
let dict = [URLResourceKey.thumbnailDictionaryKey:thumbnaildict]
return dict
}
override func load(fromContents contents: Any, ofType typeName: String?) throws {
guard let newSermon:Sermon = NSKeyedUnarchiver.unarchiveObject(with: contents as! Data) as? Sermon else{
throw documentErrors.invalidFile
}
self.sermon = newSermon
}
在我的 UIDocumentBrowserViewController 子类中,这是我获取本地文件名和创建新文档的代码。
func documentBrowser(_ controller: UIDocumentBrowserViewController, didRequestDocumentCreationWithHandler importHandler: @escaping (URL?, UIDocumentBrowserViewController.ImportMode) -> Void) {
var newDocumentURL: URL? = nil
print("creating new local document")
guard let target = self.newLocalFilename() else {
return
}
let targetSuffix = target.lastPathComponent
let tempURL = URL(fileURLWithPath: NSTemporaryDirectory() + targetSuffix)
let newDocument:SDDocument = SDDocument(fileURL: tempURL)
newDocument.sermon = Sermon()
/
newDocument.save(to: tempURL, for: .forCreating) { (saveSuccess) in
/
guard saveSuccess else {
/
importHandler(nil, .none)
return
}
/
newDocument.close(completionHandler: { (closeSuccess) in
/
guard closeSuccess else {
/
importHandler(nil, .none)
return
}
/
importHandler(tempURL, .move)
})
}
}
func newLocalFilename() -> URL? {
let fileManager = FileManager()
guard let baseURL = self.localDocumentsDirectoryURL.appendingPathComponent("Untitled")
else {return nil}
var target = baseURL.appendingPathExtension(DocumentBrowserViewController.documentExtension)
var nameSuffix = 2
while fileManager.fileExists(atPath: target.path) {
target = URL(fileURLWithPath: baseURL.path + "-\(nameSuffix).\(DocumentBrowserViewController.documentExtension)")
nameSuffix += 1
}
let targetSuffix = target.lastPathComponent
print("Target name: \(targetSuffix)")
print("new url: \(target)")
return target
}
我在尝试保存到 NSTemporaryDirectory()
时遇到了完全相同的问题。
如果您改为保存到文档目录 ([[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] firstObject]
),它似乎工作正常!
更新:看起来这个问题在 iOS 11 beta 3 中已修复,您现在可以将新创建的文档正确保存到 NSTemporaryDirectory()
。
这是我目前的理论。
这个错误
Error Domain=NSFileProviderInternalErrorDomain Code=1 "The reader is not permitted to access the URL."
首次使用 -initWithFileURL 在新 URL 创建 UIDocument 时显示。它基本上是在说 "this URL doesn't exist yet," 但在某种程度上让它听起来更像是一个权限问题。
据我所知,它不会阻止您保存、打开、编辑或关闭文件。所以我认为这只是一个多此一举的错误,Apple 应该剔除掉。
经过四五个小时的努力解决这个问题,我发现了一个简单的解决方案:不要在模拟器中测试。我切换到在我的设备上进行测试,一切立即开始按照广告宣传的那样工作。
[我不能根据这里的经验发表意见,但可能 "doesn't work in the Simulator" 问题仅限于 Sierra,但模拟器在 High Sierra 中确实有效。这可以解释为什么一些用户看到这个问题而其他人没有,尤其是为什么 Apple 在 WWDC 视频中似乎没有意识到这一点。]
我发现当 LSSupportsOpeningDocumentsInPlace
属性 在 info.plist 中设置为 YES 时,模拟器会发生错误。
将此 属性 设为 NO,然后它开始工作,以我为例。
在真实设备上,它仍然可以正常运行,没有错误。
有类似的问题,尝试了在Schemes设置中提供默认Core Location的方法,现在可以了。该答案中提到了该方法: