繁体   English   中英

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

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

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

当使用 UIActivityViewController 共享 UIImage 时,我希望共享图像的预览/缩略图显示在那里(例如共享附加到内置邮件应用程序的图像时),但共享表显示的是我的应用程序图标。

需要什么代码/设置才能在共享表中显示正在导出的图像的缩略图?

我已经设置了 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)

我实现的最简单的代码用于共享具有更好用户体验的UIImage

  1. 导入 LinkPresentation 框架:
#import <LinkPresentation/LPLinkMetadata.h>  // for Obj-C

import LinkPresentation  // for Swift, below
  1. UIViewController显示 UIActivityViewController ,使用[image, self]
let image = UIImage(named: "YourImage")!
let share = UIActivityViewController(activityItems: [image, self], applicationActivities: nil)
present(share, animated: true, completion: nil)
  1. 使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

由于它共享UIImage ,因此不应期望任何 URL。 否则用户可能会获得 URL 共享,而不是图像共享体验。

要加速共享表预览, LPLinkMetadata使用现有资源提供LPLinkMetadata对象。 无需再次在线获取。 查看 WWDC19 技术讲座视频共享中的新功能了解更多详细信息。

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

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

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

更新:

从 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

如果您的应用没有共享非常小的或透明的图像,上述步骤就足够了。 结果如下所示:

然而,在我研究这个主题的测试中,我在向 metadata.iconProvider 提供很小(阈值似乎是 40 点)或不透明(透明)的图像时遇到了问题。

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

此外,在实际设备(运行 iOS 13.1.2 的 iPhone Xs Max)上,metadata.iconProvider 提供的图像将在共享表上以缩小的尺寸显示,以防它不透明:

在模拟器 (iOS 13.0) 上,情况并非如此。

为了解决这些限制,我遵循了以下附加步骤以确保预览图像始终不透明且大小至少为 40 磅:

  1. 在上面activityViewControllerLinkMetadata的实现中,改变metadata.iconProvider的赋值如下:

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

    方法 urlInTemporaryDirForSharePreviewImage 返回一个 URL,该 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)) } } }

此代码仅适用于 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()
    }
  }
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM