FB Messenger 和 iMessage 的 UIActivityViewController 中透明 PNG 的问题
Issues with transparent PNG in UIActivityViewController for FB Messenger and iMessage
我一直在使用以下代码调用 UIActivityViewController 以通过各种社交媒体应用程序共享贴纸:
if let image = sticker.getUIImage(), let imgData = UIImagePNGRepresentation(image) {
let activityVC = UIActivityViewController(activityItems: [imgData], applicationActivities: nil)
activityVC.popoverPresentationController?.sourceView = gesture.view
self.present(activityVC, animated: true, completion: nil)
} else {
ErrorHandler.handleError(STICKER_IMAGE_NOT_FOUND, sticker)
}
在最近更新 FB Messenger(版本 98.0)之前,此代码一直运行良好。现在它显示错误 "Couldn't load content"。 FB Messenger 似乎更喜欢这样的 URL:
if let item = sticker.getImageURL() {
let activityVC = UIActivityViewController(activityItems: [item], applicationActivities: nil)
activityVC.popoverPresentationController?.sourceView = gesture.view
self.present(activityVC, animated: true, completion: nil)
} else {
ErrorHandler.handleError(STICKER_IMAGE_NOT_FOUND, sticker)
}
这适用于 FB Messenger,但 iMessage 显示透明的 PNG,背景为黑色。
我正在查看 UIActivityViewControllerCompletionWithItemsHandler,但讨论指出它在 activity 之后运行,对于我需要做的事情来说太晚了。我还尝试为 activityType 创建一个返回 UIActivityType.message 的自定义 UIActivity,但它被添加到控制器的底部而不是接管默认值。
有没有办法拦截 UIActivityViewController 中的项目选择,以便我可以使用 MFMessageComposeViewController 并将 UIImagePNGRepresentation 添加到消息中,并允许所有其他人使用 URL?
是否有我可以传递给 UIActivityViewController 的特定参数类型,它会在所有社交应用程序中正确显示透明 PNG?
TIA
麦克
绕回来结束这个。我最终从普遍使用 UIActivityViewController 切换到一个系统,该系统可以自定义为每种类型的服务完成的操作。我使用 Bob de Graaf 的 BDGShare。我构建了应用程序支持的服务列表,为每个服务显示一个按钮,然后切换到跳转到每种类型的共享。以防万一有人在做这件事,这是我想出的:
应用希望支持的分享类型:
public enum ShareServiceType:Int {
case iMessage=0, facebook, twitter, instagram, whatsApp, facebookMessenger, email, more
}
A class 存储有关共享服务类型的信息
public class ShareTargetVO: NSObject
{
var icon:String!
var label:String!
var type:ShareServiceType
init( serviceType:ShareServiceType, icon:String, label:String )
{
self.type = serviceType
self.icon = icon
self.label = label
}
}
我的社交网络助手中用于填充可用服务列表的功能:
static func getShareTargetList(for sticker : ReeSticker) -> [ShareTargetVO] {
var services:Array<ShareTargetVO> = []
// Check to see which capabilities are present and add buttons
if (BDGShare.shared().isAvailable(forServiceType: SLServiceTypeFacebook)) {
services.append(ShareTargetVO(serviceType: ShareServiceType.facebook, icon: "Icon-Share-Facebook", label: "Facebook"))
}
// Checking for facebook service type because there's no availability check with FBSDK for messenger (could be rolled into the lib)
if (BDGShare.shared().isAvailable(forServiceType: SLServiceTypeFacebook) && !sticker.type.doesContain("video")) {
services.append(ShareTargetVO(serviceType: ShareServiceType.facebookMessenger, icon: "Icon-Share-Messenger", label: "Messenger"))
}
if (BDGShare.shared().isAvailable(forServiceType: SLServiceTypeTwitter)) {
services.append(ShareTargetVO(serviceType: ShareServiceType.twitter, icon: "Icon-Share-Twitter", label: "Twitter"))
}
if (BDGShare.shared().canSendSMS()) {
services.append(ShareTargetVO(serviceType: ShareServiceType.iMessage, icon: "Icon-Share-Messages", label: "Messages"))
}
if UIApplication.shared.canOpenURL(URL(string: "whatsapp://")! as URL) && !sticker.type.contains("video") {
services.append(ShareTargetVO(serviceType: ShareServiceType.whatsApp, icon: "Icon-Share-Whatsapp", label: "What's App?"))
}
if UIApplication.shared.canOpenURL(URL(string: "instagram://app")! as URL) && !sticker.type.contains("video") {
services.append(ShareTargetVO(serviceType: ShareServiceType.instagram, icon: "Icon-Share-Instagram", label: "Instagram"))
}
if (BDGShare.shared().canSendEmail()) {
services.append(ShareTargetVO(serviceType: ShareServiceType.email, icon: "Icon-Share-Mail", label: "Email"))
}
services.append(ShareTargetVO(serviceType: ShareServiceType.more, icon: "Icon-Share-More", label: "More"))
return services
}
我的视图控制器中的一个函数,用于填充按钮的 UICollectionView,以共享仅限于从列表函数返回的那些服务:
func layoutShareButtons() {
let f = self.view.frame
let btnWidth = f.width * 0.82
let bannerWidth = btnWidth + 10
mask = UIView(frame: CGRect(x: 0, y: 0, width: f.width, height: f.height))
mask.backgroundColor = .black
mask.alpha = 0.3
self.view.addSubview(mask)
buttonList = SocialHelper.getShareTargetList(for: self.sticker)
let buttonGridLayout = UICollectionViewFlowLayout()
buttonGridLayout.sectionInset = UIEdgeInsets(top: 5, left: 5, bottom: 5, right: 5)
buttonGridLayout.itemSize = CGSize(width: 60, height: 60)
buttonGridLayout.scrollDirection = .horizontal
buttonListView = UICollectionView(frame: CGRect(x: (f.width - bannerWidth) / 2,
y: self.preview.frame.origin.y + self.preview.frame.height + 10,
width: bannerWidth,
height: 80),
collectionViewLayout: buttonGridLayout)
buttonListView.register(ShareButtonCell.self, forCellWithReuseIdentifier: "shareButtonCell")
buttonListView.dataSource = self
buttonListView.delegate = self
self.view.addSubview(buttonListView)
// cancel button for sharing view
// Button (added last to ensure it's on top of the z-order)
cancelButton = SimpleButton(frame: CGRect(x: (f.width - bannerWidth) / 2, y: self.buttonListView.frame.origin.y + self.buttonListView.frame.height + 10, width: bannerWidth, height: 52))
cancelButton.backgroundColor = UIColor(netHex:0x202020)
cancelButton.layoutComponent(0, label: "Cancel")
cancelButton.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.cancelButtonPressed(_:))))
self.view.addSubview(self.cancelButton)
}
UICollectionView didSelect 处理程序(为了获得更好的 SoC,具体取决于您的应用程序,将 "share" 函数移除到单独的 class 中,仅用于共享实现,在此应用程序中,我们正在使用的屏幕专门分享):
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
self.share(shareTarget: buttonList[indexPath.row])
}
最后,函数调用了正确的共享类型:
func share(shareTarget: ShareTargetVO) {
// Params to submit to service
self.shareUrl = self.sticker.getStickerURL()
let textStr = "" // BDGShare supports a message passed in as well but we just send the sticker
// we need the NSData either way (sticker / video)
var ok = true
// try? is fine here because result is tested below
let urlData : Data? = try? Data(contentsOf: self.shareUrl as URL)
ok = (urlData != nil)
var img: UIImage? = nil
// if it's an image type then get it
if ok {
if (self.shareUrl.pathExtension.contains("png")) || (self.shareUrl.pathExtension.contains("jpg")) {
img = UIImage(data: urlData! as Data)
ok = (img != nil)
}
}
if !ok {
let alertCtrl = UIAlertController(title: "Error sending", message: "There was an error gathering the information for sending this sticker.", preferredStyle: .alert)
alertCtrl.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.default, handler: nil))
self.present(alertCtrl, animated: true, completion: nil)
return
}
switch shareTarget.type
{
case .iMessage:
BDGShare.shared().shareSMS(textStr, recipient: nil, image: img, data:urlData! as Data, imageName: "sendSticker.png", completion: {(SharingResult) -> Void in
// Handle share result...
self.handleShareResult(shareTarget.type, shareResult: SharingResult)
})
break
case .facebook:
BDGShare.shared().shareFacebook("", urlStr: "", image: img, completion: {(SharingResult) -> Void in
// Handle share result...
self.handleShareResult(shareTarget.type, shareResult: SharingResult)
})
break
case .twitter:
... more code for handling each type of share
}
}
所以我曾经使用 BDGShare 来实现绕过 UIActivityViewController 的所有部分。顺便说一句 - 列表末尾的 "More" 选项调用 UIActivityViewController,因此如果用户选择,它仍然可供用户使用。
HTH,迈克
我一直在使用以下代码调用 UIActivityViewController 以通过各种社交媒体应用程序共享贴纸:
if let image = sticker.getUIImage(), let imgData = UIImagePNGRepresentation(image) {
let activityVC = UIActivityViewController(activityItems: [imgData], applicationActivities: nil)
activityVC.popoverPresentationController?.sourceView = gesture.view
self.present(activityVC, animated: true, completion: nil)
} else {
ErrorHandler.handleError(STICKER_IMAGE_NOT_FOUND, sticker)
}
在最近更新 FB Messenger(版本 98.0)之前,此代码一直运行良好。现在它显示错误 "Couldn't load content"。 FB Messenger 似乎更喜欢这样的 URL:
if let item = sticker.getImageURL() {
let activityVC = UIActivityViewController(activityItems: [item], applicationActivities: nil)
activityVC.popoverPresentationController?.sourceView = gesture.view
self.present(activityVC, animated: true, completion: nil)
} else {
ErrorHandler.handleError(STICKER_IMAGE_NOT_FOUND, sticker)
}
这适用于 FB Messenger,但 iMessage 显示透明的 PNG,背景为黑色。 我正在查看 UIActivityViewControllerCompletionWithItemsHandler,但讨论指出它在 activity 之后运行,对于我需要做的事情来说太晚了。我还尝试为 activityType 创建一个返回 UIActivityType.message 的自定义 UIActivity,但它被添加到控制器的底部而不是接管默认值。
有没有办法拦截 UIActivityViewController 中的项目选择,以便我可以使用 MFMessageComposeViewController 并将 UIImagePNGRepresentation 添加到消息中,并允许所有其他人使用 URL?
是否有我可以传递给 UIActivityViewController 的特定参数类型,它会在所有社交应用程序中正确显示透明 PNG?
TIA 麦克
绕回来结束这个。我最终从普遍使用 UIActivityViewController 切换到一个系统,该系统可以自定义为每种类型的服务完成的操作。我使用 Bob de Graaf 的 BDGShare。我构建了应用程序支持的服务列表,为每个服务显示一个按钮,然后切换到跳转到每种类型的共享。以防万一有人在做这件事,这是我想出的:
应用希望支持的分享类型:
public enum ShareServiceType:Int {
case iMessage=0, facebook, twitter, instagram, whatsApp, facebookMessenger, email, more
}
A class 存储有关共享服务类型的信息
public class ShareTargetVO: NSObject
{
var icon:String!
var label:String!
var type:ShareServiceType
init( serviceType:ShareServiceType, icon:String, label:String )
{
self.type = serviceType
self.icon = icon
self.label = label
}
}
我的社交网络助手中用于填充可用服务列表的功能:
static func getShareTargetList(for sticker : ReeSticker) -> [ShareTargetVO] {
var services:Array<ShareTargetVO> = []
// Check to see which capabilities are present and add buttons
if (BDGShare.shared().isAvailable(forServiceType: SLServiceTypeFacebook)) {
services.append(ShareTargetVO(serviceType: ShareServiceType.facebook, icon: "Icon-Share-Facebook", label: "Facebook"))
}
// Checking for facebook service type because there's no availability check with FBSDK for messenger (could be rolled into the lib)
if (BDGShare.shared().isAvailable(forServiceType: SLServiceTypeFacebook) && !sticker.type.doesContain("video")) {
services.append(ShareTargetVO(serviceType: ShareServiceType.facebookMessenger, icon: "Icon-Share-Messenger", label: "Messenger"))
}
if (BDGShare.shared().isAvailable(forServiceType: SLServiceTypeTwitter)) {
services.append(ShareTargetVO(serviceType: ShareServiceType.twitter, icon: "Icon-Share-Twitter", label: "Twitter"))
}
if (BDGShare.shared().canSendSMS()) {
services.append(ShareTargetVO(serviceType: ShareServiceType.iMessage, icon: "Icon-Share-Messages", label: "Messages"))
}
if UIApplication.shared.canOpenURL(URL(string: "whatsapp://")! as URL) && !sticker.type.contains("video") {
services.append(ShareTargetVO(serviceType: ShareServiceType.whatsApp, icon: "Icon-Share-Whatsapp", label: "What's App?"))
}
if UIApplication.shared.canOpenURL(URL(string: "instagram://app")! as URL) && !sticker.type.contains("video") {
services.append(ShareTargetVO(serviceType: ShareServiceType.instagram, icon: "Icon-Share-Instagram", label: "Instagram"))
}
if (BDGShare.shared().canSendEmail()) {
services.append(ShareTargetVO(serviceType: ShareServiceType.email, icon: "Icon-Share-Mail", label: "Email"))
}
services.append(ShareTargetVO(serviceType: ShareServiceType.more, icon: "Icon-Share-More", label: "More"))
return services
}
我的视图控制器中的一个函数,用于填充按钮的 UICollectionView,以共享仅限于从列表函数返回的那些服务:
func layoutShareButtons() {
let f = self.view.frame
let btnWidth = f.width * 0.82
let bannerWidth = btnWidth + 10
mask = UIView(frame: CGRect(x: 0, y: 0, width: f.width, height: f.height))
mask.backgroundColor = .black
mask.alpha = 0.3
self.view.addSubview(mask)
buttonList = SocialHelper.getShareTargetList(for: self.sticker)
let buttonGridLayout = UICollectionViewFlowLayout()
buttonGridLayout.sectionInset = UIEdgeInsets(top: 5, left: 5, bottom: 5, right: 5)
buttonGridLayout.itemSize = CGSize(width: 60, height: 60)
buttonGridLayout.scrollDirection = .horizontal
buttonListView = UICollectionView(frame: CGRect(x: (f.width - bannerWidth) / 2,
y: self.preview.frame.origin.y + self.preview.frame.height + 10,
width: bannerWidth,
height: 80),
collectionViewLayout: buttonGridLayout)
buttonListView.register(ShareButtonCell.self, forCellWithReuseIdentifier: "shareButtonCell")
buttonListView.dataSource = self
buttonListView.delegate = self
self.view.addSubview(buttonListView)
// cancel button for sharing view
// Button (added last to ensure it's on top of the z-order)
cancelButton = SimpleButton(frame: CGRect(x: (f.width - bannerWidth) / 2, y: self.buttonListView.frame.origin.y + self.buttonListView.frame.height + 10, width: bannerWidth, height: 52))
cancelButton.backgroundColor = UIColor(netHex:0x202020)
cancelButton.layoutComponent(0, label: "Cancel")
cancelButton.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.cancelButtonPressed(_:))))
self.view.addSubview(self.cancelButton)
}
UICollectionView didSelect 处理程序(为了获得更好的 SoC,具体取决于您的应用程序,将 "share" 函数移除到单独的 class 中,仅用于共享实现,在此应用程序中,我们正在使用的屏幕专门分享):
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
self.share(shareTarget: buttonList[indexPath.row])
}
最后,函数调用了正确的共享类型:
func share(shareTarget: ShareTargetVO) {
// Params to submit to service
self.shareUrl = self.sticker.getStickerURL()
let textStr = "" // BDGShare supports a message passed in as well but we just send the sticker
// we need the NSData either way (sticker / video)
var ok = true
// try? is fine here because result is tested below
let urlData : Data? = try? Data(contentsOf: self.shareUrl as URL)
ok = (urlData != nil)
var img: UIImage? = nil
// if it's an image type then get it
if ok {
if (self.shareUrl.pathExtension.contains("png")) || (self.shareUrl.pathExtension.contains("jpg")) {
img = UIImage(data: urlData! as Data)
ok = (img != nil)
}
}
if !ok {
let alertCtrl = UIAlertController(title: "Error sending", message: "There was an error gathering the information for sending this sticker.", preferredStyle: .alert)
alertCtrl.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.default, handler: nil))
self.present(alertCtrl, animated: true, completion: nil)
return
}
switch shareTarget.type
{
case .iMessage:
BDGShare.shared().shareSMS(textStr, recipient: nil, image: img, data:urlData! as Data, imageName: "sendSticker.png", completion: {(SharingResult) -> Void in
// Handle share result...
self.handleShareResult(shareTarget.type, shareResult: SharingResult)
})
break
case .facebook:
BDGShare.shared().shareFacebook("", urlStr: "", image: img, completion: {(SharingResult) -> Void in
// Handle share result...
self.handleShareResult(shareTarget.type, shareResult: SharingResult)
})
break
case .twitter:
... more code for handling each type of share
}
}
所以我曾经使用 BDGShare 来实现绕过 UIActivityViewController 的所有部分。顺便说一句 - 列表末尾的 "More" 选项调用 UIActivityViewController,因此如果用户选择,它仍然可供用户使用。
HTH,迈克