簡體   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