Files
real-e-party-iOS/YuMi/Tools/Share/ShareHelder.m
edwinQQQ 2d0063396c feat: 添加 E-Parti 启动画面及情绪颜色引导功能
主要变更:
1. 新增 ep_splash.png 作为应用启动时的展示图像。
2. 更新 Info.plist 中的应用名称和相关描述,替换为 "E-Parti"。
3. 引入 EPSignatureColorGuideView 和 EPEmotionColorStorage,支持用户选择和保存专属情绪颜色。
4. 在 AppDelegate 中集成情绪颜色引导逻辑,确保用户首次登录时能够选择专属颜色。

此更新旨在提升用户体验,增强应用的品牌识别度,并提供个性化的情绪表达功能。
2025-10-15 15:56:32 +08:00

272 lines
11 KiB
Objective-C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//
// ShareHelder.m
// YuMi
//
// Created by P on 2025/3/6.
//
#import <UIKit/UIKit.h>
#import "ShareHelder.h"
#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 {
// 创建正则表达式,匹配 URL
NSError *error = NULL;
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:@"https?://[^\\s]+"
options:NSRegularExpressionCaseInsensitive
error:&error];
if (error) {
// NSLog(@"Error creating regex: %@", error);
return @{@"url": @"", @"remainingString": inputString};
}
// 查找匹配的 URL
NSTextCheckingResult *match = [regex firstMatchInString:inputString
options:0
range:NSMakeRange(0, inputString.length)];
if (match) {
// 提取匹配的 URL
NSRange urlRange = [match range];
NSString *url = [inputString substringWithRange:urlRange];
// 提取分离 URL 后的字符串
NSString *remainingString;
if (urlRange.location + urlRange.length < inputString.length) {
remainingString = [inputString substringFromIndex:urlRange.location + urlRange.length];
} else {
remainingString = @"";
}
return @{@"url": url, @"remainingString": remainingString};
}
// 如果没有找到 URL返回原始字符串
return @{@"url": @"", @"remainingString": inputString};
}
+ (NSString *)extractURLFromString:(NSString *)inputString {
// 创建正则表达式,匹配 URL
NSError *error = NULL;
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:@"https?://[^\\s]+"
options:NSRegularExpressionCaseInsensitive
error:&error];
if (error) {
// NSLog(@"Error creating regex: %@", error);
return @"";
}
// 查找匹配的 URL
NSTextCheckingResult *match = [regex firstMatchInString:inputString
options:0
range:NSMakeRange(0, inputString.length)];
if (match) {
// 提取匹配的 URL
NSRange urlRange = [match range];
return [inputString substringWithRange:urlRange];
}
return @"";
}
+ (void)shareImage:(UIImage *)image
url:(NSString *)urlString
fromController:(UIViewController *)viewController {
#if DEBUG
urlString = [urlString stringByReplacingOccurrencesOfString:@"https" withString:@"http"];
#endif
// 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];
}
}
// 准备多种格式的分享内容
NSMutableArray *shareItems = [NSMutableArray array];
// 1. 添加纯文本(用于备忘录等文本应用)
NSString *plainText = [NSString stringWithFormat:@"🎵 发现精彩内容\n\n%@\n\n🔗 %@\n\n—— 来自 E-Parti ——",
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:shareItems.copy
applicationActivities:nil];
// 不排除任何系统分享类型,让用户看到所有选项
activityVC.excludedActivityTypes = nil;
// 保存当前分享控制器的引用
_currentActivityVC = activityVC;
_presentingVC = viewController;
// 添加完成回调监控
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) {
popover.sourceView = viewController.view;
popover.sourceRect = CGRectMake(viewController.view.bounds.size.width / 2, viewController.view.bounds.size.height / 2, 0, 0);
popover.permittedArrowDirections = UIPopoverArrowDirectionAny;
}
}
// 10. 设置超时保护定时器
_timeoutTimer = [NSTimer scheduledTimerWithTimeInterval:15.0 target:self selector:@selector(checkShareSheetTimeout) userInfo:nil repeats:NO];
// 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 {
// 1. 准备分享内容
NSString *title = @"🎵 Apple Music 专辑推荐Imagine Dragons";
NSString *subtitle = @"来自E-Parti的精彩推荐";
NSString *appName = @"E-Parti";
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];
}
}
// 3. 创建自定义 ShareProvider使用新的初始化方法
ShareProvider *musicShareProvider = [[ShareProvider alloc] initWithTitle:title
subtitle:subtitle
appName:appName
url:albumURL
image:albumImage
appIcon:appIcon];
// 4. 创建分享项
NSArray *activityItems = @[musicShareProvider, albumImage];
// 5. 创建分享面板
UIActivityViewController *activityVC = [[UIActivityViewController alloc] initWithActivityItems:activityItems applicationActivities:nil];
// 6. 设置排除的分享选项
activityVC.excludedActivityTypes = @[
UIActivityTypeAssignToContact, // 指定给联系人
UIActivityTypeSaveToCameraRoll, // 保存到相册
UIActivityTypePrint, // 打印
UIActivityTypeAddToReadingList, // 添加到 Safari 阅读列表
UIActivityTypeOpenInIBooks, // 在 iBooks 中打开
UIActivityTypeMarkupAsPDF // 标记 PDF
];
// 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];
}
@end