一樣先來看官方文件
A view controller that provides access to documents or destinations outside your app’s sandbox.
其實就是讓你去讀取檔案App的東西
有兩種模式,Don’t copy the document if you can avoid it. 原則上是不需要就不要特別copy一份:
也分兩種(四種?)
打開檔案:操作原本/copy的檔案
init(forOpeningContentTypes: [UTType])
Creates and returns a document picker that can open the types of documents you specify.init(forOpeningContentTypes: [UTType], asCopy: Bool)
Creates and returns a document picker that can open or copy the types of documents you specify.使用這兩個需先 import UniformTypeIdentifiers
,不然會有錯誤
輸出到指定位置:操作原本/copy的檔案
init(forExporting: [URL])
Creates and returns a document picker that can export the types of documents you specify.init(forExporting: [URL], asCopy: Bool)
Creates and returns a document picker that can export or copy the types of documents you specify.下面的範例都是用第一種forOpeningContentTypes
來講。
選擇操作原本檔案,開始存取前,需加上 url.startAccessingSecurityScopedResource(),結束操作後url.stopAccessingSecurityScopedResource()。
如果操作原本的檔案又沒加上權限,會取不到檔案。
使用init(forOpeningContentTypes: [UTType])
瀏覽檔案
// 打開documentPicker
@IBAction func openFile(_ sender: UIButton) {
let documenPickerVC = UIDocumentPickerViewController(forOpeningContentTypes: [.content])
documenPickerVC.delegate = self
self.present(documenPickerVC, animated: true)
}
❌ 沒有加上權限存取,讀不到檔案。這邊注意一定要用實機測試,模擬器兩種都不需要權限,會讓你以為天下太平= =
extension ViewController: UIDocumentPickerDelegate {
func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) {
do {
for url in urls {
// 直接操作檔案
let filename = url.lastPathComponent
let resourceValue = try url.resourceValues(forKeys: [.fileSizeKey])
filenameLabel.text = "檔案名稱:\(filename)"
fileSizeLabel.text = "檔案大小:\(resourceValue.fileSize ?? 0)"
}
} catch {
print(error.localizedDescription)
}
}
}
The file “ppt檔.pptx” couldn’t be opened because you don’t have permission to view it.
✅正確的寫法
extension ViewController: UIDocumentPickerDelegate {
func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) {
do {
for url in urls {
let accessing = url.startAccessingSecurityScopedResource()
defer {
if accessing {
url.stopAccessingSecurityScopedResource( }
}
// ....操作檔案
另外,有打開權限就要關掉,沒關掉的會可能會造成leak,之後app就沒辦法在存取檔案裡的東西,除非重開。所以最佳寫法就是stopAccessingSecurityScopedResource寫在defer {}
,跑完一定會關掉。
使用init(forOpeningContentTypes: [UTType], asCopy: Bool)
copy一份的方式瀏覽檔案
// 打開documentPicker
@IBAction func openFile(_ sender: UIButton) {
let documenPickerVC = UIDocumentPickerViewController(forOpeningContentTypes: [.content], asCopy: true)
documenPickerVC.delegate = self
self.present(documenPickerVC, animated: true)
}
✅ 直接操作檔案
extension ViewController: UIDocumentPickerDelegate {
func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) {
do {
for url in urls {
// 直接操作檔案
let filename = url.lastPathComponent
let resourceValue = try url.resourceValues(forKeys: [.fileSizeKey])
filenameLabel.text = "檔案名稱:\(filename)"
fileSizeLabel.text = "檔案大小:\(resourceValue.fileSize ?? 0)"
}
} catch {
print(error.localizedDescription)
}
}
}
我個人偏好跟官方講的一樣,如果沒有特殊需求直接操作原本的檔案就好,不會想要再copy一份,雖然copy方式code比較少😂
forOpeningContentTypes: [UTType]
這個參數,可以讓你限制使用者可以選擇的檔案,不能選擇的會泛灰不能點選,但這東西我覺得極為難用。
因為有被他收編的很輕易就可以寫出來,例如:.jpeg, .png,輕鬆優雅的寫在UTType array裡。
let documenPickerVC = UIDocumentPickerViewController(forOpeningContentTypes: [.jpeg, .png])
但沒被他收編的。例如:微軟word檔、ppt檔。需要自己去找對照表id轉成UTType,非常醜且可怕,而且對照表還莫名的很難搜尋到。
let documentPicker = UIDocumentPickerViewController(forOpeningContentTypes: [UTType("com.microsoft.powerpoint.ppt")!, UTType("com.microsoft.word.doc")!])
好在iOS14之後他出了一個比較優雅的做法,用fileExtension去判斷type。不過如果使用者亂改副檔名的話,這個判斷結果可能會失準,需自行斟酌。
let acceptTypes = [UTType(filenameExtension: "ppt"), UTType(filenameExtension: "doc")].compactMap{$0}
let documenPickerVC = UIDocumentPickerViewController(forOpeningContentTypes: acceptTypes)