iOS13共享表:共享UIImage时如何设置预览缩略图

时间:2019-09-09 08:39:39

标签: ios uiactivityviewcontroller ios13

iOS13上的新共享表在其左上角显示了正在共享的项目的预览/缩略图。

使用UIActivityViewController共享UIImage时,我希望在此处显示正在共享的图像的预览/缩略图(例如,例如,共享附加到内置Mail应用程序的图像时),但是共享页却显示了我的应用程序的图标。

显示共享表中要导出的图像的缩略图需要哪些代码/设置?

我已经如下设置了UIActivityViewController:

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

4 个答案:

答案 0 :(得分:22)

我为共享-----BEGIN PGP SIGNED MESSAGE-----\r\nHash: SHA1\r\n\r\n�\u0002\u000e\u0003��?>����\u0010\a�5�Z2>:\u0014h�̌\u0011M$Ua\b\u0018*����X��һ\u0013\n�\u001a�~s� |�m��Z����\f\n\u0011�4\a�\u0015۶��r�\u001c�1\b\f�yǟ\u000ex���(��\u007f�^\0�z�}��,\f,�s��v\tR����*��,s�ѱ��2^Lh\u0012�x\u001aw�,�a_a���y&3=������j����4l5@�=��V����ۻS���˷\u001bo\u0003�xs\u0004����(��c�]7�s_�M�\u0003��4W&��u\u001a��2<W\0�؛�\u001d�\u0006dS�R�\u000f���-\u001d�@��\u0019K'��V\u0012t�~�]��>XH�^�l\b\0�zK��'��Z\n_�iS���U�o�S�L���2�u#�}\u0012�E��#WR4��(eD�\\���F��Hr��GX\u001a��K��\u0019vjU�Ȋ��m\b�����{rXr��*��n��j�l��%��\u0015X�J��\r\v�7R\u0012[�(�B����q�?)HrQ����\u0018�\0��C��\u0014��ځ\u007f��n/�b�/��\u0018���1a�g[\u0014Ѝ*u/�\"�\u0003z8w��Sa��\u0005\vG���~ħl���F� \u0001��&�D�ތ��_�Y�0 b\r�?#\u0017\u001b\u0019;��&\n�@E�Q���h�\u001e\u0003\u0002��9҈\u0001E�U&��̚�\rwp���V �\u0004\u001c�C��U\u0004�n�\aX��\tڂ��Q�K\u001a\u0015���\v�B\u0006Pƅ��\"3'�GF��]�D��7�E��gېzHF����TԐb�8�<\u0005\u000f�nW|��_���?t�ˎt�\u0004��0h݁}\0�\u001d\u001e���\u0015r�O\f�-----BEGIN PGP SIGNATURE-----\r\nVersion: BCPG C# v1.8.5.0\r\n\r\niEYEARECAAYFAl5OftYACgkQflHkxBbbB4WI4gCffVMNrdwTl2G/OiDA5jd1Fjqc\r\nkmoAnis0W579VZ+fI28eGpM7OrKW" 而实现的最简单的代码,具有更好的用户体验:

  1. 导入LinkPresentation框架:
UIImage
  1. 使用#import <LinkPresentation/LPLinkMetadata.h> // for Obj-C import LinkPresentation // for Swift, below UIViewController中显示UIActivityViewController:
[image, self]
  1. 使let image = UIImage(named: "YourImage")! let share = UIActivityViewController(activityItems: [image, self], applicationActivities: nil) present(share, animated: true, completion: nil) 符合UIViewController
UIActivityItemSource

由于func activityViewControllerPlaceholderItem(_ activityViewController: UIActivityViewController) -> Any { return "" } func activityViewController(_ activityViewController: UIActivityViewController, itemForActivityType activityType: UIActivity.ActivityType?) -> Any? { return nil } func activityViewControllerLinkMetadata(_ activityViewController: UIActivityViewController) -> LPLinkMetadata? { let image = UIImage(named: "YourImage")! let imageProvider = NSItemProvider(object: image) let metadata = LPLinkMetadata() metadata.imageProvider = imageProvider return metadata } 已符合UIImage,因此只需将其用于NSItemProviderWriting

由于它共享一个NSItemProvider,因此不应包含任何URL。否则,用户可能会获得URL共享,而不是图像共享体验。

要加速共享表预览,请向UIImage对象提供现有资源。无需再次在线获取。有关更多详细信息,请查看WWDC19技术讲座视频What's New in Sharing

答案 1 :(得分:3)

只需将图像URL传递到UIActivityViewController对象而不是UIImage对象。 例如:

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

您可以看到图像名称和图像属性显示在UIActivityViewController的顶部。希望对您有帮助!

答案 2 :(得分:1)

此代码仅适用于 iOS 13 作为最低目标。我添加了一个代码示例,以便在 SwiftUI 视图中使用共享按钮,以防其他人需要它。此代码也适用于 iPad。

您可以使用此类 LinkMetadataManager 并添加您选择的图像。非常重要的部分是,您必须在项目目录中放置图像,而不是在 Assets.xcassets 文件夹中。否则,它将无法工作。

当一切都设置好后,您将在 SwiftUI 视图中以这种方式使用按钮。

struct ContentView: View {
    
  var body: some View {
    VStack {
      ShareButton()
    }
  }
}

这是将与 Apple Store 链接共享您的应用程序的类。你可以分享任何你想要的。您可以使用 LPLinkMetadata 查看图片是如何添加的,因为它是您感兴趣的部分。

import LinkPresentation

//  MARK: LinkMetadataManager
/// Transform url to metadata to populate to user.
///
final class LinkMetadataManager: NSObject,  UIActivityItemSource {

  var linkMetadata: LPLinkMetadata

  let appTitle = "Your application name"
  let appleStoreProductURL = "https://apps.apple.com/us/app/num8r/id1497392799"  // The url of your app in Apple Store
  let iconImage = "appIcon"  // The name of the image file in your directory
  let png = "png"  // The extension of the image

  init(linkMetadata: LPLinkMetadata = LPLinkMetadata()) {
    self.linkMetadata = linkMetadata
  }
}

// MARK: - Setup
extension LinkMetadataManager {
  /// Creating metadata to population in the share sheet.
  ///
  func activityViewControllerLinkMetadata(_ activityViewController: UIActivityViewController) -> LPLinkMetadata? {

    guard let url = URL(string: appleStoreProductUR) else { return linkMetadata }

    linkMetadata.originalURL = url
    linkMetadata.url = linkMetadata.originalURL
    linkMetadata.title = appTitle
    linkMetadata.iconProvider = NSItemProvider(
      contentsOf: Bundle.main.url(forResource: iconImage, withExtension: png))

    return linkMetadata
  }

  /// Showing empty string returns a share sheet with the minimum requirement.
  ///
  func activityViewControllerPlaceholderItem(_ activityViewController: UIActivityViewController) -> Any {
    return String()
  }

  /// Sharing url of the application.
  ///
  func activityViewController(_ activityViewController: UIActivityViewController,
                              itemForActivityType activityType: UIActivity.ActivityType?) -> Any? {
    return linkMetadata.url
  }
}

使用 View 的这个扩展在 SwiftUI 视图上触发共享表。

import SwiftUI

//  MARK: View+ShareSheet
extension View {

  /// Populate Apple share sheet to enable user to share Apple Store link.
  ///
  func showAppShareSheet() {
    guard let source = UIApplication.shared.windows.first?.rootViewController else {
      return
    }

    let activityItemMetadata = LinkMetadataManager()

    let activityVC = UIActivityViewController(
      activityItems: [activityItemMetadata],
      applicationActivities: nil)

    if let popoverController = activityVC.popoverPresentationController {
      popoverController.sourceView = source.view
      popoverController.permittedArrowDirections = []
      popoverController.sourceRect = CGRect(x: source.view.bounds.midX,
                                            y: source.view.bounds.midY,
                                            width: .zero, height: .zero)
    }
    source.present(activityVC, animated: true)
  }
}

然后,创建一个 ShareButton 作为组件以在您的任何 SwiftUI 视图中使用它。这就是 ContentView 中使用的内容。

import SwiftUI

//  MARK: ShareButton
/// Share button to send app store link using
/// the Apple classic share screen for iPhone
/// and iPad.
///
struct ShareButton: View {

  @Environment(\.horizontalSizeClass) private var horizontalSizeClass

  var body: some View {
    ZStack {
      Button(action: { showAppShareSheet() }) {
        Image(systemName: "square.and.arrow.up")
          .font(horizontalSizeClass == .compact ? .title2 : .title)
          .foregroundColor(.accentColor)
      }
      .padding()
    }
  }
}

答案 3 :(得分:0)

更新

从iOS 13.2.2开始,标准方式似乎按预期方式工作(将图像URL传递到UIActivityViewController时),请参阅@ tatsuki.dev的答案(现已设置为接受的答案):

在iOS 13.0上并非如此:

原始答案:

我终于能够找到解决这个问题的方法。

要在iOS 13的共享表中显示正在共享的图像的预览/缩略图,必须采用UIActivityItemSource协议,包括其新的(iOS13)activityViewControllerLinkMetadata方法。

从问题中发布的代码开始,这些是必需的步骤:

  1. 导入LinkPresentation框架:

    import LinkPresentation
    
  2. 在您的UIViewController子类中创建一个可选的URL属性

    var urlOfImageToShare: URL?
    
  3. 实现UIActivityItemSource委托方法如下:

    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. 在呈现股份单的代码部分中,需要稍微更改activityVC的声明。 activityItems参数应为[self],而不是上述问题中发布的代码中的[image]:

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

    在展示共享表时,必须调用上面声明的UIActivityItemSource委托方法。

    此外,在展示activityVC之前,我们需要设置urlOfImageToShare的值(UIActivityItemSource委托方法需要此值):

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

如果您的应用没有共享很小或透明的图像,则上述步骤就足够了。结果看起来像这样:

     

     

但是,在研究此主题的测试中,将图像提供给meta.iconProvider时,图像很小(阈值似乎是40点)或不透明(透明)。

     

如果metadata.iconProvider提供的图像小于40点,则iOS似乎使用metas.imageProvider生成预览图像。

     

此外,在实际设备(运行iOS 13.1.2的iPhone Xs Max)上,由元数据.iconProvider提供的图像在共享表上的显示尺寸也会减小,以防不透明:

     

     

在Simulator(iOS 13.0)上并非如此。

     

要解决这些限制,我按照以下附加步骤操作,以确保预览图像始终不透明并且大小至少为40点:

  1. 在上面的activityViewControllerLinkMetadata的实现中,如下更改metadata.iconProvider的分配:

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

    方法urlInTemporaryDirForSharePreviewImage将URL返回到不透明的图像,并在必要时在临时目录中创建要共享的图像的放大副本:

    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
    }
    

    新图像的实际生成是使用以下扩展名完成的:

    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))
            }
        }
    }