[英]How to locate an app to share to in iOS13 share sheet via UI test?
[英]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
:
#import <LinkPresentation/LPLinkMetadata.h> // for Obj-C
import LinkPresentation // for Swift, below
UIViewController
显示 UIActivityViewController ,使用[image, self]
: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
。
由于它共享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 方法。
从问题中发布的代码开始,这些将是必需的步骤:
导入 LinkPresentation 框架:
import LinkPresentation
在 UIViewController 子类中创建一个可选的 URL 属性
var urlOfImageToShare: URL?
实现 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 } }
在呈现共享表的代码部分,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 磅:
在上面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.