新增分享功能的多个方法,包括提取 URL 和剩余字符串、强制关闭分享弹窗,以及分享内容的构建逻辑。更新 ShareProvider 类以支持新的初始化方法和不同分享类型的内容提供。保持代码结构一致性。

This commit is contained in:
edwinQQQ
2025-06-09 16:39:32 +08:00
parent 8dab721de9
commit 443e1a4332
4 changed files with 377 additions and 83 deletions

View File

@@ -19,6 +19,19 @@ NS_ASSUME_NONNULL_BEGIN
url:(NSString *)urlString
fromController:(UIViewController *)viewController;
/// 提取 URL 和剩余字符串
/// @param inputString 输入字符串
/// @return 包含 URL 和剩余字符串的字典
+ (NSDictionary *)extractURLAndRemainingStringFromString:(NSString *)inputString;
/// 提取 URL 字符串
/// @param inputString 输入字符串
/// @return 提取的 URL 字符串
+ (NSString *)extractURLFromString:(NSString *)inputString;
/// 强制关闭分享弹窗
+ (void)forceCloseShareSheet;
@end
NS_ASSUME_NONNULL_END

View File

@@ -10,6 +10,11 @@
#import "ShareProvider.h"
#import <LinkPresentation/LinkPresentation.h>
//
static UIActivityViewController *_currentActivityVC = nil;
static NSTimer *_timeoutTimer = nil;
static __weak UIViewController *_presentingVC = nil;
@implementation ShareHelder
+ (NSDictionary *)extractURLAndRemainingStringFromString:(NSString *)inputString {
@@ -73,48 +78,85 @@
+ (void)shareImage:(UIImage *)image
url:(NSString *)urlString
fromController:(UIViewController *)viewController {
fromController:(UIViewController *)viewController {
#if DEBUG
urlString = [urlString stringByReplacingOccurrencesOfString:@"https" withString:@"http"];
#endif
// 1.
NSDictionary *dic = [self extractURLAndRemainingStringFromString:urlString];
NSString *text = [dic objectForKey:@"remainingString"];
NSURL *url = [NSURL URLWithString:[dic objectForKey:@"url"]];
// 2.
NSMutableArray *shareItems = @[text, url].mutableCopy;
if (@available(iOS 13.0, *)) {
LPLinkMetadata *metadata = [[LPLinkMetadata alloc] init];
metadata.originalURL = url;
metadata.URL = url;
metadata.title = @"MoliStars";
// 使
UIImage *iconImage = image;
metadata.iconProvider = [[NSItemProvider alloc] initWithObject:iconImage];
//
UIImage *thumbnailImage = image;
metadata.imageProvider = [[NSItemProvider alloc] initWithObject:thumbnailImage];
[shareItems addObject:metadata];
// 1. URL
NSDictionary *dic = [self extractURLAndRemainingStringFromString:urlString];
NSString *remainingText = [dic objectForKey:@"remainingString"];
NSString *urlStr = [dic objectForKey:@"url"];
NSURL *url = urlStr.length > 0 ? [NSURL URLWithString:urlStr] : nil;
// 2.
NSString *title = @"🎵 发现精彩内容";
NSString *subtitle = remainingText.length > 0 ? remainingText : @"快来看看这个有趣的内容吧!";
NSString *appName = [YYUtility appName];
// 3.
UIImage *appIcon = [UIImage imageNamed:@"AppIcon"] ?: [UIImage imageNamed:@"app_icon"];
if (!appIcon) {
// bundle
NSString *iconPath = [[NSBundle mainBundle] pathForResource:@"AppIcon60x60" ofType:@"png"];
if (iconPath) {
appIcon = [UIImage imageWithContentsOfFile:iconPath];
}
}
// 3.
NSArray *activityItems = shareItems.copy;
//
NSMutableArray *shareItems = [NSMutableArray array];
// 4.
// 1.
NSString *plainText = [NSString stringWithFormat:@"🎵 发现精彩内容\n\n%@\n\n🔗 %@\n\n—— 来自 MoliStars ——",
subtitle, url.absoluteString ?: @""];
[shareItems addObject:plainText];
// 2. URL
if (url) {
[shareItems addObject:url];
}
// 3.
if (image) {
[shareItems addObject:image];
}
// 4. ShareProvider
ShareProvider *shareProvider = [[ShareProvider alloc] initWithTitle:title
subtitle:subtitle
appName:appName
url:url
image:image
appIcon:appIcon];
[shareItems addObject:shareProvider];
// ActivityViewController
UIActivityViewController *activityVC = [[UIActivityViewController alloc]
initWithActivityItems:activityItems
initWithActivityItems:shareItems.copy
applicationActivities:nil];
//
activityVC.excludedActivityTypes = nil;
// 5.
activityVC.excludedActivityTypes = @[
UIActivityTypeAssignToContact, //
// UIActivityTypeAddToHomeScreen //
];
//
_currentActivityVC = activityVC;
_presentingVC = viewController;
// 6. iPad
//
activityVC.completionWithItemsHandler = ^(UIActivityType activityType, BOOL completed, NSArray *returnedItems, NSError *activityError) {
[self cleanupShareSession];
if (activityError) {
NSLog(@"ShareHelder: Activity error occurred: %@", activityError.localizedDescription);
//
dispatch_async(dispatch_get_main_queue(), ^{
[self forceCloseShareSheet];
});
}
};
// 8. iPad
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
UIPopoverPresentationController *popover = activityVC.popoverPresentationController;
if (popover) {
@@ -123,67 +165,90 @@
popover.permittedArrowDirections = UIPopoverArrowDirectionAny;
}
}
// 10.
_timeoutTimer = [NSTimer scheduledTimerWithTimeInterval:15.0 target:self selector:@selector(checkShareSheetTimeout) userInfo:nil repeats:NO];
// 7.
// 11.
[viewController presentViewController:activityVC animated:YES completion:nil];
}
#pragma mark -
+ (void)forceCloseShareSheet {
if (_currentActivityVC) {
UIViewController *presentingVC = _currentActivityVC.presentingViewController;
if (presentingVC) {
[presentingVC dismissViewControllerAnimated:YES completion:^{
[self cleanupShareSession];
}];
} else if (_presentingVC) {
[_presentingVC dismissViewControllerAnimated:YES completion:^{
[self cleanupShareSession];
}];
}
}
//
[self cleanupShareSession];
}
+ (void)checkShareSheetTimeout {
//
if (_currentActivityVC && _currentActivityVC.presentingViewController) {
NSLog(@"ShareHelder: Share sheet timeout detected, attempting force close");
[self forceCloseShareSheet];
}
}
+ (void)cleanupShareSession {
//
if (_timeoutTimer) {
[_timeoutTimer invalidate];
_timeoutTimer = nil;
}
//
_currentActivityVC = nil;
_presentingVC = nil;
}
+ (void)__shareImage:(UIImage *)image
url:(NSString *)urlString
fromController:(UIViewController *)viewController {
// NSMutableArray *items = [NSMutableArray array];
//
// if (image) {
// NSData *imageData = UIImagePNGRepresentation(image);
// if (imageData) {
// NSItemProvider *imageProvider = [[NSItemProvider alloc] initWithItem:imageData typeIdentifier:@"public.png"];
// [items addObject:imageProvider];
// }
// }
//
// // URL +
// if (urlString.length > 0) {
// NSString *textToShare = [NSString stringWithFormat:@"分享链接:%@", urlString];
// NSItemProvider *textProvider = [[NSItemProvider alloc] initWithItem:textToShare typeIdentifier:@"public.plain-text"];
// [items addObject:textProvider];
// }
// 1.
NSString *title = @"🎵 Apple Music 专辑推荐Imagine Dragons";
NSString *subtitle = @"来自MoliStars的精彩推荐";
NSString *appName = @"MoliStars";
NSURL *albumURL = [NSURL URLWithString:[NSString stringWithFormat:@"%@/%@", API_HOST_URL, urlString]];
UIImage *albumImage = image;
// 2.
UIImage *appIcon = [UIImage imageNamed:@"AppIcon"] ?: [UIImage imageNamed:@"app_icon"];
if (!appIcon) {
// bundle
NSString *iconPath = [[NSBundle mainBundle] pathForResource:@"AppIcon60x60" ofType:@"png"];
if (iconPath) {
appIcon = [UIImage imageWithContentsOfFile:iconPath];
}
}
// ItemProvider
ShareProvider *musicShareProvider = [[ShareProvider alloc] initWithTitle:title url:albumURL image:albumImage];
// 3. ShareProvider使
ShareProvider *musicShareProvider = [[ShareProvider alloc] initWithTitle:title
subtitle:subtitle
appName:appName
url:albumURL
image:albumImage
appIcon:appIcon];
//
// 4.
NSArray *activityItems = @[musicShareProvider, albumImage];
// //
// if (image) {
// [items addObject:image];
// }
//
// // URL
// if (urlString.length > 0) {
// NSURL *url = [NSURL URLWithString:urlString];
// if (url) {
// [items addObject:url];
// }
// }
//
// if (items.count == 0) {
// NSLog(@"❌ 无效的分享内容");
// return;
// }
//
// 5.
UIActivityViewController *activityVC = [[UIActivityViewController alloc] initWithActivityItems:activityItems applicationActivities:nil];
//
// 6.
activityVC.excludedActivityTypes = @[
UIActivityTypeAssignToContact, //
UIActivityTypeSaveToCameraRoll, //
@@ -193,13 +258,13 @@
UIActivityTypeMarkupAsPDF // PDF
];
// iPad `popoverPresentationController`
// 7. iPad `popoverPresentationController`
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
activityVC.popoverPresentationController.sourceView = viewController.view;
activityVC.popoverPresentationController.sourceRect = CGRectMake(viewController.view.bounds.size.width / 2, viewController.view.bounds.size.height / 2, 0, 0);
}
//
// 8.
[viewController presentViewController:activityVC animated:YES completion:nil];
}

View File

@@ -6,14 +6,26 @@
//
#import <UIKit/UIKit.h>
#import <LinkPresentation/LinkPresentation.h>
NS_ASSUME_NONNULL_BEGIN
@interface ShareProvider : UIActivityItemProvider
@property (nonatomic, strong) NSString *title;
@property (nonatomic, strong) NSString *subtitle;
@property (nonatomic, strong) NSString *appName;
@property (nonatomic, strong) NSURL *url;
@property (nonatomic, strong) UIImage *image;
- (instancetype)initWithTitle:(NSString *)title url:(NSURL *)url image:(UIImage *)image;
@property (nonatomic, strong) UIImage *appIcon;
- (instancetype)initWithTitle:(NSString *)title
subtitle:(NSString *)subtitle
appName:(NSString *)appName
url:(NSURL *)url
image:(UIImage *)image
appIcon:(UIImage *)appIcon;
@end
NS_ASSUME_NONNULL_END

View File

@@ -6,23 +6,227 @@
//
#import "ShareProvider.h"
#import <UniformTypeIdentifiers/UniformTypeIdentifiers.h>
@implementation ShareProvider
- (instancetype)initWithTitle:(NSString *)title url:(NSURL *)url image:(UIImage *)image {
self = [super initWithPlaceholderItem:title];
- (instancetype)initWithTitle:(NSString *)title
subtitle:(NSString *)subtitle
appName:(NSString *)appName
url:(NSURL *)url
image:(UIImage *)image
appIcon:(UIImage *)appIcon {
// 使
self = [super initWithPlaceholderItem:title ?: @""];
if (self) {
_title = title;
_title = title ?: @"";
_subtitle = subtitle ?: @"";
_appName = appName ?: @"MoliStar";
_url = url;
_image = image;
_appIcon = appIcon;
}
return self;
}
//
- (id)item {
return [NSString stringWithFormat:@"%@\n%@", self.title, self.url.absoluteString];
#pragma mark - UIActivityItemSource
//
- (id)activityViewController:(UIActivityViewController *)activityViewController
itemForActivityType:(UIActivityType)activityType {
if (!activityType) {
//
return [self fullShareText];
}
// -
if ([activityType isEqualToString:UIActivityTypeMail]) {
return [self emailShareText];
}
// / -
if ([activityType isEqualToString:UIActivityTypeMessage]) {
return [self messageShareText];
}
// -
if ([activityType containsString:@"notes"] ||
[activityType containsString:@"Notes"]) {
return [self notesShareText];
}
// -
if ([activityType isEqualToString:UIActivityTypePostToTwitter] ||
[activityType isEqualToString:UIActivityTypePostToFacebook]) {
return [self socialShareText];
}
// AirDrop -
if ([activityType isEqualToString:UIActivityTypeAirDrop]) {
if (self.image) {
return self.image;
} else {
return self.url ?: [self fullShareText];
}
}
// - URL
if ([activityType isEqualToString:UIActivityTypeCopyToPasteboard]) {
return self.url.absoluteString ?: [self fullShareText];
}
//
return [self fullShareText];
}
//
- (NSString *)activityViewController:(UIActivityViewController *)activityViewController
subjectForActivityType:(UIActivityType)activityType {
if ([activityType isEqualToString:UIActivityTypeMail]) {
return [NSString stringWithFormat:@"来自%@的分享:%@", self.appName, self.title];
}
return nil;
}
//
- (NSString *)activityViewController:(UIActivityViewController *)activityViewController
dataTypeIdentifierForActivityType:(UIActivityType)activityType {
if (self.image && [activityType isEqualToString:UIActivityTypeAirDrop]) {
if (@available(iOS 14.0, *)) {
return UTTypePNG.identifier;
} else {
return @"public.png";
}
}
if (@available(iOS 14.0, *)) {
return UTTypeText.identifier;
} else {
return @"public.plain-text";
}
}
//
- (LPLinkMetadata *)activityViewControllerLinkMetadata:(UIActivityViewController *)activityViewController {
LPLinkMetadata *metadata = [[LPLinkMetadata alloc] init];
//
metadata.title = self.title;
// 使 originalURL
if (self.subtitle.length > 0) {
metadata.originalURL = [NSURL fileURLWithPath:self.subtitle];
}
// URL
if (self.url) {
metadata.URL = self.url;
}
// - 使使
UIImage *iconToUse = self.appIcon ?: self.image;
if (iconToUse) {
metadata.iconProvider = [[NSItemProvider alloc] initWithObject:iconToUse];
}
//
if (self.image && self.image != iconToUse) {
metadata.imageProvider = [[NSItemProvider alloc] initWithObject:self.image];
}
return metadata;
}
#pragma mark - -
- (NSString *)fullShareText {
NSMutableString *text = [NSMutableString string];
if (self.title.length > 0) {
[text appendFormat:@"📱 %@\n\n", self.title];
}
if (self.subtitle.length > 0) {
[text appendFormat:@"%@\n\n", self.subtitle];
}
if (self.url) {
[text appendFormat:@"🔗 %@\n\n", self.url.absoluteString];
}
[text appendFormat:@"—— 来自 %@ ——", self.appName];
return text.copy;
}
- (NSString *)emailShareText {
NSMutableString *text = [NSMutableString string];
[text appendString:@"<html><body>"];
if (self.title.length > 0) {
[text appendFormat:@"<h2>📱 %@</h2>", self.title];
}
if (self.subtitle.length > 0) {
[text appendFormat:@"<p>%@</p>", self.subtitle];
}
if (self.url) {
[text appendFormat:@"<p><a href=\"%@\">🔗 点击访问</a></p>", self.url.absoluteString];
}
[text appendFormat:@"<br><hr><small>通过 %@ 分享</small>", self.appName];
[text appendString:@"</body></html>"];
return text.copy;
}
- (NSString *)messageShareText {
if (self.url) {
return [NSString stringWithFormat:@"📱 %@\n%@", self.title, self.url.absoluteString];
}
return [NSString stringWithFormat:@"📱 %@\n%@", self.title, self.subtitle];
}
- (NSString *)notesShareText {
NSMutableString *text = [NSMutableString string];
[text appendFormat:@"📝 %@\n", self.title];
[text appendString:@"━━━━━━━━━━━━━━━━━━━━\n\n"];
if (self.subtitle.length > 0) {
[text appendFormat:@"📄 %@\n\n", self.subtitle];
}
if (self.url) {
[text appendFormat:@"🔗 链接:%@\n\n", self.url.absoluteString];
}
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
formatter.dateFormat = @"yyyy-MM-dd HH:mm";
[text appendFormat:@"⏰ 保存时间:%@\n", [formatter stringFromDate:[NSDate date]]];
[text appendFormat:@"📱 来源:%@", self.appName];
return text.copy;
}
- (NSString *)socialShareText {
NSMutableString *text = [NSMutableString string];
if (self.title.length > 0) {
[text appendFormat:@"📱 %@ ", self.title];
}
if (self.url) {
[text appendFormat:@"%@ ", self.url.absoluteString];
}
[text appendFormat:@"#%@ #分享 #推荐", [self.appName stringByReplacingOccurrencesOfString:@" " withString:@""]];
return text.copy;
}
@end