Is there a delegate method that gets called before the UIDocumentInteractionController share sheet pop ups

Please can someone help.I would like to add an alert message once a user clicks on the share button on a pdf viewer, i'm using UIDocumentInteractionController to preview the pdf document. and i wanted to know if there are any delegate methods or functions that i can override, where i can add my alert before opening the sharing sheet?[![enter image description here][1]][1]

class ViewController: UIViewController {
    
    var documentController : UIDocumentInteractionController!

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
    }

    @IBAction private func buttonTapped(_ sender: Any) {
        
       guard let fileURL = Bundle.main.url(forResource: "infomation", withExtension: "pdf") else {return}
        documentController = UIDocumentInteractionController.init(url: fileURL)
        documentController.delegate = self
        documentController.presentPreview(animated: true)
    }
}

extension ViewController: UIDocumentInteractionControllerDelegate {
    func documentInteractionControllerViewControllerForPreview(_ controller: UIDocumentInteractionController) -> UIViewController {
        
        return self
    }
    
    func documentInteractionControllerWillPresentOpenInMenu(_ controller: UIDocumentInteractionController) {
  
    }
    
    func documentInteractionControllerWillPresentOptionsMenu(_ controller: UIDocumentInteractionController) {
    
    }
    
    func documentInteractionController(_ controller: UIDocumentInteractionController, willBeginSendingToApplication application: String?) {
    
    }
}

but none of them get called when i click on the share button, even though i have set the delegate to be my view controller.is there a way that i can do this ?


Solution 1:

Explanation:

There isn't a way to do that without hacking it together by iterating all the sub-views and override the button's action and target.

One clean way is to create your own preview controller like shown below. If you add the QLPreviewController to UINavigationController, it will automatically create a bottom toolbar which you don't want. To work around this, you add it as a child controller of a regular UIViewController and create a custom navigation bar.

You can also use the UTI for the UIActivityItem to determine how the item can be shared. I've only implemented the preview and basic sharing capabilities, but the rest should be super easy to do, and at least it's more customizable than UIDocumentInteractionController since you have full control of everything.

Now for the code that does all of this:

//
//  ViewController.swift
//  CustomDocumentController
//
//  Created by brandon on 2022-01-13.
//

import UIKit
import QuickLook
import MobileCoreServices
import UniformTypeIdentifiers

private class PreviewItem: NSObject, QLPreviewItem {
    var previewItemURL: URL?
    var previewItemTitle: String?
    
    init(title: String, url: URL) {
        super.init()
        
        previewItemURL = url
        previewItemTitle = title
    }
}

class DocumentInteractionController: UIViewController {
    
    private let url: URL
    
    private lazy var navigationBar: UINavigationBar = {
        let navigationBar = UINavigationBar()
        let navigationItem = UINavigationItem(title: name ?? "Document")
        navigationItem.hidesBackButton = true
        navigationItem.leftBarButtonItem = UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(onDone))
        navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .action, target: self, action: #selector(onShare)) //UIImage(systemName: "square.and.arrow.up")
        navigationBar.pushItem(navigationItem, animated: false)
        return navigationBar
    }()
    
    private lazy var previewController: QLPreviewController = {
        let previewController = QLPreviewController()
        previewController.title = name
        previewController.dataSource = self
        previewController.delegate = self
        previewController.reloadData()
        return previewController
    }()
    
    var uti: String? {
        didSet {
            previewController.reloadData()
        }
    }
    
    var name: String? {
        didSet {
            previewController.title = name
            navigationBar.topItem?.title = name
        }
    }
    
    init(url: URL) {
        self.url = url
        super.init(nibName: nil, bundle: nil)
        //super.init(rootViewController: self.previewController)
        self.modalPresentationStyle = .fullScreen
        
        name = (try? url.resourceValues(forKeys: [.localizedNameKey]))?.localizedName
        uti = (try? url.resourceValues(forKeys: [.typeIdentifierKey]))?.typeIdentifier
        
        if uti == nil {
            if #available(iOS 15.0, *) {
                uti = UTType.url.identifier
            } else {
                uti = String(kUTTypeURL)
            }
        }
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()

        addChild(previewController)
        previewController.didMove(toParent: self)
        
        let separator = UIView()
        separator.backgroundColor = .lightGray
        
        view.addSubview(navigationBar)
        view.addSubview(separator)
        view.addSubview(previewController.view)
        
        NSLayoutConstraint.activate([
            navigationBar.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor),
            navigationBar.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor),
            navigationBar.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
            
            separator.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor),
            separator.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor),
            separator.topAnchor.constraint(equalTo: navigationBar.bottomAnchor),
            separator.heightAnchor.constraint(equalToConstant: 1.0 / UIScreen.main.scale),
            
            previewController.view.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor),
            previewController.view.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor),
            previewController.view.topAnchor.constraint(equalTo: separator.bottomAnchor),
            previewController.view.bottomAnchor.constraint(equalTo: view.bottomAnchor)
        ])
        
        [navigationBar, separator, previewController.view].forEach({
            $0?.translatesAutoresizingMaskIntoConstraints = false
        })
        
        navigationBar.barTintColor = previewController.view.backgroundColor
        view.backgroundColor = previewController.view.backgroundColor
    }
}

extension DocumentInteractionController: QLPreviewControllerDelegate, QLPreviewControllerDataSource {
    func numberOfPreviewItems(in controller: QLPreviewController) -> Int {
        return 1
    }
    
    func previewController(_ controller: QLPreviewController, previewItemAt index: Int) -> QLPreviewItem {
        return PreviewItem(title: name ?? "Document", url: url)
    }
}

extension DocumentInteractionController {
    @objc
    private func onDone() {
        self.dismiss(animated: true, completion: nil)
    }
    
    @objc
    private func onShare() {
        let activityViewController = UIActivityViewController(activityItems: [url],
                                                              applicationActivities: nil)
        
        activityViewController.excludedActivityTypes = [.assignToContact]
        if UIDevice.current.userInterfaceIdiom == .pad {
            activityViewController.popoverPresentationController?.sourceView = self.view
        }
        self.present(activityViewController, animated: true, completion: nil)
    }
}

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        
        let url = Bundle.main.url(forResource: "Testing", withExtension: ".md")
        let doc = DocumentInteractionController(url: url!)
        
        DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) {
            self.present(doc, animated: true, completion: nil)
        }
    }
}

Screenshots:

Image-1 Image-2