iOS13 share sheet: how to set preview thumbnail when sharing UIImage

烂漫一生 提交于 2020-01-04 09:02:19

问题


The new share sheet on iOS13 shows a preview/thumbnail of the item being shared on its top left corner.

When sharing an UIImage using an UIActivityViewController I would expect a preview/thumbnail of the image being shared to be displayed there (like e.g. when sharing an image attached to the built in Mail app), but instead the share sheet is showing my app's icon.

What code/settings are required to show a thumbnail of the image being exported in the share sheet?

I have set up the UIActivityViewController as follows:

let image = UIImage(named: "test")!
let activityVC = UIActivityViewController(activityItems: [image], applicationActivities: nil)                          
activityVC.popoverPresentationController?.sourceView = self.view                            
self.present(activityVC, animated: true, completion: nil)

回答1:


Just pass the image urls to UIActivityViewController not the UIImage objects. For example:

let imageURLs: [URL] = self.prepareImageURLs()
let activityViewController = UIActivityViewController(activityItems: imageURLs, applicationActivities: nil)
self.present(activityViewController, animated: true, completion: nil)

You can see that the image name and the image properties are shown in the top of the UIActivityViewController. Hope it helps!




回答2:


Update:

As of iOS 13.2.2 the standard way seems to be working as expected (when passing image URL(s) to UIActivityViewController), see @tatsuki.dev 's answer (now set as accepted answer):

On iOS 13.0 that was still not the case:

Original Answer:

I finally was able to figure out a solution to this issue.

To display the preview/thumbnail of the image being shared in the share sheet on iOS 13 it is necessary to adopt the UIActivityItemSource protocol, including its new (iOS13) activityViewControllerLinkMetadata method.

Starting from the code posted in the question, these would be the required steps:

  1. Import the LinkPresentation framework:

    import LinkPresentation
    
  2. create an optional URL property in your UIViewController subclass

    var urlOfImageToShare: URL?
    
  3. Implement the UIActivityItemSource delegate methods as follows:

    extension YourViewController: UIActivityItemSource {
    
        func activityViewControllerPlaceholderItem(_ activityViewController: UIActivityViewController) -> Any {
            return UIImage() // an empty UIImage is sufficient to ensure share sheet shows right actions
        }
    
        func activityViewController(_ activityViewController: UIActivityViewController, itemForActivityType activityType: UIActivity.ActivityType?) -> Any? {
            return urlOfImageToShare
        }
    
        func activityViewControllerLinkMetadata(_ activityViewController: UIActivityViewController) -> LPLinkMetadata? {
            let metadata = LPLinkMetadata()
    
            metadata.title = "Description of image to share" // Preview Title
            metadata.originalURL = urlOfImageToShare // determines the Preview Subtitle
            metadata.url = urlOfImageToShare
            metadata.imageProvider = NSItemProvider.init(contentsOf: urlOfImageToShare)
            metadata.iconProvider = NSItemProvider.init(contentsOf: urlOfImageToShare)
    
            return metadata
        }
    }
    
  4. In the part of the code presenting the share sheet, the declaration of activityVC needs to be slightly changed. The activityItems parameter should be [self] instead of [image] as in the code posted in the question above:

    //let activityVC = UIActivityViewController(activityItems: [image], applicationActivities: nil)   
    let activityVC = UIActivityViewController(activityItems: [self] , applicationActivities: nil)
    

    This is necessary to have the UIActivityItemSource delegate methods declared above being called when presenting the share sheet.

    Also, before presenting activityVC we need to set the value of urlOfImageToShare (which is needed by the UIActivityItemSource delegate methods):

    urlOfImageToShare = yourImageURL // <<< update this to work with your code 
    

The above steps should suffice if your app is not sharing very small or transparent images. The result looks like this:

In my tests while researching about this topic however, I had issues when providing images to metadata.iconProvider which were small (threshold seems to be 40 points) or non-opaque (transparent).

It seems like iOS uses metadata.imageProvider to generate the preview image if metadata.iconProvider delivers an image smaller than 40 points.

Also, on an actual device (iPhone Xs Max running iOS 13.1.2), the image provided by metadata.iconProvider would be displayed in reduced size on the share sheet in case it was not opaque:

On Simulator (iOS 13.0) this was not the case.

To work around these limitations, I followed these additional steps to ensure the preview image is always opaque and at least 40 points in size:

  1. In the implementation of activityViewControllerLinkMetadata above, change the assignment of metadata.iconProvider as follows:

    //metadata.iconProvider = NSItemProvider.init(contentsOf: urlOfImageToShare)
    metadata.iconProvider = NSItemProvider.init(contentsOf: urlInTemporaryDirForSharePreviewImage(urlOfImageToShare))
    

    Method urlInTemporaryDirForSharePreviewImage returns an URL to an opaque and if necessary enlarged copy of the image being shared created in the temporary directory:

    func urlInTemporaryDirForSharePreviewImage(_ url: URL?) -> URL? {
        if let imageURL = url,
           let data = try? Data(contentsOf: imageURL),
           let image = UIImage(data: data) {
    
            let applicationTemporaryDirectoryURL = FileManager.default.temporaryDirectory
            let sharePreviewURL = applicationTemporaryDirectoryURL.appendingPathComponent("sharePreview.png")
    
            let resizedOpaqueImage = image.adjustedForShareSheetPreviewIconProvider()
    
            if let data = resizedOpaqueImage.pngData() {
                do {
                    try data.write(to: sharePreviewURL)
                    return sharePreviewURL
                } catch {
                    print ("Error: \(error.localizedDescription)")
                }
            }
        }
        return nil
    }
    

    The actual generation of the new image is done using the following extension:

    extension UIImage {
        func adjustedForShareSheetPreviewIconProvider() -> UIImage {
            let replaceTransparencyWithColor = UIColor.black // change as required
            let minimumSize: CGFloat = 40.0  // points
    
            let format = UIGraphicsImageRendererFormat.init()
            format.opaque = true
            format.scale = self.scale
    
            let imageWidth = self.size.width
            let imageHeight = self.size.height
            let imageSmallestDimension = max(imageWidth, imageHeight)
            let deviceScale = UIScreen.main.scale
            let resizeFactor = minimumSize * deviceScale  / (imageSmallestDimension * self.scale)
    
            let size = resizeFactor > 1.0
                ? CGSize(width: imageWidth * resizeFactor, height: imageHeight * resizeFactor)
                : self.size
    
            return UIGraphicsImageRenderer(size: size, format: format).image { context in
                let size = context.format.bounds.size
                replaceTransparencyWithColor.setFill()
                context.fill(CGRect(x: 0, y: 0, width: size.width, height: size.height))
                self.draw(in: CGRect(origin: .zero, size: size))
            }
        }
    }
    


来源:https://stackoverflow.com/questions/57850483/ios13-share-sheet-how-to-set-preview-thumbnail-when-sharing-uiimage

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!