使用较小的 UIImage 为 UIActivityViewController 设置缩略图

Posted

技术标签:

【中文标题】使用较小的 UIImage 为 UIActivityViewController 设置缩略图【英文标题】:Set thumbnail image for UIActivityViewController with smaller UIImage 【发布时间】:2019-08-06 15:23:34 【问题描述】:

我想设置一个较小的特定缩略图,但仍为我正在分享的活动项目使用高质量的图像。我的问题是分享大图时,生成缩略图需要时间,这可能会在分享到某些应用时出现问题。

例如,如果我展示我的 UIActivity 控制器并选择 Messenger,

我没有时间让我的缩略图加载,选择一个人并点击发送,它永远不会完成发送,我需要取消。

只有等待缩略图生成,然后点击发送,一切都会正常进行。

在这种情况下,“照片”是高质量的UIImage,但是如何设置缩略图以避免在UIActivityViewController出现时生成一个高质量的图像呢?

let activity = UIActivityViewController(activityItems: [photo], applicationActivities: nil)

UIApplication.topViewController?.present(activity, animated: true, completion: nil)

有一个关于缩略图的文档,但我不确定如何实现它,或者在这种情况下是否有帮助:https://developer.apple.com/documentation/uikit/uiactivityitemsource/1620462-activityviewcontroller

另外,我阅读了 UIActivityItemProvider,它指出:

例如,您可以使用提供者对象来表示需要处理的大型视频文件,然后才能将其共享到用户的社交媒体帐户。

这是否意味着它可以推迟显示我想与之共享图像的应用程序,直到它准备好?我继承了 UIActivityItemProvider,并传递了一个缩略图,但它似乎没有任何效果。我要么做错了什么,要么这不是我想要使用的。

我的子类 UIActivityItemProvider:

class CustomProvider : UIActivityItemProvider 

    var image : UIImage!

    init(placeholderItem: AnyObject, image : UIImage) 
        super.init(placeholderItem: placeholderItem)
        self.image = image
    

    override var item: Any 
        return self.image!
    

我有一个共享功能,它是一个扩展。我创建了一个缩略图图像并将其传递给 CustomProvider 函数中的 placeholderItem:

extension Equatable 
    func share() 

        let imageData = (self as! UIImage).pngData()!
        let options = [
            kCGImageSourceCreateThumbnailWithTransform: true,
            kCGImageSourceCreateThumbnailFromImageAlways: true,
            kCGImageSourceThumbnailMaxPixelSize: 300] as CFDictionary
        let source = CGImageSourceCreateWithData(imageData as CFData, nil)!
        let imageReference = CGImageSourceCreateThumbnailAtIndex(source, 0, options)!
        let thumbnail = UIImage(cgImage: imageReference)


        let firstActivityItem = CustomProvider(placeholderItem: thumbnail, image: self as! UIImage)

        let activity = UIActivityViewController(activityItems: [firstActivityItem], applicationActivities: nil)

        UIApplication.topViewController?.present(activity, animated: true, completion: nil)
    

【问题讨论】:

为什么不先创建一个较小的UIImage 图像,同时等待创建另一个图像?当然,如果用户点击send,你将不得不等待更大的图像被创建。因此,在任何情况下,您仍在等待创建图像。问题是 - 您是要允许用户按“我想发送一些东西”并最初显示较小的图像,还是等到所有内容都准备好加载?无论哪种方式,您都在等待图像准备好。 是的,我不介意让他们等。我只是想让缩略图出现在那里,但最终会发送更大的图像。但是我该怎么做呢? 好吧,这取决于;您是从应用程序中创建图像还是从互联网获取图像?如果您使用 AVAssetWriter 或其他东西从您的应用程序中创建图像,则创建 2 个图像。第一个小得多(只是缩略图大小),另一个是原始的。使用较小的缩略图,原件会放在某个地方。如果来自网络,那么(如果您要存储自己的图像)存储 2 张图像,其中一张尺寸要小得多。 然而,我的问题是,用户必须等待任何人发送“原始”图像,那么为什么不让他们等待发送图像,直到创建原始图像? 图片是在我的应用程序中制作的,而不是互联网。缩略图似乎是在选择活动时创建的,比如 Facebook Messenger,所以我无法控制事先这样做。请参见上面的代码。我可以给它一系列活动项目。如果该数组中的图像表示缩略图,我不知道它是哪一个。它似乎使用了该数组中首先提供的任何图像。 【参考方案1】:

ActivityViewController 为您完成工作。

func activityViewController(activityViewController: UIActivityViewController, thumbnailImageForActivityType activityType: String?, suggestedSize size: CGSize) -> UIImage? 
    let maxSize = [size.width, size.height].max()
    let imageData = (self as! UIImage).pngData()!
    let options = [
            kCGImageSourceCreateThumbnailWithTransform: true,
            kCGImageSourceCreateThumbnailFromImageAlways: true,
            kCGImageSourceThumbnailMaxPixelSize: maxSize as Any] as CFDictionary
    let source = CGImageSourceCreateWithData(imageData as CFData, nil)!
    let imageReference = CGImageSourceCreateThumbnailAtIndex(source, 0, options)!
    let thumbnail = UIImage(cgImage: imageReference)
    return thumbnail

let activity = UIActivityViewController(activityItems: [photo], applicationActivities: nil)

UIApplication.topViewController?.present(activity, animated: true, completion: nil)

我认为您不需要特定的ActivityType,所以这是假设所有活动。

另外,请确保扩展 UIActivityItemSource

我使用this SO Post 作为参考。

编辑1

子类UIActivityItemSource

class MyActivityType:NSObject, UIActivityItemSource 

    var image:UIImage!
    var placeholderImage:UIImage!

    convenience init(image:UIImage, placeholderImage:UIImage) 
        self.init()
        self.image = image
        self.placeholderImage = placeholderImage
    

    //Placeholder - we use `return UIImage()` so the sheet knows it is an image
    func activityViewControllerPlaceholderItem(_ activityViewController: UIActivityViewController) -> Any 
        return self.placeholderImage
    

    //Actual item you are sending
    func activityViewController(_ activityViewController: UIActivityViewController, itemForActivityType activityType: UIActivity.ActivityType?) -> Any? 
        return self.image
    

    //Thumbnail - Apple states that `For activities that support a preview image, returns a thumbnail preview image for the item.` but since you are pushing an image for your item, should be good to go.
    func activityViewController(_ activityViewController: UIActivityViewController, thumbnailImageForActivityType activityType: UIActivity.ActivityType?, suggestedSize size: CGSize) -> UIImage? 
        return self.placeholderImage
    


我没有收到任何错误 - 但我无法运行它,所以我没有看到任何运行时错误。

然后你就可以这样发送了--

DispatchQueue.global(qos: .userInitiated).async  [weak self] in

    let imageData = (self! as! UIImage).pngData()!
    let options = [
        kCGImageSourceCreateThumbnailWithTransform: true,
        kCGImageSourceCreateThumbnailFromImageAlways: true,
        kCGImageSourceThumbnailMaxPixelSize: 300] as CFDictionary
    let source = CGImageSourceCreateWithData(imageData as CFData, nil)!
    let imageReference = CGImageSourceCreateThumbnailAtIndex(source, 0, options)!
    let thumbnail = UIImage(cgImage: imageReference)

    //Back to main thread
    DispatchQueue.main.async  [weak self] in

        //Remove loading indicator
        let firstActivityItem = MyActivityType(image: (self! as! UIImage), placeholderImage: thumbnail)

        let activity = UIActivityViewController(activityItems: [firstActivityItem], applicationActivities: nil)

        UIApplication.topViewController?.present(activity, animated: true, completion: nil)
    

【讨论】:

【参考方案2】:

您可以在应用程序中生成低质量图像并在 UIActivityViewController 中引用该 url

let activity = UIActivityViewController(
activityItems: ["Some description", url],
applicationActivities: nil)

您可以使用以下方法生成可以稍后保存在本地的低质量图像:

NSData * UIImageJPEGRepresentation(UIImage *image, CGFloat compressionQuality);

... 或参考以下帖子了解更多技术知识和方法: http://vocaro.com/trevor/blog/2009/10/12/resize-a-uiimage-the-right-way/

学分。从以下位置提取的材料:

https://www.raywenderlich.com/813044-uiactivityviewcontroller-tutorial-sharing-data https://developer.apple.com/documentation/uikit/1624115-uiimagejpegrepresentation https://thomasguenzel.com/blog/2015/04/16/uiactivityviewcontroller-nsdata-with-filename/

【讨论】:

那不是永远不会发送高质量的图像吗?这似乎只是发送低质量的图像。 对不起。我误解了“Messenger 降低已发送图像的质量或大小超出您的控制范围是可以的”要求。因为你对质量下降很好。 但是developer.apple.com/documentation/uikit/uiactivityitemsource/…(你分享的链接)似乎微不足道。只需继承 UIActivityViewController 并将其设置为 UIActivityItemSource(与 UITableViewControllers 数据源相同)。然后在上述方法的实现中,返回一个缩小版 无法避免向 Messenger 发送高质量的大照片后质量下降。这发生在他们身上。当您发送一张大照片并且不等待照片的方形缩略图出现在模态视图中时,就会出现问题。我确认这是某些应用程序(包括 Messenger)的问题,并且现在正在与 Facebook 团队联系。

以上是关于使用较小的 UIImage 为 UIActivityViewController 设置缩略图的主要内容,如果未能解决你的问题,请参考以下文章

使用 Python 按行号将大文本文件拆分为较小的文本文件

scss 使用Partials将您的样式拆分为较小的块

将较小的 UIWebView 显示为表单

将庞大的 sql 表逻辑拆分为较小的表

将大减速器拆分为较小的减速器

使用 PHP 将大型 KML 文件拆分为较小的文件