Swift - 自动将 pdf 文件保存到我的 iPhone 上的文件应用程序
Swift - Automatically save pdf file to Files app On My iPhone
我非常需要帮助。
我正在尝试创建一个允许用户从 link 保存 pdf 文件的应用程序,例如我在 subjectLinks 数组中给出的示例,所有这些 link 都指向一个 pdf 页面,我正在尝试下载它并将其保存在我的应用程序中。到目前为止,我已经到处搜索并找到了一种使用文件应用程序来完成它的方法,所以我在代码中所做的是下载 pdf 的数据并使用 UIDocument 和 presentPreview 打开它来显示它,我已经设法允许用户共享下载的文件并将其保存到文件。
但是问题出现了,因为我想这样做,以便当用户单击下载时,文件会自动保存到目录中的“文件”应用程序中,这样用户就不需要单击选项按钮然后选择“保存到”文件,然后查找保存位置。这可能吗??
如果这不可能,至少当用户选择选项按钮并单击“保存到文件”时,它会自动创建一个单独的目录,用户可以在其中查看和保存 pdf 文件?
I want to do this as most times, when Save to Files is chosen, saving 'On My iPhone' is not available as there is no directory or such present so it can only be saved to google驱动器或 iCloud Drive,这是一个很大的不便。
抱歉这么久 post。但如果有人能帮助解决我的问题,我将不胜感激。提前非常感谢您:)
P.S 到目前为止,我的代码中的所有内容都运行良好,只是我完全不知道如何实现上面概述的功能?
import UIKit
import StoreKit
class TableViewController: UITableViewController {
let documentInteractionController = UIDocumentInteractionController()
let subjectLinks = ["https://pastpapers.papacambridge.com/Cambridge%20International%20Examinations%20(CIE)/AS%20and%20A%20Level/Accounting%20(9706)/2015%20Jun/9706_s15_qp_42.pdf", "https://pastpapers.papacambridge.com/Cambridge%20International%20Examinations%20(CIE)/AS%20and%20A%20Level/Economics%20(9708)/2017%20Jun/9708_s17_qp_12.pdf", "https://pastpapers.papacambridge.com/Cambridge%20International%20Examinations%20(CIE)/AS%20and%20A%20Level/Mathematics%20(9709)/2018-May-June/9709_s18_qp_12.pdf"]
override func viewDidLoad() {
super.viewDidLoad()
documentInteractionController.delegate = self as? UIDocumentInteractionControllerDelegate
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return subjectLinks.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
cell.textLabel?.text = subjectLinks[indexPath.row]
return cell
}
override func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath) -> [UITableViewRowAction]?
{
// 1
let shareAction = UITableViewRowAction(style: UITableViewRowActionStyle.default, title: "Download" , handler: { (action:UITableViewRowAction, indexPath: IndexPath) -> Void in
// 2
let downloadMenu = UIAlertController(title: nil, message: "Download this paper", preferredStyle: .actionSheet)
let cancelAction = UIAlertAction(title: "Cancel", style: UIAlertActionStyle.cancel, handler: nil)
downloadMenu.addAction(UIAlertAction(title: "Download", style: UIAlertActionStyle.destructive, handler: { action in self.storeAndShare(withURLString: self.subjectLinks[indexPath.row])}))
downloadMenu.addAction(cancelAction)
self.present(downloadMenu, animated: true, completion: nil)
})
// 3
let rateAction = UITableViewRowAction(style: UITableViewRowActionStyle.default, title: "Rate" , handler: { (action:UITableViewRowAction, indexPath:IndexPath) -> Void in
// 4
let rateMenu = UIAlertController(title: nil, message: "Rate this App", preferredStyle: .actionSheet)
let appRateAction = UIAlertAction(title: "Rate", style: UIAlertActionStyle.default, handler: {action in SKStoreReviewController.requestReview()})
let cancelAction = UIAlertAction(title: "Cancel", style: UIAlertActionStyle.cancel, handler: nil)
rateMenu.addAction(appRateAction)
rateMenu.addAction(cancelAction)
self.present(rateMenu, animated: true, completion: nil)
})
// 5
return [shareAction,rateAction]
}
}
extension TableViewController {
/// This function will set all the required properties, and then provide a preview for the document
func share(url: URL) {
documentInteractionController.url = url
documentInteractionController.uti = url.typeIdentifier ?? "public.data, public.content"
documentInteractionController.name = url.localizedName ?? url.lastPathComponent
documentInteractionController.presentPreview(animated: true)
}
/// This function will store your document to some temporary URL and then provide sharing, copying, printing, saving options to the user
func storeAndShare(withURLString: String) {
guard let url = URL(string: withURLString) else { return }
/// START YOUR ACTIVITY INDICATOR HERE
URLSession.shared.dataTask(with: url) { data, response, error in
guard let data = data, error == nil else { return }
let fileManager = FileManager.default
do {
let documentDirectory = try fileManager.url(for: .documentDirectory, in: .userDomainMask, appropriateFor:nil, create:false)
let fileURL = documentDirectory.appendingPathComponent("fileName.pdf")
try data.write(to: fileURL)
DispatchQueue.main.async {
self.share(url: fileURL)
}
} catch {
print(error)
}
}.resume()
}
}
extension TableViewController: UIDocumentInteractionControllerDelegate {
/// If presenting atop a navigation stack, provide the navigation controller in order to animate in a manner consistent with the rest of the platform
func documentInteractionControllerViewControllerForPreview(_ controller: UIDocumentInteractionController) -> UIViewController {
guard let navVC = self.navigationController else {
return self
}
return navVC
}
}
下载任何 pdf 文件并自动保存在 iPhone 文件夹内的示例。
let urlString = "https://www.tutorialspoint.com/swift/swift_tutorial.pdf"
let url = URL(string: urlString)
let fileName = String((url!.lastPathComponent)) as NSString
//Mark: Create destination URL
let documentsUrl:URL = (FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first as URL?)!
let destinationFileUrl = documentsUrl.appendingPathComponent("\(fileName)")
//Mark: Create URL to the source file you want to download
let fileURL = URL(string: urlString)
let sessionConfig = URLSessionConfiguration.default
let session = URLSession(configuration: sessionConfig)
let request = URLRequest(url:fileURL!)
let task = session.downloadTask(with: request) { (tempLocalUrl, response, error) in
if let tempLocalUrl = tempLocalUrl, error == nil {
//Mark: Success
if let statusCode = (response as? HTTPURLResponse)?.statusCode {
print("Successfully downloaded. Status code: \(statusCode)")
}
do {
try FileManager.default.copyItem(at: tempLocalUrl, to: destinationFileUrl)
do {
//Mark: Show UIActivityViewController to save the downloaded file
let contents = try FileManager.default.contentsOfDirectory(at: documentsUrl, includingPropertiesForKeys: nil, options: .skipsHiddenFiles)
for indexx in 0..<contents.count {
if contents[indexx].lastPathComponent == destinationFileUrl.lastPathComponent {
let activityViewController = UIActivityViewController(activityItems: [contents[indexx]], applicationActivities: nil)
self.present(activityViewController, animated: true, completion: nil)
}
}
}
catch (let err) {
print("error: \(err)")
}
} catch (let writeError) {
print("Error creating a file \(destinationFileUrl) : \(writeError)")
}
} else {
print("Error took place while downloading a file. Error description: \(error?.localizedDescription ?? "")")
}
}
task.resume()
第 1 步:需要在 info.plist
中添加权限
<key>UIFileSharingEnabled</key>
<true/>
<key>LSSupportsOpeningDocumentsInPlace</key>
<true/>
第 2 步:从服务器下载文档 url[任何文档]
func downloadPdf(sender:UIButton) {
DispatchQueue.main.async {
//Do UI Code here.
let pdfUrl = self.myOrderListModel?.myorderList?[sender.tag].invoiceUrl ?? ""
guard let fileURL = URL(string: pdfUrl) else { return }
var originalUrlStr : String = ""
print(fileURL.pathExtension)
if fileURL.pathExtension == ""{
originalUrlStr = pdfUrl + ".pdf"
} else {
originalUrlStr = pdfUrl
}
guard let originalUrl = URL(string: originalUrlStr) else { return }
let urlSession = URLSession(configuration: .default, delegate: self, delegateQueue: OperationQueue())
let downloadTask = urlSession.downloadTask(with: originalUrl)
downloadTask.resume()
}
}
extension InvoiceViewController: URLSessionDownloadDelegate {
func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) {
print("File Downloaded Location- ", location)
guard let url = downloadTask.originalRequest?.url else {
return
}
let docsPath = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask)[0]
let destinationPath = docsPath.appendingPathComponent(url.lastPathComponent)
try? FileManager.default.removeItem(at: destinationPath)
do{
try FileManager.default.copyItem(at: location, to: destinationPath)
print("File Downloaded Location- ", destinationPath)
DispatchQueue.main.async {
let urlString: String = destinationPath.absoluteString
self.saveInvoiceToDevice(filePath: urlString)
}
}catch let error {
print("Copy Error: \(error.localizedDescription)")
}
}
}
步骤 #3:将下载的文件保存在设备“文件”文件夹中
func saveInvoiceToDevice(filePath : String) {
let fileURL = URL(string: filePath)
if FileManager.default.fileExists(atPath: fileURL!.path){
let url = URL(fileURLWithPath: fileURL!.path)
let activityViewController: UIActivityViewController = UIActivityViewController(activityItems: [url], applicationActivities: nil)
activityViewController.popoverPresentationController?.sourceView=self.view
//If user on iPad
if UIDevice.current.userInterfaceIdiom == .pad {
if activityViewController.responds(to: #selector(getter: UIViewController.popoverPresentationController)) {
}
}
self.present(activityViewController, animated: true, completion: nil)
}
else {
debugPrint("document was not found")
}
}
我非常需要帮助。
我正在尝试创建一个允许用户从 link 保存 pdf 文件的应用程序,例如我在 subjectLinks 数组中给出的示例,所有这些 link 都指向一个 pdf 页面,我正在尝试下载它并将其保存在我的应用程序中。到目前为止,我已经到处搜索并找到了一种使用文件应用程序来完成它的方法,所以我在代码中所做的是下载 pdf 的数据并使用 UIDocument 和 presentPreview 打开它来显示它,我已经设法允许用户共享下载的文件并将其保存到文件。
但是问题出现了,因为我想这样做,以便当用户单击下载时,文件会自动保存到目录中的“文件”应用程序中,这样用户就不需要单击选项按钮然后选择“保存到”文件,然后查找保存位置。这可能吗??
如果这不可能,至少当用户选择选项按钮并单击“保存到文件”时,它会自动创建一个单独的目录,用户可以在其中查看和保存 pdf 文件?
I want to do this as most times, when Save to Files is chosen, saving 'On My iPhone' is not available as there is no directory or such present so it can only be saved to google驱动器或 iCloud Drive,这是一个很大的不便。
抱歉这么久 post。但如果有人能帮助解决我的问题,我将不胜感激。提前非常感谢您:)
P.S 到目前为止,我的代码中的所有内容都运行良好,只是我完全不知道如何实现上面概述的功能?
import UIKit
import StoreKit
class TableViewController: UITableViewController {
let documentInteractionController = UIDocumentInteractionController()
let subjectLinks = ["https://pastpapers.papacambridge.com/Cambridge%20International%20Examinations%20(CIE)/AS%20and%20A%20Level/Accounting%20(9706)/2015%20Jun/9706_s15_qp_42.pdf", "https://pastpapers.papacambridge.com/Cambridge%20International%20Examinations%20(CIE)/AS%20and%20A%20Level/Economics%20(9708)/2017%20Jun/9708_s17_qp_12.pdf", "https://pastpapers.papacambridge.com/Cambridge%20International%20Examinations%20(CIE)/AS%20and%20A%20Level/Mathematics%20(9709)/2018-May-June/9709_s18_qp_12.pdf"]
override func viewDidLoad() {
super.viewDidLoad()
documentInteractionController.delegate = self as? UIDocumentInteractionControllerDelegate
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return subjectLinks.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
cell.textLabel?.text = subjectLinks[indexPath.row]
return cell
}
override func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath) -> [UITableViewRowAction]?
{
// 1
let shareAction = UITableViewRowAction(style: UITableViewRowActionStyle.default, title: "Download" , handler: { (action:UITableViewRowAction, indexPath: IndexPath) -> Void in
// 2
let downloadMenu = UIAlertController(title: nil, message: "Download this paper", preferredStyle: .actionSheet)
let cancelAction = UIAlertAction(title: "Cancel", style: UIAlertActionStyle.cancel, handler: nil)
downloadMenu.addAction(UIAlertAction(title: "Download", style: UIAlertActionStyle.destructive, handler: { action in self.storeAndShare(withURLString: self.subjectLinks[indexPath.row])}))
downloadMenu.addAction(cancelAction)
self.present(downloadMenu, animated: true, completion: nil)
})
// 3
let rateAction = UITableViewRowAction(style: UITableViewRowActionStyle.default, title: "Rate" , handler: { (action:UITableViewRowAction, indexPath:IndexPath) -> Void in
// 4
let rateMenu = UIAlertController(title: nil, message: "Rate this App", preferredStyle: .actionSheet)
let appRateAction = UIAlertAction(title: "Rate", style: UIAlertActionStyle.default, handler: {action in SKStoreReviewController.requestReview()})
let cancelAction = UIAlertAction(title: "Cancel", style: UIAlertActionStyle.cancel, handler: nil)
rateMenu.addAction(appRateAction)
rateMenu.addAction(cancelAction)
self.present(rateMenu, animated: true, completion: nil)
})
// 5
return [shareAction,rateAction]
}
}
extension TableViewController {
/// This function will set all the required properties, and then provide a preview for the document
func share(url: URL) {
documentInteractionController.url = url
documentInteractionController.uti = url.typeIdentifier ?? "public.data, public.content"
documentInteractionController.name = url.localizedName ?? url.lastPathComponent
documentInteractionController.presentPreview(animated: true)
}
/// This function will store your document to some temporary URL and then provide sharing, copying, printing, saving options to the user
func storeAndShare(withURLString: String) {
guard let url = URL(string: withURLString) else { return }
/// START YOUR ACTIVITY INDICATOR HERE
URLSession.shared.dataTask(with: url) { data, response, error in
guard let data = data, error == nil else { return }
let fileManager = FileManager.default
do {
let documentDirectory = try fileManager.url(for: .documentDirectory, in: .userDomainMask, appropriateFor:nil, create:false)
let fileURL = documentDirectory.appendingPathComponent("fileName.pdf")
try data.write(to: fileURL)
DispatchQueue.main.async {
self.share(url: fileURL)
}
} catch {
print(error)
}
}.resume()
}
}
extension TableViewController: UIDocumentInteractionControllerDelegate {
/// If presenting atop a navigation stack, provide the navigation controller in order to animate in a manner consistent with the rest of the platform
func documentInteractionControllerViewControllerForPreview(_ controller: UIDocumentInteractionController) -> UIViewController {
guard let navVC = self.navigationController else {
return self
}
return navVC
}
}
下载任何 pdf 文件并自动保存在 iPhone 文件夹内的示例。
let urlString = "https://www.tutorialspoint.com/swift/swift_tutorial.pdf"
let url = URL(string: urlString)
let fileName = String((url!.lastPathComponent)) as NSString
//Mark: Create destination URL
let documentsUrl:URL = (FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first as URL?)!
let destinationFileUrl = documentsUrl.appendingPathComponent("\(fileName)")
//Mark: Create URL to the source file you want to download
let fileURL = URL(string: urlString)
let sessionConfig = URLSessionConfiguration.default
let session = URLSession(configuration: sessionConfig)
let request = URLRequest(url:fileURL!)
let task = session.downloadTask(with: request) { (tempLocalUrl, response, error) in
if let tempLocalUrl = tempLocalUrl, error == nil {
//Mark: Success
if let statusCode = (response as? HTTPURLResponse)?.statusCode {
print("Successfully downloaded. Status code: \(statusCode)")
}
do {
try FileManager.default.copyItem(at: tempLocalUrl, to: destinationFileUrl)
do {
//Mark: Show UIActivityViewController to save the downloaded file
let contents = try FileManager.default.contentsOfDirectory(at: documentsUrl, includingPropertiesForKeys: nil, options: .skipsHiddenFiles)
for indexx in 0..<contents.count {
if contents[indexx].lastPathComponent == destinationFileUrl.lastPathComponent {
let activityViewController = UIActivityViewController(activityItems: [contents[indexx]], applicationActivities: nil)
self.present(activityViewController, animated: true, completion: nil)
}
}
}
catch (let err) {
print("error: \(err)")
}
} catch (let writeError) {
print("Error creating a file \(destinationFileUrl) : \(writeError)")
}
} else {
print("Error took place while downloading a file. Error description: \(error?.localizedDescription ?? "")")
}
}
task.resume()
第 1 步:需要在 info.plist
中添加权限 <key>UIFileSharingEnabled</key>
<true/>
<key>LSSupportsOpeningDocumentsInPlace</key>
<true/>
第 2 步:从服务器下载文档 url[任何文档]
func downloadPdf(sender:UIButton) {
DispatchQueue.main.async {
//Do UI Code here.
let pdfUrl = self.myOrderListModel?.myorderList?[sender.tag].invoiceUrl ?? ""
guard let fileURL = URL(string: pdfUrl) else { return }
var originalUrlStr : String = ""
print(fileURL.pathExtension)
if fileURL.pathExtension == ""{
originalUrlStr = pdfUrl + ".pdf"
} else {
originalUrlStr = pdfUrl
}
guard let originalUrl = URL(string: originalUrlStr) else { return }
let urlSession = URLSession(configuration: .default, delegate: self, delegateQueue: OperationQueue())
let downloadTask = urlSession.downloadTask(with: originalUrl)
downloadTask.resume()
}
}
extension InvoiceViewController: URLSessionDownloadDelegate {
func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) {
print("File Downloaded Location- ", location)
guard let url = downloadTask.originalRequest?.url else {
return
}
let docsPath = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask)[0]
let destinationPath = docsPath.appendingPathComponent(url.lastPathComponent)
try? FileManager.default.removeItem(at: destinationPath)
do{
try FileManager.default.copyItem(at: location, to: destinationPath)
print("File Downloaded Location- ", destinationPath)
DispatchQueue.main.async {
let urlString: String = destinationPath.absoluteString
self.saveInvoiceToDevice(filePath: urlString)
}
}catch let error {
print("Copy Error: \(error.localizedDescription)")
}
}
}
步骤 #3:将下载的文件保存在设备“文件”文件夹中
func saveInvoiceToDevice(filePath : String) {
let fileURL = URL(string: filePath)
if FileManager.default.fileExists(atPath: fileURL!.path){
let url = URL(fileURLWithPath: fileURL!.path)
let activityViewController: UIActivityViewController = UIActivityViewController(activityItems: [url], applicationActivities: nil)
activityViewController.popoverPresentationController?.sourceView=self.view
//If user on iPad
if UIDevice.current.userInterfaceIdiom == .pad {
if activityViewController.responds(to: #selector(getter: UIViewController.popoverPresentationController)) {
}
}
self.present(activityViewController, animated: true, completion: nil)
}
else {
debugPrint("document was not found")
}
}