feat: 重构分享功能 - 将各平台分享逻辑整理到对应Manager中
- 新增 WechatShareManager:封装微信分享逻辑,支持ShareContent对象 - 增强 DouyinShareManager:新增ShareContent支持和分享引导功能 - 优化 QQShareManager:支持截图/纯文本分享类型自动识别 - 重构 SharePanel:简化为UI调度层,移除具体分享实现 - 实现职责分离:各Manager专注自己的平台分享逻辑 - 提升可维护性:修改某平台不影响其他平台 - 增强可扩展性:新增分享平台更容易实现
This commit is contained in:
74
msext/Class/Utils/DouyinShareManager.h
Normal file
74
msext/Class/Utils/DouyinShareManager.h
Normal file
@@ -0,0 +1,74 @@
|
||||
//
|
||||
// DouyinShareManager.h
|
||||
// msext
|
||||
//
|
||||
// Created on 2025/06/15.
|
||||
// Copyright © 2025年. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
typedef NS_ENUM(NSInteger, DouyinShareType) {
|
||||
DouyinShareTypeImage, // 图片分享
|
||||
DouyinShareTypeVideo, // 视频分享
|
||||
DouyinShareTypeHashtag, // 话题挑战分享
|
||||
DouyinShareTypeOpenProfile // 打开个人主页
|
||||
};
|
||||
|
||||
@interface DouyinShareManager : NSObject
|
||||
|
||||
/**
|
||||
* 检查抖音是否已安装
|
||||
* @return BOOL 是否已安装抖音
|
||||
*/
|
||||
+ (BOOL)isDouyinInstalled;
|
||||
|
||||
/**
|
||||
* 分享到抖音
|
||||
* @param type 分享类型
|
||||
* @param hashtagName 话题名称(仅用于话题挑战类型)
|
||||
* @param image 图片(仅用于图片分享类型)
|
||||
* @param videoUrl 视频本地URL(仅用于视频分享类型)
|
||||
* @param userId 用户ID(仅用于打开个人主页类型)
|
||||
* @param completion 完成回调
|
||||
*/
|
||||
+ (void)shareToDouyin:(DouyinShareType)type
|
||||
hashtagName:(NSString * _Nullable)hashtagName
|
||||
image:(UIImage * _Nullable)image
|
||||
videoUrl:(NSURL * _Nullable)videoUrl
|
||||
userId:(NSString * _Nullable)userId
|
||||
completion:(void(^_Nullable)(BOOL success))completion;
|
||||
|
||||
/**
|
||||
* 处理从抖音返回的URL
|
||||
* 需要在 AppDelegate 的 application:openURL:options: 方法中调用
|
||||
*/
|
||||
+ (BOOL)handleOpenURL:(NSURL *)url;
|
||||
|
||||
/**
|
||||
* 使用ShareContent对象进行抖音分享
|
||||
* 支持根据type字段自动选择分享类型:
|
||||
* - type=2:截图分享(复制图片到剪贴板+引导)
|
||||
* - 其他:文本分享(复制文本到剪贴板+引导)
|
||||
* @param shareContent 完整的分享内容对象,包含title、desc、type字段
|
||||
* @param completion 分享完成回调
|
||||
*/
|
||||
+ (void)shareWithContent:(id)shareContent
|
||||
completion:(void (^ _Nullable)(BOOL success))completion;
|
||||
|
||||
/**
|
||||
* 显示抖音图片分享引导弹窗
|
||||
*/
|
||||
+ (void)showImageShareGuidance;
|
||||
|
||||
/**
|
||||
* 显示抖音文本分享引导弹窗
|
||||
*/
|
||||
+ (void)showTextShareGuidance;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
595
msext/Class/Utils/DouyinShareManager.m
Normal file
595
msext/Class/Utils/DouyinShareManager.m
Normal file
@@ -0,0 +1,595 @@
|
||||
//
|
||||
// DouyinShareManager.m
|
||||
// msext
|
||||
//
|
||||
// Created on 2025/06/15.
|
||||
// Copyright © 2025年. All rights reserved.
|
||||
//
|
||||
|
||||
#import "DouyinShareManager.h"
|
||||
#import "FuncPublic.h" // 用于截图功能
|
||||
#import <Photos/Photos.h>
|
||||
#import <MobileCoreServices/MobileCoreServices.h>
|
||||
|
||||
// 抖音URL Schemes
|
||||
#define kDouyinScheme @"snssdk1128://"
|
||||
#define kDouyinShareScheme @"snssdk1128://share/video"
|
||||
#define kDouyinImageShareScheme @"snssdk1128://share/image"
|
||||
#define kDouyinHashtagScheme @"snssdk1128://challenge/detail/"
|
||||
#define kDouyinUserProfileScheme @"snssdk1128://user/profile/"
|
||||
|
||||
// 抖音应用回调的处理
|
||||
static void(^DouyinShareCompletion)(BOOL) = nil;
|
||||
|
||||
@implementation DouyinShareManager
|
||||
|
||||
#pragma mark - Public Methods
|
||||
|
||||
+ (BOOL)isDouyinInstalled {
|
||||
return [[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:kDouyinScheme]];
|
||||
}
|
||||
|
||||
+ (void)shareToDouyin:(DouyinShareType)type
|
||||
hashtagName:(NSString *)hashtagName
|
||||
image:(UIImage *)image
|
||||
videoUrl:(NSURL *)videoUrl
|
||||
userId:(NSString *)userId
|
||||
completion:(void (^)(BOOL))completion {
|
||||
|
||||
// 检查抖音是否已安装
|
||||
if (![self isDouyinInstalled]) {
|
||||
if (completion) {
|
||||
completion(NO);
|
||||
}
|
||||
[self showAppNotInstalledAlert:@"抖音"];
|
||||
return;
|
||||
}
|
||||
|
||||
// 保存回调
|
||||
DouyinShareCompletion = completion;
|
||||
|
||||
switch (type) {
|
||||
case DouyinShareTypeImage:
|
||||
[self shareImageToDouyin:image completion:completion];
|
||||
break;
|
||||
|
||||
case DouyinShareTypeVideo:
|
||||
[self shareVideoToDouyin:videoUrl completion:completion];
|
||||
break;
|
||||
|
||||
case DouyinShareTypeHashtag:
|
||||
[self shareHashtagToDouyin:hashtagName completion:completion];
|
||||
break;
|
||||
|
||||
case DouyinShareTypeOpenProfile:
|
||||
[self openDouyinUserProfile:userId completion:completion];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
+ (BOOL)handleOpenURL:(NSURL *)url {
|
||||
// 判断是否是抖音返回的URL
|
||||
NSString *urlString = [url absoluteString];
|
||||
if ([urlString hasPrefix:@"msext://douyin_share_result"]) {
|
||||
// 根据返回的URL解析结果
|
||||
NSString *result = [self getUrlParam:urlString paramName:@"result"];
|
||||
BOOL success = result && [result isEqualToString:@"success"];
|
||||
|
||||
// 执行回调
|
||||
if (DouyinShareCompletion) {
|
||||
DouyinShareCompletion(success);
|
||||
DouyinShareCompletion = nil;
|
||||
}
|
||||
return YES;
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
|
||||
+ (void)shareWithContent:(id)shareContent
|
||||
completion:(void (^ _Nullable)(BOOL success))completion {
|
||||
NSLog(@"🔍 [DouyinShareManager] 开始抖音分享");
|
||||
|
||||
// 检查抖音是否已安装
|
||||
if (![self isDouyinInstalled]) {
|
||||
NSLog(@"❌ [DouyinShareManager] 抖音未安装");
|
||||
[self showAppNotInstalledAlert:@"抖音"];
|
||||
if (completion) {
|
||||
completion(NO);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// 检查ShareContent对象
|
||||
if (!shareContent) {
|
||||
NSLog(@"❌ [DouyinShareManager] ShareContent对象为空");
|
||||
if (completion) {
|
||||
completion(NO);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// 通过KVC从ShareContent对象中提取参数
|
||||
NSString *title = nil;
|
||||
NSString *description = nil;
|
||||
NSString *type = nil;
|
||||
|
||||
@try {
|
||||
if ([shareContent respondsToSelector:@selector(valueForKey:)]) {
|
||||
title = [shareContent valueForKey:@"title"];
|
||||
description = [shareContent valueForKey:@"desc"];
|
||||
type = [shareContent valueForKey:@"type"];
|
||||
}
|
||||
|
||||
NSLog(@"🔍 [DouyinShareManager] 从ShareContent提取参数:");
|
||||
NSLog(@"🔍 - 标题: %@", title ?: @"(无)");
|
||||
NSLog(@"🔍 - 描述: %@", description ?: @"(无)");
|
||||
NSLog(@"🔍 - 类型: %@", type ?: @"(无)");
|
||||
|
||||
} @catch (NSException *exception) {
|
||||
NSLog(@"❌ [DouyinShareManager] 解析ShareContent时发生异常: %@", exception.reason);
|
||||
if (completion) {
|
||||
completion(NO);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// 根据分享类型选择分享方式
|
||||
BOOL isScreenshotShare = [type intValue] == 2;
|
||||
|
||||
if (isScreenshotShare) {
|
||||
// 截图分享模式
|
||||
NSLog(@"🔍 [DouyinShareManager] 执行截图分享");
|
||||
|
||||
UIImage *screenImage = [FuncPublic getImageWithFullScreenshot];
|
||||
if (screenImage) {
|
||||
// 复制图片到剪贴板
|
||||
UIPasteboard *pasteboard = [UIPasteboard generalPasteboard];
|
||||
pasteboard.image = screenImage;
|
||||
|
||||
NSLog(@"✅ [DouyinShareManager] 截图已复制到剪贴板");
|
||||
|
||||
// 显示分享引导弹窗
|
||||
[self showImageShareGuidance];
|
||||
|
||||
if (completion) {
|
||||
completion(YES);
|
||||
}
|
||||
} else {
|
||||
NSLog(@"❌ [DouyinShareManager] 截图获取失败");
|
||||
if (completion) {
|
||||
completion(NO);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 文本分享模式
|
||||
NSLog(@"🔍 [DouyinShareManager] 执行文本分享");
|
||||
|
||||
// 组合分享内容
|
||||
NSString *shareText = @"";
|
||||
if (title && title.length > 0) {
|
||||
shareText = title;
|
||||
}
|
||||
if (description && description.length > 0) {
|
||||
if (shareText.length > 0) {
|
||||
shareText = [NSString stringWithFormat:@"%@\n\n%@", shareText, description];
|
||||
} else {
|
||||
shareText = description;
|
||||
}
|
||||
}
|
||||
|
||||
if (shareText.length == 0) {
|
||||
shareText = @"来自进贤聚友棋牌的精彩内容分享";
|
||||
}
|
||||
|
||||
// 复制到剪贴板
|
||||
UIPasteboard *pasteboard = [UIPasteboard generalPasteboard];
|
||||
[pasteboard setString:shareText];
|
||||
|
||||
NSLog(@"✅ [DouyinShareManager] 文本已复制到剪贴板: %@", shareText);
|
||||
|
||||
// 显示分享引导弹窗
|
||||
[self showTextShareGuidance];
|
||||
|
||||
if (completion) {
|
||||
completion(YES);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - Private Methods
|
||||
|
||||
+ (void)shareImageToDouyin:(UIImage *)image completion:(void (^)(BOOL))completion {
|
||||
if (!image) {
|
||||
if (completion) {
|
||||
completion(NO);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// 方案1:先保存图片到相册,然后使用UIPasteboard传递图片数据,最后打开抖音
|
||||
[self requestPhotoLibraryAuthorizationWithCompletion:^(BOOL granted) {
|
||||
if (!granted) {
|
||||
if (completion) {
|
||||
completion(NO);
|
||||
}
|
||||
[self showPhotoLibraryPermissionAlert];
|
||||
return;
|
||||
}
|
||||
|
||||
// 将图片保存到相册
|
||||
[[PHPhotoLibrary sharedPhotoLibrary] performChanges:^{
|
||||
[PHAssetChangeRequest creationRequestForAssetFromImage:image];
|
||||
} completionHandler:^(BOOL success, NSError * _Nullable error) {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
if (success) {
|
||||
// 同时将图片数据放到剪贴板,供抖音读取
|
||||
UIPasteboard *pasteboard = [UIPasteboard generalPasteboard];
|
||||
[pasteboard setImage:image];
|
||||
|
||||
// 尝试多种URL方案打开抖音分享
|
||||
NSArray *urlSchemes = @[
|
||||
@"snssdk1128://camera", // 打开抖音拍摄界面
|
||||
@"snssdk1128://publish", // 打开发布界面
|
||||
@"snssdk1128://share/image", // 图片分享(可能已废弃)
|
||||
@"snssdk1128://" // 基础scheme,打开抖音主界面
|
||||
];
|
||||
|
||||
BOOL opened = NO;
|
||||
for (NSString *urlString in urlSchemes) {
|
||||
NSURL *douyinURL = [NSURL URLWithString:urlString];
|
||||
if ([[UIApplication sharedApplication] canOpenURL:douyinURL]) {
|
||||
if (@available(iOS 10.0, *)) {
|
||||
[[UIApplication sharedApplication] openURL:douyinURL options:@{} completionHandler:^(BOOL openSuccess) {
|
||||
if (completion) {
|
||||
completion(openSuccess);
|
||||
}
|
||||
}];
|
||||
} else {
|
||||
BOOL openSuccess = [[UIApplication sharedApplication] openURL:douyinURL];
|
||||
if (completion) {
|
||||
completion(openSuccess);
|
||||
}
|
||||
}
|
||||
opened = YES;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!opened && completion) {
|
||||
completion(NO);
|
||||
}
|
||||
} else {
|
||||
if (completion) {
|
||||
completion(NO);
|
||||
}
|
||||
NSLog(@"保存图片到相册失败:%@", error);
|
||||
}
|
||||
});
|
||||
}];
|
||||
}];
|
||||
}
|
||||
|
||||
+ (void)shareVideoToDouyin:(NSURL *)videoUrl completion:(void (^)(BOOL))completion {
|
||||
if (!videoUrl) {
|
||||
if (completion) {
|
||||
completion(NO);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// 检查文件是否存在
|
||||
if (![[NSFileManager defaultManager] fileExistsAtPath:videoUrl.path]) {
|
||||
if (completion) {
|
||||
completion(NO);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// 将视频保存到相册
|
||||
[self requestPhotoLibraryAuthorizationWithCompletion:^(BOOL granted) {
|
||||
if (!granted) {
|
||||
if (completion) {
|
||||
completion(NO);
|
||||
}
|
||||
[self showPhotoLibraryPermissionAlert];
|
||||
return;
|
||||
}
|
||||
|
||||
[[PHPhotoLibrary sharedPhotoLibrary] performChanges:^{
|
||||
[PHAssetChangeRequest creationRequestForAssetFromVideoAtFileURL:videoUrl];
|
||||
} completionHandler:^(BOOL success, NSError * _Nullable error) {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
if (success) {
|
||||
// 获取刚保存的视频的资源标识符
|
||||
PHFetchOptions *fetchOptions = [[PHFetchOptions alloc] init];
|
||||
fetchOptions.sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:@"creationDate" ascending:NO]];
|
||||
PHFetchResult *fetchResult = [PHAsset fetchAssetsWithMediaType:PHAssetMediaTypeVideo options:fetchOptions];
|
||||
|
||||
if (fetchResult.firstObject) {
|
||||
PHAsset *asset = fetchResult.firstObject;
|
||||
NSString *localIdentifier = asset.localIdentifier;
|
||||
|
||||
// 构建抖音分享URL
|
||||
NSString *urlString = [NSString stringWithFormat:@"%@?video=%@&from=app_%@",
|
||||
kDouyinShareScheme,
|
||||
[self encodeString:localIdentifier],
|
||||
[self encodeString:@"msext"]];
|
||||
|
||||
NSURL *douyinURL = [NSURL URLWithString:urlString];
|
||||
if ([[UIApplication sharedApplication] canOpenURL:douyinURL]) {
|
||||
if (@available(iOS 10.0, *)) {
|
||||
[[UIApplication sharedApplication] openURL:douyinURL options:@{} completionHandler:^(BOOL success) {
|
||||
// 这里不调用completion,等待抖音返回再调用
|
||||
}];
|
||||
} else {
|
||||
[[UIApplication sharedApplication] openURL:douyinURL];
|
||||
}
|
||||
} else {
|
||||
if (completion) {
|
||||
completion(NO);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (completion) {
|
||||
completion(NO);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (completion) {
|
||||
completion(NO);
|
||||
}
|
||||
NSLog(@"保存视频到相册失败:%@", error);
|
||||
}
|
||||
});
|
||||
}];
|
||||
}];
|
||||
}
|
||||
|
||||
+ (void)shareHashtagToDouyin:(NSString *)hashtagName completion:(void (^)(BOOL))completion {
|
||||
if (!hashtagName || hashtagName.length == 0) {
|
||||
if (completion) {
|
||||
completion(NO);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// 构建打开话题挑战的URL
|
||||
NSString *urlString = [NSString stringWithFormat:@"%@%@",
|
||||
kDouyinHashtagScheme,
|
||||
[self encodeString:hashtagName]];
|
||||
|
||||
NSURL *douyinURL = [NSURL URLWithString:urlString];
|
||||
if ([[UIApplication sharedApplication] canOpenURL:douyinURL]) {
|
||||
if (@available(iOS 10.0, *)) {
|
||||
[[UIApplication sharedApplication] openURL:douyinURL options:@{} completionHandler:^(BOOL success) {
|
||||
if (completion) {
|
||||
completion(success);
|
||||
}
|
||||
}];
|
||||
} else {
|
||||
BOOL success = [[UIApplication sharedApplication] openURL:douyinURL];
|
||||
if (completion) {
|
||||
completion(success);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (completion) {
|
||||
completion(NO);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+ (void)openDouyinUserProfile:(NSString *)userId completion:(void (^)(BOOL))completion {
|
||||
if (!userId || userId.length == 0) {
|
||||
if (completion) {
|
||||
completion(NO);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// 构建打开用户主页的URL
|
||||
NSString *urlString = [NSString stringWithFormat:@"%@%@",
|
||||
kDouyinUserProfileScheme,
|
||||
[self encodeString:userId]];
|
||||
|
||||
NSURL *douyinURL = [NSURL URLWithString:urlString];
|
||||
if ([[UIApplication sharedApplication] canOpenURL:douyinURL]) {
|
||||
if (@available(iOS 10.0, *)) {
|
||||
[[UIApplication sharedApplication] openURL:douyinURL options:@{} completionHandler:^(BOOL success) {
|
||||
if (completion) {
|
||||
completion(success);
|
||||
}
|
||||
}];
|
||||
} else {
|
||||
BOOL success = [[UIApplication sharedApplication] openURL:douyinURL];
|
||||
if (completion) {
|
||||
completion(success);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (completion) {
|
||||
completion(NO);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+ (void)requestPhotoLibraryAuthorizationWithCompletion:(void (^)(BOOL granted))completion {
|
||||
PHAuthorizationStatus status = [PHPhotoLibrary authorizationStatus];
|
||||
|
||||
switch (status) {
|
||||
case PHAuthorizationStatusAuthorized: {
|
||||
if (completion) {
|
||||
completion(YES);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case PHAuthorizationStatusNotDetermined: {
|
||||
[PHPhotoLibrary requestAuthorization:^(PHAuthorizationStatus newStatus) {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
if (completion) {
|
||||
completion(newStatus == PHAuthorizationStatusAuthorized);
|
||||
}
|
||||
});
|
||||
}];
|
||||
break;
|
||||
}
|
||||
|
||||
case PHAuthorizationStatusDenied:
|
||||
case PHAuthorizationStatusRestricted: {
|
||||
if (completion) {
|
||||
completion(NO);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default: {
|
||||
if (completion) {
|
||||
completion(NO);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+ (NSString *)encodeString:(NSString *)string {
|
||||
if (!string) return @"";
|
||||
return [string stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLQueryAllowedCharacterSet]];
|
||||
}
|
||||
|
||||
+ (NSString *)getUrlParam:(NSString *)url paramName:(NSString *)name {
|
||||
NSError *error;
|
||||
NSString *regTags = [[NSString alloc] initWithFormat:@"(^|&|\\?)%@=([^&]*)(&|$)", name];
|
||||
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:regTags options:NSRegularExpressionCaseInsensitive error:&error];
|
||||
|
||||
NSTextCheckingResult *match = [regex firstMatchInString:url options:0 range:NSMakeRange(0, [url length])];
|
||||
if (match) {
|
||||
NSString *paramValue = [url substringWithRange:[match rangeAtIndex:2]];
|
||||
return paramValue;
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
+ (void)showAppNotInstalledAlert:(NSString *)appName {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:[NSString stringWithFormat:@"%@未安装", appName]
|
||||
message:[NSString stringWithFormat:@"您的设备未安装%@客户端,无法进行分享", appName]
|
||||
preferredStyle:UIAlertControllerStyleAlert];
|
||||
|
||||
UIAlertAction *okAction = [UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDefault handler:nil];
|
||||
[alertController addAction:okAction];
|
||||
|
||||
UIViewController *topVC = [self topViewController];
|
||||
[topVC presentViewController:alertController animated:YES completion:nil];
|
||||
});
|
||||
}
|
||||
|
||||
+ (void)showPhotoLibraryPermissionAlert {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"无法访问相册"
|
||||
message:@"请在设置中开启相册权限,以便分享到抖音"
|
||||
preferredStyle:UIAlertControllerStyleAlert];
|
||||
|
||||
UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:nil];
|
||||
UIAlertAction *settingsAction = [UIAlertAction actionWithTitle:@"去设置" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
|
||||
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:UIApplicationOpenSettingsURLString]];
|
||||
}];
|
||||
|
||||
[alertController addAction:cancelAction];
|
||||
[alertController addAction:settingsAction];
|
||||
|
||||
UIViewController *topVC = [self topViewController];
|
||||
[topVC presentViewController:alertController animated:YES completion:nil];
|
||||
});
|
||||
}
|
||||
|
||||
+ (UIViewController *)topViewController {
|
||||
UIViewController *rootViewController = [UIApplication sharedApplication].keyWindow.rootViewController;
|
||||
while (rootViewController.presentedViewController) {
|
||||
rootViewController = rootViewController.presentedViewController;
|
||||
}
|
||||
|
||||
if ([rootViewController isKindOfClass:[UINavigationController class]]) {
|
||||
UINavigationController *navigationController = (UINavigationController *)rootViewController;
|
||||
return navigationController.visibleViewController;
|
||||
}
|
||||
|
||||
if ([rootViewController isKindOfClass:[UITabBarController class]]) {
|
||||
UITabBarController *tabBarController = (UITabBarController *)rootViewController;
|
||||
UIViewController *selectedViewController = tabBarController.selectedViewController;
|
||||
|
||||
if ([selectedViewController isKindOfClass:[UINavigationController class]]) {
|
||||
UINavigationController *navigationController = (UINavigationController *)selectedViewController;
|
||||
return navigationController.visibleViewController;
|
||||
}
|
||||
|
||||
return selectedViewController;
|
||||
}
|
||||
|
||||
return rootViewController;
|
||||
}
|
||||
|
||||
+ (void)showImageShareGuidance {
|
||||
NSString *title = @"抖音分享准备完成";
|
||||
NSString *message = @"✅ 图片已复制到剪贴板\n\n"
|
||||
@"🎯 分享到好友或群聊步骤:\n"
|
||||
@"1️⃣ 打开抖音,点击右下角「消息」\n"
|
||||
@"2️⃣ 选择好友或群聊进入聊天\n"
|
||||
@"3️⃣ 在输入框长按粘贴图片\n"
|
||||
@"4️⃣ 点击发送即可分享";
|
||||
|
||||
[self showGuidanceAlertWithTitle:title message:message];
|
||||
}
|
||||
|
||||
+ (void)showTextShareGuidance {
|
||||
NSString *title = @"抖音分享准备完成";
|
||||
NSString *message = @"✅ 内容已复制到剪贴板\n\n"
|
||||
@"🎯 分享到好友或群聊步骤:\n"
|
||||
@"1️⃣ 打开抖音,点击右下角「消息」\n"
|
||||
@"2️⃣ 选择好友或群聊进入聊天\n"
|
||||
@"3️⃣ 在输入框长按粘贴内容\n"
|
||||
@"4️⃣ 点击发送即可分享";
|
||||
|
||||
[self showGuidanceAlertWithTitle:title message:message];
|
||||
}
|
||||
|
||||
+ (void)showGuidanceAlertWithTitle:(NSString *)title message:(NSString *)message {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
UIAlertController *alert = [UIAlertController alertControllerWithTitle:title
|
||||
message:message
|
||||
preferredStyle:UIAlertControllerStyleAlert];
|
||||
|
||||
UIAlertAction *openAction = [UIAlertAction actionWithTitle:@"立即打开抖音"
|
||||
style:UIAlertActionStyleDefault
|
||||
handler:^(UIAlertAction * _Nonnull action) {
|
||||
NSURL *douyinURL = [NSURL URLWithString:kDouyinScheme];
|
||||
if ([[UIApplication sharedApplication] canOpenURL:douyinURL]) {
|
||||
[[UIApplication sharedApplication] openURL:douyinURL options:@{} completionHandler:nil];
|
||||
}
|
||||
}];
|
||||
|
||||
UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:@"稍后分享"
|
||||
style:UIAlertActionStyleCancel
|
||||
handler:nil];
|
||||
|
||||
[alert addAction:openAction];
|
||||
[alert addAction:cancelAction];
|
||||
|
||||
// 获取顶层视图控制器
|
||||
UIViewController *topVC = [self getTopViewController];
|
||||
if (topVC) {
|
||||
[topVC presentViewController:alert animated:YES completion:nil];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
+ (UIViewController *)getTopViewController {
|
||||
UIViewController *topViewController = [UIApplication sharedApplication].keyWindow.rootViewController;
|
||||
while (topViewController.presentedViewController) {
|
||||
topViewController = topViewController.presentedViewController;
|
||||
}
|
||||
return topViewController;
|
||||
}
|
||||
|
||||
@end
|
||||
222
msext/Class/Utils/QQAppIDValidator.m
Normal file
222
msext/Class/Utils/QQAppIDValidator.m
Normal file
@@ -0,0 +1,222 @@
|
||||
//
|
||||
// QQAppIDValidator.m
|
||||
// msext
|
||||
//
|
||||
// Created on 2025/06/16.
|
||||
// Copyright © 2025年. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <UIKit/UIKit.h>
|
||||
#import "QQShareManager.h"
|
||||
|
||||
/**
|
||||
* QQ AppID配置验证工具
|
||||
* 用于验证无SDK方式的QQ分享配置是否正确
|
||||
*/
|
||||
@interface QQAppIDValidator : NSObject
|
||||
|
||||
/**
|
||||
* 执行完整的QQ分享配置检查
|
||||
* 包括AppID、URL Schemes、回调配置等
|
||||
*/
|
||||
+ (void)performCompleteValidation;
|
||||
|
||||
/**
|
||||
* 显示配置检查结果
|
||||
*/
|
||||
+ (void)showValidationResults;
|
||||
|
||||
/**
|
||||
* 生成QQ分享测试URL用于调试
|
||||
*/
|
||||
+ (NSString *)generateTestQQShareURL;
|
||||
|
||||
@end
|
||||
|
||||
@implementation QQAppIDValidator
|
||||
|
||||
+ (void)performCompleteValidation {
|
||||
NSLog(@"\n🔍 开始QQ分享配置验证...\n");
|
||||
|
||||
// 1. 检查QQ安装状态
|
||||
BOOL isQQInstalled = [QQShareManager isQQInstalled];
|
||||
NSLog(@"1️⃣ QQ安装检查: %@", isQQInstalled ? @"✅ 已安装" : @"❌ 未安装");
|
||||
|
||||
// 2. 检查AppID配置
|
||||
BOOL isAppIDValid = [QQShareManager validateQQAppIDConfiguration];
|
||||
NSString *appID = [QQShareManager getCurrentQQAppID];
|
||||
NSLog(@"2️⃣ AppID配置检查: %@", isAppIDValid ? [NSString stringWithFormat:@"✅ 有效 (%@)", appID] : @"❌ 无效");
|
||||
|
||||
// 3. 检查URL Schemes配置
|
||||
[self validateURLSchemes];
|
||||
|
||||
// 4. 检查回调配置
|
||||
[self validateCallbackConfiguration];
|
||||
|
||||
// 5. 生成测试URL
|
||||
NSString *testURL = [self generateTestQQShareURL];
|
||||
NSLog(@"5️⃣ 测试分享URL: %@", testURL);
|
||||
|
||||
NSLog(@"\n✅ QQ分享配置验证完成\n");
|
||||
}
|
||||
|
||||
+ (void)validateURLSchemes {
|
||||
NSArray *requiredSchemes = @[@"mqqapi", @"mqq", @"mqqOpensdkSSoLogin", @"mqqopensdkapiV2", @"mqqopensdkapiV3", @"mqqopensdkapiV4", @"wtloginmqq2", @"mqzone"];
|
||||
|
||||
BOOL allSchemesConfigured = YES;
|
||||
NSMutableArray *missingSchemes = [NSMutableArray array];
|
||||
|
||||
for (NSString *scheme in requiredSchemes) {
|
||||
NSString *testURLString = [NSString stringWithFormat:@"%@://test", scheme];
|
||||
NSURL *testURL = [NSURL URLWithString:testURLString];
|
||||
|
||||
if (![[UIApplication sharedApplication] canOpenURL:testURL]) {
|
||||
allSchemesConfigured = NO;
|
||||
[missingSchemes addObject:scheme];
|
||||
}
|
||||
}
|
||||
|
||||
if (allSchemesConfigured) {
|
||||
NSLog(@"3️⃣ URL Schemes配置: ✅ 完整");
|
||||
} else {
|
||||
NSLog(@"3️⃣ URL Schemes配置: ❌ 缺少 %@", [missingSchemes componentsJoinedByString:@", "]);
|
||||
NSLog(@" 请在Info.plist的LSApplicationQueriesSchemes中添加缺少的schemes");
|
||||
}
|
||||
}
|
||||
|
||||
+ (void)validateCallbackConfiguration {
|
||||
// 检查msext回调scheme是否配置
|
||||
NSURL *callbackURL = [NSURL URLWithString:@"msext://test"];
|
||||
NSBundle *mainBundle = [NSBundle mainBundle];
|
||||
NSDictionary *infoPlist = [mainBundle infoDictionary];
|
||||
NSArray *urlTypes = infoPlist[@"CFBundleURLTypes"];
|
||||
|
||||
BOOL callbackConfigured = NO;
|
||||
for (NSDictionary *urlType in urlTypes) {
|
||||
NSArray *schemes = urlType[@"CFBundleURLSchemes"];
|
||||
if ([schemes containsObject:@"msext"]) {
|
||||
callbackConfigured = YES;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
NSLog(@"4️⃣ 回调配置检查: %@", callbackConfigured ? @"✅ 正确" : @"❌ 缺失msext scheme");
|
||||
if (!callbackConfigured) {
|
||||
NSLog(@" 请在Info.plist的CFBundleURLTypes中添加msext scheme配置");
|
||||
}
|
||||
}
|
||||
|
||||
+ (NSString *)generateTestQQShareURL {
|
||||
NSString *appID = [QQShareManager getCurrentQQAppID];
|
||||
if (!appID) {
|
||||
return @"❌ 无法生成测试URL:AppID未配置";
|
||||
}
|
||||
|
||||
// 构建基础的QQ分享测试URL
|
||||
NSMutableString *testURL = [NSMutableString stringWithString:@"mqqapi://share/to_fri?"];
|
||||
[testURL appendString:@"version=1"];
|
||||
[testURL appendString:@"&cflag=0"];
|
||||
[testURL appendString:@"&src_type=app"];
|
||||
[testURL appendString:@"&sdkv=2.9.0"];
|
||||
[testURL appendString:@"&sdkp=i"];
|
||||
[testURL appendFormat:@"&appid=%@", appID];
|
||||
[testURL appendString:@"&app_name=进贤聚友棋牌"];
|
||||
[testURL appendString:@"&callback_type=scheme"];
|
||||
[testURL appendString:@"&callback_name=msext"];
|
||||
[testURL appendString:@"&req_type=0"];
|
||||
[testURL appendString:@"&title=测试标题"];
|
||||
[testURL appendString:@"&description=测试描述"];
|
||||
|
||||
return testURL;
|
||||
}
|
||||
|
||||
+ (void)showValidationResults {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"QQ分享配置验证"
|
||||
message:@"请查看控制台日志以获取详细的验证结果"
|
||||
preferredStyle:UIAlertControllerStyleAlert];
|
||||
|
||||
UIAlertAction *validateAction = [UIAlertAction actionWithTitle:@"重新验证"
|
||||
style:UIAlertActionStyleDefault
|
||||
handler:^(UIAlertAction *action) {
|
||||
[self performCompleteValidation];
|
||||
}];
|
||||
|
||||
UIAlertAction *testAction = [UIAlertAction actionWithTitle:@"测试分享"
|
||||
style:UIAlertActionStyleDefault
|
||||
handler:^(UIAlertAction *action) {
|
||||
[self performTestShare];
|
||||
}];
|
||||
|
||||
UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:@"关闭"
|
||||
style:UIAlertActionStyleCancel
|
||||
handler:nil];
|
||||
|
||||
[alert addAction:validateAction];
|
||||
[alert addAction:testAction];
|
||||
[alert addAction:cancelAction];
|
||||
|
||||
UIViewController *rootVC = [UIApplication sharedApplication].keyWindow.rootViewController;
|
||||
UIViewController *topVC = [self topViewController:rootVC];
|
||||
[topVC presentViewController:alert animated:YES completion:nil];
|
||||
});
|
||||
}
|
||||
|
||||
+ (void)performTestShare {
|
||||
NSLog(@"🧪 开始QQ分享测试...");
|
||||
|
||||
[QQShareManager shareToQQFriend:QQShareTypeText
|
||||
title:@"QQ分享配置测试"
|
||||
description:@"如果您看到这条消息,说明QQ分享配置正确"
|
||||
thumbImage:nil
|
||||
url:nil
|
||||
image:nil
|
||||
completion:^(BOOL success) {
|
||||
NSLog(@"🧪 QQ分享测试结果: %@", success ? @"✅ 成功" : @"❌ 失败");
|
||||
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
NSString *message = success ? @"QQ分享测试成功!配置正确。" : @"QQ分享测试失败!请检查配置。";
|
||||
UIAlertController *resultAlert = [UIAlertController alertControllerWithTitle:@"测试结果"
|
||||
message:message
|
||||
preferredStyle:UIAlertControllerStyleAlert];
|
||||
UIAlertAction *okAction = [UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDefault handler:nil];
|
||||
[resultAlert addAction:okAction];
|
||||
|
||||
UIViewController *rootVC = [UIApplication sharedApplication].keyWindow.rootViewController;
|
||||
UIViewController *topVC = [self topViewController:rootVC];
|
||||
[topVC presentViewController:resultAlert animated:YES completion:nil];
|
||||
});
|
||||
}];
|
||||
}
|
||||
|
||||
+ (UIViewController *)topViewController:(UIViewController *)rootViewController {
|
||||
if (rootViewController.presentedViewController == nil) {
|
||||
return rootViewController;
|
||||
}
|
||||
|
||||
if ([rootViewController.presentedViewController isKindOfClass:[UINavigationController class]]) {
|
||||
UINavigationController *navigationController = (UINavigationController *)rootViewController.presentedViewController;
|
||||
UIViewController *lastViewController = [[navigationController viewControllers] lastObject];
|
||||
return [self topViewController:lastViewController];
|
||||
}
|
||||
|
||||
if ([rootViewController.presentedViewController isKindOfClass:[UITabBarController class]]) {
|
||||
UITabBarController *tabBarController = (UITabBarController *)rootViewController.presentedViewController;
|
||||
UIViewController *selectedViewController = tabBarController.selectedViewController;
|
||||
return [self topViewController:selectedViewController];
|
||||
}
|
||||
|
||||
return [self topViewController:rootViewController.presentedViewController];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
// 便捷调用方法
|
||||
void validateQQShareConfiguration(void) {
|
||||
[QQAppIDValidator performCompleteValidation];
|
||||
}
|
||||
|
||||
void showQQShareValidationDialog(void) {
|
||||
[QQAppIDValidator showValidationResults];
|
||||
}
|
||||
179
msext/Class/Utils/QQShareManager.h
Normal file
179
msext/Class/Utils/QQShareManager.h
Normal file
@@ -0,0 +1,179 @@
|
||||
//
|
||||
// QQShareManager.h
|
||||
// msext
|
||||
//
|
||||
// Created on 2025/06/15.
|
||||
// Copyright © 2025年. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
typedef NS_ENUM(NSInteger, QQShareType) {
|
||||
QQShareTypeText, // 纯文本分享
|
||||
QQShareTypeImage, // 图片分享
|
||||
QQShareTypeNews, // 新闻/网页分享
|
||||
QQShareTypeAudio, // 音频分享
|
||||
QQShareTypeVideo // 视频分享
|
||||
};
|
||||
|
||||
@interface QQShareManager : NSObject
|
||||
|
||||
/**
|
||||
* 检查QQ是否已安装
|
||||
* @return BOOL 是否已安装QQ
|
||||
*/
|
||||
+ (BOOL)isQQInstalled;
|
||||
|
||||
/**
|
||||
* 验证QQ AppID配置是否正确
|
||||
* @return BOOL AppID是否有效配置
|
||||
*/
|
||||
+ (BOOL)validateQQAppIDConfiguration;
|
||||
|
||||
/**
|
||||
* 获取当前配置的QQ AppID
|
||||
* @return NSString 当前的QQ AppID,如果未配置返回nil
|
||||
*/
|
||||
+ (NSString * _Nullable)getCurrentQQAppID;
|
||||
|
||||
/**
|
||||
* 分享到QQ好友
|
||||
* @param type 分享类型
|
||||
* @param title 标题
|
||||
* @param description 描述
|
||||
* @param thumbImage 缩略图
|
||||
* @param url 链接URL
|
||||
* @param image 图片(仅图片分享时使用)
|
||||
* @param completion 完成回调
|
||||
*/
|
||||
+ (void)shareToQQFriend:(QQShareType)type
|
||||
title:(NSString * _Nullable)title
|
||||
description:(NSString * _Nullable)description
|
||||
thumbImage:(UIImage * _Nullable)thumbImage
|
||||
url:(NSString * _Nullable)url
|
||||
image:(UIImage * _Nullable)image
|
||||
completion:(void(^_Nullable)(BOOL success))completion;
|
||||
|
||||
/**
|
||||
* 分享到QQ空间
|
||||
* @param type 分享类型
|
||||
* @param title 标题
|
||||
* @param description 描述
|
||||
* @param thumbImage 缩略图
|
||||
* @param url 链接URL
|
||||
* @param images 图片数组(可多张图片)
|
||||
* @param completion 完成回调
|
||||
*/
|
||||
+ (void)shareToQZone:(QQShareType)type
|
||||
title:(NSString * _Nullable)title
|
||||
description:(NSString * _Nullable)description
|
||||
thumbImage:(UIImage * _Nullable)thumbImage
|
||||
url:(NSString * _Nullable)url
|
||||
images:(NSArray<UIImage *> * _Nullable)images
|
||||
completion:(void(^_Nullable)(BOOL success))completion;
|
||||
|
||||
/**
|
||||
* 简化版QQ分享到好友(解决900101错误)
|
||||
* 使用最基础的参数格式,避免复杂参数导致的错误
|
||||
* @param title 标题
|
||||
* @param description 描述
|
||||
* @param url 链接URL(可为nil)
|
||||
* @param completion 完成回调
|
||||
*/
|
||||
+ (void)simpleShareToQQFriend:(NSString * _Nullable)title
|
||||
description:(NSString * _Nullable)description
|
||||
url:(NSString * _Nullable)url
|
||||
completion:(void(^_Nullable)(BOOL success))completion;
|
||||
|
||||
/**
|
||||
* 使用系统分享面板进行QQ分享(最可靠的方案)
|
||||
* @param title 分享标题
|
||||
* @param description 分享描述
|
||||
* @param url 分享链接
|
||||
* @param viewController 用于呈现分享面板的视图控制器
|
||||
* @param completion 分享完成回调
|
||||
*/
|
||||
+ (void)shareWithSystemShare:(NSString * _Nullable)title
|
||||
description:(NSString * _Nullable)description
|
||||
url:(NSString * _Nullable)url
|
||||
fromViewController:(UIViewController *)viewController
|
||||
completion:(void (^ _Nullable)(BOOL success))completion;
|
||||
|
||||
/**
|
||||
* 使用系统分享面板进行QQ分享(自动获取顶层视图控制器)
|
||||
* @param title 分享标题
|
||||
* @param description 分享描述
|
||||
* @param url 分享链接
|
||||
* @param completion 分享完成回调
|
||||
*/
|
||||
+ (void)shareWithSystemShareAuto:(NSString * _Nullable)title
|
||||
description:(NSString * _Nullable)description
|
||||
url:(NSString * _Nullable)url
|
||||
completion:(void (^ _Nullable)(BOOL success))completion;
|
||||
|
||||
/**
|
||||
* 尝试强制弹出QQ会话选择列表
|
||||
* 使用多种URL格式尝试打开QQ的好友选择界面
|
||||
* @param completion 完成回调
|
||||
*/
|
||||
+ (void)forceOpenQQSessionSelector:(void(^_Nullable)(BOOL success))completion;
|
||||
|
||||
/**
|
||||
* 使用多种方式尝试QQ分享
|
||||
* 按优先级尝试不同的QQ分享方式,直到找到有效的
|
||||
* @param title 标题
|
||||
* @param description 描述
|
||||
* @param completion 完成回调
|
||||
*/
|
||||
+ (void)tryMultipleQQShareMethods:(NSString * _Nullable)title
|
||||
description:(NSString * _Nullable)description
|
||||
completion:(void(^_Nullable)(BOOL success))completion;
|
||||
|
||||
/**
|
||||
* 处理从QQ返回的URL
|
||||
* 需要在 AppDelegate 的 application:openURL:options: 方法中调用
|
||||
*/
|
||||
+ (BOOL)handleOpenURL:(NSURL *)url;
|
||||
|
||||
/**
|
||||
* 验证和修复URL格式
|
||||
* @param url 原始URL字符串
|
||||
* @return 修复后的有效URL,如果无法修复返回nil
|
||||
*/
|
||||
+ (NSString * _Nullable)validateAndFixURL:(NSString * _Nullable)url;
|
||||
|
||||
/**
|
||||
* 获取应用启动图用作分享缩略图(已弃用,建议使用getAppIcon)
|
||||
* @return 启动图UIImage对象
|
||||
*/
|
||||
+ (UIImage * _Nullable)getAppLaunchImage;
|
||||
|
||||
/**
|
||||
* 获取应用桌面图标用作分享缩略图
|
||||
* @return 应用桌面图标UIImage对象
|
||||
*/
|
||||
+ (UIImage * _Nullable)getAppIcon;
|
||||
|
||||
/**
|
||||
* 检查是否可以安全呈现系统分享面板
|
||||
* @return BOOL 是否可以安全呈现
|
||||
*/
|
||||
+ (BOOL)canSafelyPresentSharePanel;
|
||||
|
||||
/**
|
||||
* 使用系统分享面板进行QQ分享(传递完整ShareContent对象)
|
||||
* 支持根据type字段自动选择分享类型:
|
||||
* - type=2:截图分享(包含屏幕截图+文字说明)
|
||||
* - 其他:纯文本分享(文字+链接+应用图标)
|
||||
* @param shareContent 完整的分享内容对象,需包含title、desc、webpageUrl、type字段
|
||||
* @param completion 分享完成回调
|
||||
*/
|
||||
+ (void)shareWithSystemShareContent:(id)shareContent
|
||||
completion:(void (^ _Nullable)(BOOL success))completion;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
1474
msext/Class/Utils/QQShareManager.m
Normal file
1474
msext/Class/Utils/QQShareManager.m
Normal file
File diff suppressed because it is too large
Load Diff
84
msext/Class/Utils/SharePanel.h
Normal file
84
msext/Class/Utils/SharePanel.h
Normal file
@@ -0,0 +1,84 @@
|
||||
//
|
||||
// SharePanel.h
|
||||
// msext
|
||||
//
|
||||
// Created on 2025/06/15.
|
||||
// Copyright © 2025年. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
// 分享类型枚举
|
||||
typedef NS_ENUM(NSInteger, ShareType) {
|
||||
ShareTypeWeChat, // 微信分享
|
||||
ShareTypeQQ, // QQ分享
|
||||
ShareTypeDouyin // 抖音分享
|
||||
};
|
||||
|
||||
// 分享内容模型
|
||||
@interface ShareContent : NSObject
|
||||
|
||||
@property (nonatomic, copy) NSString *title; // 标题
|
||||
@property (nonatomic, copy) NSString *desc; // 描述 (原名 description 与 NSObject 方法冲突)
|
||||
@property (nonatomic, copy) NSString *webpageUrl; // 链接
|
||||
@property (nonatomic, copy) NSString *sharefriend; // 好友分享,朋友圈分享
|
||||
@property (nonatomic, copy) NSString *type; // 好友分享,朋友圈分享
|
||||
|
||||
|
||||
@end
|
||||
|
||||
// 分享面板回调
|
||||
typedef void (^SharePanelCompletionBlock)(ShareType type, BOOL success);
|
||||
|
||||
@interface SharePanel : UIView
|
||||
|
||||
/**
|
||||
* 显示分享面板
|
||||
* @param content 分享内容
|
||||
* @param completion 分享完成后的回调
|
||||
*/
|
||||
+ (void)showWithContent:(ShareContent *)content
|
||||
completion:(nullable SharePanelCompletionBlock)completion;
|
||||
|
||||
/**
|
||||
* 显示分享面板(使用字典数据)
|
||||
* @param data 包含分享内容的字典
|
||||
* @param completion 分享完成后的回调
|
||||
*
|
||||
* 字典key说明:
|
||||
* - title: 标题
|
||||
* - description: 描述
|
||||
* - link: 链接URL
|
||||
* - thumbImage: 缩略图 (UIImage对象)
|
||||
* - image: 分享图片 (UIImage对象, 仅图片分享时使用)
|
||||
* - videoPath: 视频路径 (仅视频分享时使用)
|
||||
*/
|
||||
+ (void)showWithDictionary:(NSDictionary *)data
|
||||
completion:(nullable SharePanelCompletionBlock)completion;
|
||||
|
||||
/**
|
||||
* 隐藏分享面板
|
||||
*/
|
||||
+ (void)dismiss;
|
||||
|
||||
/**
|
||||
* QQ分享到好友
|
||||
* @param content 分享内容
|
||||
* @param completion 分享完成后的回调
|
||||
*/
|
||||
+ (void)shareToQQFriend:(ShareContent *)content
|
||||
completion:(nullable SharePanelCompletionBlock)completion;
|
||||
|
||||
/**
|
||||
* QQ分享到空间
|
||||
* @param content 分享内容
|
||||
* @param completion 分享完成后的回调
|
||||
*/
|
||||
+ (void)shareToQZone:(ShareContent *)content
|
||||
completion:(nullable SharePanelCompletionBlock)completion;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
662
msext/Class/Utils/SharePanel.m
Normal file
662
msext/Class/Utils/SharePanel.m
Normal file
@@ -0,0 +1,662 @@
|
||||
//
|
||||
// SharePanel.m
|
||||
// msext
|
||||
//
|
||||
// Created on 2025/06/15.
|
||||
// Copyright © 2025年. All rights reserved.
|
||||
//
|
||||
|
||||
#import "SharePanel.h"
|
||||
#import "WechatShareManager.h" // 微信分享管理器
|
||||
#import "QQShareManager.h" // QQ分享管理器
|
||||
#import "DouyinShareManager.h" // 抖音分享管理器
|
||||
|
||||
// 面板常量
|
||||
#define kPanelHeight 180.0f
|
||||
#define kButtonSize 60.0f
|
||||
#define kButtonMargin 30.0f
|
||||
#define kButtonTopMargin 40.0f
|
||||
#define kAnimationDuration 0.25f
|
||||
|
||||
@interface SharePanel()
|
||||
|
||||
@property (nonatomic, strong) UIView *contentView;
|
||||
@property (nonatomic, strong) UIButton *wechatButton;
|
||||
@property (nonatomic, strong) UIButton *qqButton;
|
||||
@property (nonatomic, strong) UIButton *douyinButton;
|
||||
@property (nonatomic, strong) UILabel *wechatLabel;
|
||||
@property (nonatomic, strong) UILabel *qqLabel;
|
||||
@property (nonatomic, strong) UILabel *douyinLabel;
|
||||
|
||||
@property (nonatomic, strong) ShareContent *shareContent;
|
||||
@property (nonatomic, copy) SharePanelCompletionBlock completionBlock;
|
||||
|
||||
// 私有方法声明
|
||||
- (UIViewController *)getTopViewController;
|
||||
|
||||
@end
|
||||
|
||||
static SharePanel *sharedPanel = nil;
|
||||
|
||||
@implementation ShareContent
|
||||
@end
|
||||
|
||||
@implementation SharePanel
|
||||
|
||||
#pragma mark - Lifecycle
|
||||
|
||||
+ (instancetype)sharedPanel {
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
sharedPanel = [[self alloc] initWithFrame:CGRectZero];
|
||||
});
|
||||
return sharedPanel;
|
||||
}
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame {
|
||||
CGRect screenBounds = [UIScreen mainScreen].bounds;
|
||||
self = [super initWithFrame:screenBounds];
|
||||
|
||||
if (self) {
|
||||
// 设置背景为半透明黑色
|
||||
self.backgroundColor = [UIColor colorWithWhite:0 alpha:0.5];
|
||||
|
||||
// 添加点击手势
|
||||
UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleBackgroundTap:)];
|
||||
[self addGestureRecognizer:tapGesture];
|
||||
|
||||
// 创建内容视图
|
||||
_contentView = [[UIView alloc] initWithFrame:CGRectMake(0, screenBounds.size.height, screenBounds.size.width, kPanelHeight)];
|
||||
_contentView.backgroundColor = [UIColor whiteColor];
|
||||
|
||||
// 圆角设置
|
||||
UIBezierPath *maskPath = [UIBezierPath bezierPathWithRoundedRect:_contentView.bounds
|
||||
byRoundingCorners:UIRectCornerTopLeft|UIRectCornerTopRight
|
||||
cornerRadii:CGSizeMake(10.0, 10.0)];
|
||||
CAShapeLayer *maskLayer = [CAShapeLayer layer];
|
||||
maskLayer.frame = _contentView.bounds;
|
||||
maskLayer.path = maskPath.CGPath;
|
||||
_contentView.layer.mask = maskLayer;
|
||||
|
||||
[self addSubview:_contentView];
|
||||
|
||||
// 创建分享按钮
|
||||
[self setupShareButtons];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
#pragma mark - UI Setup
|
||||
|
||||
- (void)setupShareButtons {
|
||||
CGFloat contentWidth = self.contentView.frame.size.width;
|
||||
CGFloat startX = (contentWidth - (3 * kButtonSize + 2 * kButtonMargin)) / 2;
|
||||
|
||||
// 微信分享按钮
|
||||
_wechatButton = [self createButtonWithImage:@"shareWechat" atIndex:0 startX:startX];
|
||||
|
||||
// 绑定点击事件为先关闭面板,然后执行微信分享
|
||||
[_wechatButton addTarget:self action:@selector(handleButtonClick:) forControlEvents:UIControlEventTouchUpInside];
|
||||
_wechatButton.tag = ShareTypeWeChat;
|
||||
[self.contentView addSubview:_wechatButton];
|
||||
|
||||
// 微信分享文字
|
||||
_wechatLabel = [self createLabelWithText:@"微信" forButton:_wechatButton];
|
||||
[self.contentView addSubview:_wechatLabel];
|
||||
|
||||
// QQ分享按钮
|
||||
_qqButton = [self createButtonWithImage:@"shareQQ" atIndex:1 startX:startX];
|
||||
|
||||
// 绑定点击事件为先关闭面板,然后执行QQ分享
|
||||
[_qqButton addTarget:self action:@selector(handleButtonClick:) forControlEvents:UIControlEventTouchUpInside];
|
||||
_qqButton.tag = ShareTypeQQ;
|
||||
[self.contentView addSubview:_qqButton];
|
||||
|
||||
// QQ分享文字
|
||||
_qqLabel = [self createLabelWithText:@"QQ" forButton:_qqButton];
|
||||
[self.contentView addSubview:_qqLabel];
|
||||
|
||||
// 抖音分享按钮
|
||||
_douyinButton = [self createButtonWithImage:@"shareDouyin" atIndex:2 startX:startX];
|
||||
|
||||
// 绑定点击事件为先关闭面板,然后执行抖音分享
|
||||
[_douyinButton addTarget:self action:@selector(handleButtonClick:) forControlEvents:UIControlEventTouchUpInside];
|
||||
_douyinButton.tag = ShareTypeDouyin;
|
||||
[self.contentView addSubview:_douyinButton];
|
||||
|
||||
// 抖音分享文字
|
||||
_douyinLabel = [self createLabelWithText:@"抖音" forButton:_douyinButton];
|
||||
[self.contentView addSubview:_douyinLabel];
|
||||
|
||||
// 添加取消按钮
|
||||
UIButton *cancelButton = [UIButton buttonWithType:UIButtonTypeSystem];
|
||||
cancelButton.frame = CGRectMake(0, kPanelHeight - 40, contentWidth, 40);
|
||||
[cancelButton setTitle:@"取消" forState:UIControlStateNormal];
|
||||
[cancelButton setTitleColor:[UIColor darkGrayColor] forState:UIControlStateNormal];
|
||||
cancelButton.titleLabel.font = [UIFont systemFontOfSize:16];
|
||||
[cancelButton addTarget:self action:@selector(cancelAction) forControlEvents:UIControlEventTouchUpInside];
|
||||
[self.contentView addSubview:cancelButton];
|
||||
|
||||
// 添加分隔线
|
||||
UIView *lineView = [[UIView alloc] initWithFrame:CGRectMake(0, kPanelHeight - 41, contentWidth, 1)];
|
||||
lineView.backgroundColor = [UIColor colorWithWhite:0.9 alpha:1.0];
|
||||
[self.contentView addSubview:lineView];
|
||||
}
|
||||
|
||||
- (UIButton *)createButtonWithImage:(NSString *)imageName atIndex:(NSInteger)index startX:(CGFloat)startX {
|
||||
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
|
||||
CGFloat x = startX + index * (kButtonSize + kButtonMargin);
|
||||
button.frame = CGRectMake(x, kButtonTopMargin, kButtonSize, kButtonSize);
|
||||
[button setImage:[UIImage imageNamed:imageName] forState:UIControlStateNormal];
|
||||
button.layer.cornerRadius = kButtonSize / 2;
|
||||
button.clipsToBounds = YES;
|
||||
return button;
|
||||
}
|
||||
|
||||
- (UILabel *)createLabelWithText:(NSString *)text forButton:(UIButton *)button {
|
||||
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, kButtonSize, 20)];
|
||||
label.center = CGPointMake(button.center.x, button.frame.origin.y + button.frame.size.height + 15);
|
||||
label.text = text;
|
||||
label.textColor = [UIColor darkGrayColor];
|
||||
label.font = [UIFont systemFontOfSize:12];
|
||||
label.textAlignment = NSTextAlignmentCenter;
|
||||
return label;
|
||||
}
|
||||
|
||||
#pragma mark - Public Methods
|
||||
|
||||
+ (void)showWithContent:(ShareContent *)content completion:(SharePanelCompletionBlock)completion {
|
||||
SharePanel *panel = [SharePanel sharedPanel];
|
||||
panel.shareContent = content;
|
||||
panel.completionBlock = completion;
|
||||
|
||||
UIWindow *keyWindow = [UIApplication sharedApplication].keyWindow;
|
||||
[keyWindow addSubview:panel];
|
||||
|
||||
[UIView animateWithDuration:kAnimationDuration animations:^{
|
||||
CGRect frame = panel.contentView.frame;
|
||||
frame.origin.y = [UIScreen mainScreen].bounds.size.height - kPanelHeight;
|
||||
panel.contentView.frame = frame;
|
||||
panel.backgroundColor = [UIColor colorWithWhite:0 alpha:0.5];
|
||||
}];
|
||||
}
|
||||
|
||||
+ (void)showWithDictionary:(NSDictionary *)data completion:(SharePanelCompletionBlock)completion {
|
||||
// 创建ShareContent对象并从字典中提取数据
|
||||
ShareContent *content = [[ShareContent alloc] init];
|
||||
|
||||
// 提取文本数据objectForKey
|
||||
|
||||
// content.title = data[@"title"];//标题
|
||||
// content.desc = data[@"description"]; // 更改为新的属性名 desc
|
||||
// content.webpageUrl = data[@"webpageUrl"];//网页链接
|
||||
// content.type = data[@"type"];//类型 1-网页/文字 2-截图
|
||||
// content.sharefriend = data[@"sharefriend"];// 是否分享给好友
|
||||
content.title = [data objectForKey:@"title"];//标题
|
||||
content.desc = [data objectForKey:@"description"]; // 更改为新的属性名 desc
|
||||
content.webpageUrl = [data objectForKey:@"webpageUrl"];//网页链接
|
||||
content.type = [data objectForKey:@"type"];//类型 1-网页/文字 2-截图
|
||||
content.sharefriend = [data objectForKey: @"sharefriend"];// 是否分享给好友
|
||||
|
||||
|
||||
// 使用现有方法显示分享面板
|
||||
[self showWithContent:content completion:completion];
|
||||
}
|
||||
|
||||
+ (void)dismiss {
|
||||
SharePanel *panel = [SharePanel sharedPanel];
|
||||
//[panel removeFromSuperview];
|
||||
[UIView animateWithDuration:kAnimationDuration animations:^{
|
||||
CGRect frame = panel.contentView.frame;
|
||||
frame.origin.y = [UIScreen mainScreen].bounds.size.height;
|
||||
panel.contentView.frame = frame;
|
||||
panel.backgroundColor = [UIColor colorWithWhite:0 alpha:0];
|
||||
} completion:^(BOOL finished) {
|
||||
[panel removeFromSuperview];
|
||||
}];
|
||||
}
|
||||
|
||||
+ (void)dismissWithCompletion:(void (^)(void))completion {
|
||||
SharePanel *panel = [SharePanel sharedPanel];
|
||||
[UIView animateWithDuration:kAnimationDuration animations:^{
|
||||
CGRect frame = panel.contentView.frame;
|
||||
frame.origin.y = [UIScreen mainScreen].bounds.size.height;
|
||||
panel.contentView.frame = frame;
|
||||
panel.backgroundColor = [UIColor colorWithWhite:0 alpha:0];
|
||||
} completion:^(BOOL finished) {
|
||||
[panel removeFromSuperview];
|
||||
if (completion) {
|
||||
completion();
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
#pragma mark - Actions
|
||||
|
||||
- (void)handleBackgroundTap:(UITapGestureRecognizer *)gesture {
|
||||
CGPoint point = [gesture locationInView:self];
|
||||
if (!CGRectContainsPoint(self.contentView.frame, point)) {
|
||||
[SharePanel dismiss];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)cancelAction {
|
||||
[SharePanel dismiss];
|
||||
}
|
||||
|
||||
- (void)wechatShareAction {
|
||||
[self handleWechatShareAction];
|
||||
}
|
||||
|
||||
- (void)qqShareAction {
|
||||
[self handleQQShareAction];
|
||||
}
|
||||
|
||||
- (void)douyinShareAction {
|
||||
[self handleDouyinShareAction];
|
||||
}
|
||||
|
||||
#pragma mark - Button Action Handlers
|
||||
|
||||
- (void)handleButtonClick:(UIButton *)sender {
|
||||
ShareType shareType = (ShareType)sender.tag;
|
||||
|
||||
// 先关闭面板,在面板完全关闭后再执行对应的分享操作
|
||||
__weak typeof(self) weakSelf = self;
|
||||
[SharePanel dismissWithCompletion:^{
|
||||
switch (shareType) {
|
||||
case ShareTypeWeChat:
|
||||
[weakSelf handleWechatShareAction];
|
||||
break;
|
||||
case ShareTypeQQ:
|
||||
[weakSelf handleQQShareAction];
|
||||
break;
|
||||
case ShareTypeDouyin:
|
||||
[weakSelf handleDouyinShareAction];
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)handleWechatShareAction {
|
||||
NSLog(@"🔍 [SharePanel] 处理微信分享");
|
||||
|
||||
ShareContent *content = self.shareContent;
|
||||
SharePanelCompletionBlock completionBlock = self.completionBlock;
|
||||
|
||||
// 使用WechatShareManager处理微信分享
|
||||
[WechatShareManager shareWithContent:content
|
||||
completion:^(BOOL success) {
|
||||
NSLog(@"微信分享结果: %@", success ? @"✅ 成功" : @"❌ 失败");
|
||||
if (completionBlock) {
|
||||
completionBlock(ShareTypeWeChat, success);
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)handleQQShareAction {
|
||||
NSLog(@"🔍 [SharePanel] 处理QQ分享");
|
||||
|
||||
ShareContent *content = self.shareContent;
|
||||
SharePanelCompletionBlock completionBlock = self.completionBlock;
|
||||
|
||||
// 使用QQShareManager处理QQ分享
|
||||
[QQShareManager shareWithSystemShareContent:content
|
||||
completion:^(BOOL success) {
|
||||
NSLog(@"QQ分享结果: %@", success ? @"✅ 成功" : @"❌ 失败");
|
||||
if (completionBlock) {
|
||||
completionBlock(ShareTypeQQ, success);
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)handleDouyinShareAction {
|
||||
NSLog(@"🔍 [SharePanel] 处理抖音分享");
|
||||
|
||||
ShareContent *content = self.shareContent;
|
||||
SharePanelCompletionBlock completionBlock = self.completionBlock;
|
||||
|
||||
// 使用DouyinShareManager处理抖音分享
|
||||
[DouyinShareManager shareWithContent:content
|
||||
completion:^(BOOL success) {
|
||||
NSLog(@"抖音分享结果: %@", success ? @"✅ 成功" : @"❌ 失败");
|
||||
if (completionBlock) {
|
||||
completionBlock(ShareTypeDouyin, success);
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
#pragma mark - Helper Methods
|
||||
|
||||
- (void)showErrorAlertWithMessage:(NSString *)message {
|
||||
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"分享失败"
|
||||
message:message
|
||||
preferredStyle:UIAlertControllerStyleAlert];
|
||||
|
||||
UIAlertAction *okAction = [UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDefault handler:nil];
|
||||
[alert addAction:okAction];
|
||||
|
||||
UIViewController *rootVC = [UIApplication sharedApplication].keyWindow.rootViewController;
|
||||
UIViewController *topVC = [self topViewController:rootVC];
|
||||
[topVC presentViewController:alert animated:YES completion:nil];
|
||||
}
|
||||
|
||||
- (UIViewController *)topViewController:(UIViewController *)rootViewController {
|
||||
if (rootViewController.presentedViewController == nil) {
|
||||
return rootViewController;
|
||||
}
|
||||
|
||||
if ([rootViewController.presentedViewController isKindOfClass:[UINavigationController class]]) {
|
||||
UINavigationController *navigationController = (UINavigationController *)rootViewController.presentedViewController;
|
||||
UIViewController *lastViewController = [[navigationController viewControllers] lastObject];
|
||||
return [self topViewController:lastViewController];
|
||||
}
|
||||
|
||||
if ([rootViewController.presentedViewController isKindOfClass:[UITabBarController class]]) {
|
||||
UITabBarController *tabBarController = (UITabBarController *)rootViewController.presentedViewController;
|
||||
UIViewController *selectedViewController = tabBarController.selectedViewController;
|
||||
return [self topViewController:selectedViewController];
|
||||
}
|
||||
|
||||
return [self topViewController:rootViewController.presentedViewController];
|
||||
}
|
||||
|
||||
- (UIImage *)getAppIconImage {
|
||||
// 获取应用的主Bundle
|
||||
NSBundle *mainBundle = [NSBundle mainBundle];
|
||||
|
||||
// 获取应用图标信息
|
||||
NSDictionary *infoDictionary = [mainBundle infoDictionary];
|
||||
NSArray *iconFiles = infoDictionary[@"CFBundleIcons"][@"CFBundlePrimaryIcon"][@"CFBundleIconFiles"];
|
||||
|
||||
// 如果找到图标文件
|
||||
if (iconFiles && [iconFiles count] > 0) {
|
||||
// 使用最后一个图标(通常是最高分辨率的)
|
||||
NSString *iconLastName = [iconFiles lastObject];
|
||||
UIImage *image = [UIImage imageNamed:iconLastName];
|
||||
return image;
|
||||
}
|
||||
|
||||
// 如果没有找到使用应用名称
|
||||
NSString *appIconName = infoDictionary[@"CFBundleIconFile"];
|
||||
if (appIconName) {
|
||||
return [UIImage imageNamed:appIconName];
|
||||
}
|
||||
|
||||
// 如果上面的方法都失败了,尝试直接使用应用图标名称
|
||||
UIImage *appIcon = [UIImage imageNamed:@"AppIcon"];
|
||||
if (appIcon) {
|
||||
return appIcon;
|
||||
}
|
||||
|
||||
// 如果实在找不到,返回一个1x1像素的透明图片(避免崩溃)
|
||||
UIGraphicsBeginImageContext(CGSizeMake(1, 1));
|
||||
UIImage *blankImage = UIGraphicsGetImageFromCurrentImageContext();
|
||||
UIGraphicsEndImageContext();
|
||||
|
||||
return blankImage;
|
||||
}
|
||||
|
||||
// 显示复制成功提示
|
||||
- (void)showCopySuccessAlert {
|
||||
// 创建一个新的UIWindow,设置为最高层级
|
||||
UIWindow *toastWindow = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
|
||||
toastWindow.windowLevel = UIWindowLevelStatusBar + 100; // 尽可能高的层级
|
||||
toastWindow.backgroundColor = [UIColor clearColor];
|
||||
|
||||
// 创建根视图控制器
|
||||
__block UIViewController *rootVC = [[UIViewController alloc] init];
|
||||
rootVC.view.backgroundColor = [UIColor clearColor];
|
||||
toastWindow.rootViewController = rootVC;
|
||||
|
||||
// 创建一个悬浮的提示框,显示在屏幕顶部
|
||||
UIView *toastView = [[UIView alloc] initWithFrame:CGRectMake(0, -60, toastWindow.bounds.size.width - 40, 60)];
|
||||
toastView.center = CGPointMake(CGRectGetMidX(toastWindow.bounds), toastView.center.y);
|
||||
toastView.backgroundColor = [UIColor colorWithRed:0.0 green:0.0 blue:0.0 alpha:0.8];
|
||||
toastView.layer.cornerRadius = 10;
|
||||
toastView.alpha = 0;
|
||||
|
||||
// 添加图标
|
||||
UIImageView *iconView = [[UIImageView alloc] initWithFrame:CGRectMake(15, 15, 30, 30)];
|
||||
UIImage *checkmarkImage = [self createCheckmarkImage];
|
||||
iconView.image = checkmarkImage;
|
||||
iconView.contentMode = UIViewContentModeScaleAspectFit;
|
||||
[toastView addSubview:iconView];
|
||||
|
||||
// 添加提示文本
|
||||
UILabel *messageLabel = [[UILabel alloc] initWithFrame:CGRectMake(55, 0, toastView.bounds.size.width - 70, toastView.bounds.size.height)];
|
||||
messageLabel.text = @"内容已复制到剪贴板,请在抖音中粘贴";
|
||||
messageLabel.textColor = [UIColor whiteColor];
|
||||
messageLabel.textAlignment = NSTextAlignmentLeft;
|
||||
messageLabel.numberOfLines = 2;
|
||||
messageLabel.font = [UIFont systemFontOfSize:14 weight:UIFontWeightMedium];
|
||||
[toastView addSubview:messageLabel];
|
||||
|
||||
// 将toast视图添加到window
|
||||
[rootVC.view addSubview:toastView];
|
||||
|
||||
// 显示窗口
|
||||
[toastWindow makeKeyAndVisible];
|
||||
|
||||
// 执行显示动画:从顶部滑出
|
||||
[UIView animateWithDuration:0.3 delay:0 options:UIViewAnimationOptionCurveEaseOut animations:^{
|
||||
toastView.frame = CGRectOffset(toastView.frame, 0, 80 + [self safeAreaInsets].top);
|
||||
toastView.alpha = 1.0;
|
||||
} completion:nil];
|
||||
|
||||
// 2秒后开始消失动画
|
||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
|
||||
// 消失动画
|
||||
[UIView animateWithDuration:0.3 delay:0 options:UIViewAnimationOptionCurveEaseIn animations:^{
|
||||
toastView.frame = CGRectOffset(toastView.frame, 0, -80 - [self safeAreaInsets].top);
|
||||
toastView.alpha = 0.0;
|
||||
} completion:^(BOOL finished) {
|
||||
// 释放窗口
|
||||
toastWindow.hidden = YES;
|
||||
// 设置为nil以释放内存
|
||||
rootVC = nil;
|
||||
}];
|
||||
});
|
||||
}
|
||||
|
||||
// 创建一个对勾图标
|
||||
- (UIImage *)createCheckmarkImage {
|
||||
UIGraphicsBeginImageContextWithOptions(CGSizeMake(30, 30), NO, [UIScreen mainScreen].scale);
|
||||
CGContextRef context = UIGraphicsGetCurrentContext();
|
||||
|
||||
// 绘制圆形背景
|
||||
CGContextSetFillColorWithColor(context, [UIColor colorWithRed:0.2 green:0.8 blue:0.4 alpha:1.0].CGColor);
|
||||
CGContextFillEllipseInRect(context, CGRectMake(0, 0, 30, 30));
|
||||
|
||||
// 绘制对勾
|
||||
CGContextSetStrokeColorWithColor(context, [UIColor whiteColor].CGColor);
|
||||
CGContextSetLineWidth(context, 2.5);
|
||||
CGContextSetLineCap(context, kCGLineCapRound);
|
||||
CGContextSetLineJoin(context, kCGLineJoinRound);
|
||||
|
||||
// 绘制对勾路径
|
||||
CGContextMoveToPoint(context, 8, 15);
|
||||
CGContextAddLineToPoint(context, 13, 20);
|
||||
CGContextAddLineToPoint(context, 22, 10);
|
||||
CGContextStrokePath(context);
|
||||
|
||||
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
|
||||
UIGraphicsEndImageContext();
|
||||
|
||||
return image;
|
||||
}
|
||||
|
||||
// 获取安全区域插入值
|
||||
- (UIEdgeInsets)safeAreaInsets {
|
||||
if (@available(iOS 11.0, *)) {
|
||||
return [UIApplication sharedApplication].keyWindow.safeAreaInsets;
|
||||
}
|
||||
return UIEdgeInsetsZero;
|
||||
}
|
||||
|
||||
// URL编码方法
|
||||
- (NSString *)encodeUrlString:(NSString *)string {
|
||||
if (!string) return @"";
|
||||
return [string stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLQueryAllowedCharacterSet]];
|
||||
}
|
||||
|
||||
#pragma mark - QQ分享独立方法
|
||||
|
||||
+ (void)shareToQQFriend:(ShareContent *)content
|
||||
completion:(SharePanelCompletionBlock)completion {
|
||||
|
||||
if (![QQShareManager isQQInstalled]) {
|
||||
[self showAlertWithMessage:@"您的设备未安装QQ客户端,无法进行QQ分享"];
|
||||
if (completion) {
|
||||
completion(ShareTypeQQ, NO);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// 准备分享内容
|
||||
NSString *title = content.title ?: @"游戏分享";
|
||||
NSString *description = content.desc ?: @"精彩游戏分享";
|
||||
NSString *shareUrl = content.webpageUrl;
|
||||
|
||||
// 准备缩略图
|
||||
UIImage *thumbImage = [UIImage imageNamed:@"sharelogo"] ?: [UIImage imageNamed:@"Icon180"];
|
||||
|
||||
// 根据内容选择分享类型
|
||||
QQShareType shareType = shareUrl ? QQShareTypeNews : QQShareTypeText;
|
||||
|
||||
// 使用QQShareManager的分享方法
|
||||
if (shareType == QQShareTypeText) {
|
||||
// 纯文本分享
|
||||
[QQShareManager shareToQQFriend:QQShareTypeText
|
||||
title:title
|
||||
description:description
|
||||
thumbImage:nil
|
||||
url:nil
|
||||
image:nil
|
||||
completion:^(BOOL success) {
|
||||
if (completion) {
|
||||
completion(ShareTypeQQ, success);
|
||||
}
|
||||
}];
|
||||
} else {
|
||||
// 链接分享
|
||||
[QQShareManager shareToQQFriend:QQShareTypeNews
|
||||
title:title
|
||||
description:description
|
||||
thumbImage:thumbImage
|
||||
url:shareUrl
|
||||
image:nil
|
||||
completion:^(BOOL success) {
|
||||
if (completion) {
|
||||
completion(ShareTypeQQ, success);
|
||||
}
|
||||
}];
|
||||
}
|
||||
}
|
||||
|
||||
+ (void)shareToQZone:(ShareContent *)content
|
||||
completion:(SharePanelCompletionBlock)completion {
|
||||
|
||||
if (![QQShareManager isQQInstalled]) {
|
||||
[self showAlertWithMessage:@"您的设备未安装QQ客户端,无法进行QQ空间分享"];
|
||||
if (completion) {
|
||||
completion(ShareTypeQQ, NO);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// 准备分享内容
|
||||
NSString *title = content.title ?: @"游戏分享";
|
||||
NSString *description = content.desc ?: @"精彩游戏分享";
|
||||
NSString *shareUrl = content.webpageUrl;
|
||||
|
||||
// 准备缩略图
|
||||
UIImage *thumbImage = [UIImage imageNamed:@"sharelogo"] ?: [UIImage imageNamed:@"Icon180"];
|
||||
|
||||
// 根据内容选择分享类型
|
||||
QQShareType shareType = shareUrl ? QQShareTypeNews : QQShareTypeText;
|
||||
|
||||
[QQShareManager shareToQZone:shareType
|
||||
title:title
|
||||
description:description
|
||||
thumbImage:thumbImage
|
||||
url:shareUrl
|
||||
images:thumbImage ? @[thumbImage] : nil
|
||||
completion:^(BOOL success) {
|
||||
if (completion) {
|
||||
completion(ShareTypeQQ, success);
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
+ (void)showAlertWithMessage:(NSString *)message {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"提示"
|
||||
message:message
|
||||
preferredStyle:UIAlertControllerStyleAlert];
|
||||
|
||||
UIAlertAction *okAction = [UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDefault handler:nil];
|
||||
[alertController addAction:okAction];
|
||||
|
||||
UIViewController *topVC = [self topViewController];
|
||||
[topVC presentViewController:alertController animated:YES completion:nil];
|
||||
});
|
||||
}
|
||||
|
||||
+ (UIViewController *)topViewController {
|
||||
UIViewController *rootViewController = [UIApplication sharedApplication].keyWindow.rootViewController;
|
||||
while (rootViewController.presentedViewController) {
|
||||
rootViewController = rootViewController.presentedViewController;
|
||||
}
|
||||
|
||||
if ([rootViewController isKindOfClass:[UINavigationController class]]) {
|
||||
UINavigationController *navigationController = (UINavigationController *)rootViewController;
|
||||
return navigationController.visibleViewController;
|
||||
}
|
||||
|
||||
if ([rootViewController isKindOfClass:[UITabBarController class]]) {
|
||||
UITabBarController *tabBarController = (UITabBarController *)rootViewController;
|
||||
UIViewController *selectedViewController = tabBarController.selectedViewController;
|
||||
|
||||
if ([selectedViewController isKindOfClass:[UINavigationController class]]) {
|
||||
UINavigationController *navigationController = (UINavigationController *)selectedViewController;
|
||||
return navigationController.visibleViewController;
|
||||
}
|
||||
|
||||
return selectedViewController;
|
||||
}
|
||||
|
||||
return rootViewController;
|
||||
}
|
||||
|
||||
// 获取最顶层的视图控制器(实例方法)
|
||||
- (UIViewController *)getTopViewController {
|
||||
UIViewController *topController = [UIApplication sharedApplication].keyWindow.rootViewController;
|
||||
while (topController.presentedViewController) {
|
||||
topController = topController.presentedViewController;
|
||||
}
|
||||
|
||||
if ([topController isKindOfClass:[UINavigationController class]]) {
|
||||
UINavigationController *navigationController = (UINavigationController *)topController;
|
||||
return navigationController.visibleViewController;
|
||||
}
|
||||
|
||||
if ([topController isKindOfClass:[UITabBarController class]]) {
|
||||
UITabBarController *tabBarController = (UITabBarController *)topController;
|
||||
UIViewController *selectedViewController = tabBarController.selectedViewController;
|
||||
|
||||
if ([selectedViewController isKindOfClass:[UINavigationController class]]) {
|
||||
UINavigationController *navigationController = (UINavigationController *)selectedViewController;
|
||||
return navigationController.visibleViewController;
|
||||
}
|
||||
|
||||
return selectedViewController;
|
||||
}
|
||||
|
||||
return topController;
|
||||
}
|
||||
|
||||
@end
|
||||
82
msext/Class/Utils/WechatShareManager.h
Normal file
82
msext/Class/Utils/WechatShareManager.h
Normal file
@@ -0,0 +1,82 @@
|
||||
//
|
||||
// WechatShareManager.h
|
||||
// msext
|
||||
//
|
||||
// Created on 2025/06/17.
|
||||
// Copyright © 2025年. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
typedef NS_ENUM(NSInteger, WechatShareType) {
|
||||
WechatShareTypeText, // 纯文本分享
|
||||
WechatShareTypeImage, // 图片分享
|
||||
WechatShareTypeLink, // 链接分享
|
||||
WechatShareTypeScreenshot // 截图分享
|
||||
};
|
||||
|
||||
typedef NS_ENUM(NSInteger, WechatScene) {
|
||||
WechatSceneSession, // 微信好友
|
||||
WechatSceneTimeline // 微信朋友圈
|
||||
};
|
||||
|
||||
@interface WechatShareManager : NSObject
|
||||
|
||||
/**
|
||||
* 检查微信是否已安装
|
||||
* @return BOOL 是否已安装微信
|
||||
*/
|
||||
+ (BOOL)isWechatInstalled;
|
||||
|
||||
/**
|
||||
* 使用ShareContent对象进行微信分享
|
||||
* @param shareContent 完整的分享内容对象,包含title、desc、webpageUrl、type、sharefriend字段
|
||||
* @param completion 分享完成回调
|
||||
*/
|
||||
+ (void)shareWithContent:(id)shareContent
|
||||
completion:(void (^ _Nullable)(BOOL success))completion;
|
||||
|
||||
/**
|
||||
* 分享链接到微信
|
||||
* @param url 链接URL
|
||||
* @param title 标题
|
||||
* @param description 描述
|
||||
* @param thumbImage 缩略图
|
||||
* @param scene 分享场景(好友/朋友圈)
|
||||
* @param completion 完成回调
|
||||
*/
|
||||
+ (void)shareLinkToWechat:(NSString *)url
|
||||
title:(NSString * _Nullable)title
|
||||
description:(NSString * _Nullable)description
|
||||
thumbImage:(UIImage * _Nullable)thumbImage
|
||||
scene:(WechatScene)scene
|
||||
completion:(void (^ _Nullable)(BOOL success))completion;
|
||||
|
||||
/**
|
||||
* 分享图片到微信
|
||||
* @param image 图片
|
||||
* @param tagName 标签名称
|
||||
* @param messageExt 扩展信息
|
||||
* @param thumbImage 缩略图
|
||||
* @param scene 分享场景(好友/朋友圈)
|
||||
* @param completion 完成回调
|
||||
*/
|
||||
+ (void)shareImageToWechat:(UIImage *)image
|
||||
tagName:(NSString * _Nullable)tagName
|
||||
messageExt:(NSString * _Nullable)messageExt
|
||||
thumbImage:(UIImage * _Nullable)thumbImage
|
||||
scene:(WechatScene)scene
|
||||
completion:(void (^ _Nullable)(BOOL success))completion;
|
||||
|
||||
/**
|
||||
* 获取应用图标作为缩略图
|
||||
* @return 应用图标UIImage对象
|
||||
*/
|
||||
+ (UIImage * _Nullable)getAppIconImage;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
265
msext/Class/Utils/WechatShareManager.m
Normal file
265
msext/Class/Utils/WechatShareManager.m
Normal file
@@ -0,0 +1,265 @@
|
||||
//
|
||||
// WechatShareManager.m
|
||||
// msext
|
||||
//
|
||||
// Created on 2025/06/17.
|
||||
// Copyright © 2025年. All rights reserved.
|
||||
//
|
||||
|
||||
#import "WechatShareManager.h"
|
||||
#import "WXApiManager.h"
|
||||
#import "WXApiRequestHandler.h"
|
||||
#import "FuncPublic.h"
|
||||
|
||||
@implementation WechatShareManager
|
||||
|
||||
#pragma mark - Public Methods
|
||||
|
||||
+ (BOOL)isWechatInstalled {
|
||||
return [WXApi isWXAppInstalled];
|
||||
}
|
||||
|
||||
+ (void)shareWithContent:(id)shareContent
|
||||
completion:(void (^ _Nullable)(BOOL success))completion {
|
||||
NSLog(@"🔍 [WechatShareManager] 开始微信分享");
|
||||
|
||||
// 检查微信是否已安装
|
||||
if (![self isWechatInstalled]) {
|
||||
NSLog(@"❌ [WechatShareManager] 微信未安装");
|
||||
if (completion) {
|
||||
completion(NO);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// 检查ShareContent对象
|
||||
if (!shareContent) {
|
||||
NSLog(@"❌ [WechatShareManager] ShareContent对象为空");
|
||||
if (completion) {
|
||||
completion(NO);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// 通过KVC从ShareContent对象中提取参数
|
||||
NSString *title = nil;
|
||||
NSString *description = nil;
|
||||
NSString *webpageUrl = nil;
|
||||
NSString *type = nil;
|
||||
NSString *sharefriend = nil;
|
||||
|
||||
@try {
|
||||
if ([shareContent respondsToSelector:@selector(valueForKey:)]) {
|
||||
title = [shareContent valueForKey:@"title"];
|
||||
description = [shareContent valueForKey:@"desc"];
|
||||
webpageUrl = [shareContent valueForKey:@"webpageUrl"];
|
||||
type = [shareContent valueForKey:@"type"];
|
||||
sharefriend = [shareContent valueForKey:@"sharefriend"];
|
||||
}
|
||||
|
||||
NSLog(@"🔍 [WechatShareManager] 从ShareContent提取参数:");
|
||||
NSLog(@"🔍 - 标题: %@", title ?: @"(无)");
|
||||
NSLog(@"🔍 - 描述: %@", description ?: @"(无)");
|
||||
NSLog(@"🔍 - 链接: %@", webpageUrl ?: @"(无)");
|
||||
NSLog(@"🔍 - 类型: %@", type ?: @"(无)");
|
||||
NSLog(@"🔍 - 分享目标: %@", sharefriend ?: @"(无)");
|
||||
|
||||
} @catch (NSException *exception) {
|
||||
NSLog(@"❌ [WechatShareManager] 解析ShareContent时发生异常: %@", exception.reason);
|
||||
if (completion) {
|
||||
completion(NO);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// 确定分享场景
|
||||
enum WXScene currentScene;
|
||||
if ([sharefriend intValue] == 1) {
|
||||
currentScene = WXSceneSession; // 微信好友
|
||||
NSLog(@"🔍 [WechatShareManager] 分享目标: 微信好友");
|
||||
} else {
|
||||
currentScene = WXSceneTimeline; // 微信朋友圈
|
||||
NSLog(@"🔍 [WechatShareManager] 分享目标: 微信朋友圈");
|
||||
}
|
||||
|
||||
// 获取应用图标作为缩略图
|
||||
UIImage *thumbImage = [self getAppIconImage];
|
||||
|
||||
// 根据分享类型选择分享方式
|
||||
BOOL isScreenshotShare = [type intValue] == 2;
|
||||
|
||||
if (isScreenshotShare) {
|
||||
// 截图分享
|
||||
NSLog(@"🔍 [WechatShareManager] 执行截图分享");
|
||||
|
||||
UIImage *screenImage = [FuncPublic getImageWithFullScreenshot];
|
||||
if (screenImage) {
|
||||
NSData *imageData = UIImageJPEGRepresentation(screenImage, 0.6);
|
||||
|
||||
[WXApiRequestHandler sendImageData:imageData
|
||||
TagName:[NSString stringWithFormat:@"%@游戏截图分享", @"进贤聚友棋牌"]
|
||||
MessageExt:description ?: @""
|
||||
Action:@"share_action"
|
||||
ThumbImage:thumbImage
|
||||
InScene:currentScene];
|
||||
|
||||
NSLog(@"✅ [WechatShareManager] 截图分享请求已发送");
|
||||
} else {
|
||||
NSLog(@"❌ [WechatShareManager] 截图获取失败");
|
||||
if (completion) {
|
||||
completion(NO);
|
||||
}
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
// 链接分享(默认)
|
||||
NSLog(@"🔍 [WechatShareManager] 执行链接分享");
|
||||
|
||||
[WXApiRequestHandler sendLinkURL:webpageUrl ?: @""
|
||||
TagName:[NSString stringWithFormat:@"%@游戏分享", @"进贤聚友棋牌"]
|
||||
Title:title ?: @""
|
||||
Description:description ?: @""
|
||||
ThumbImage:thumbImage
|
||||
InScene:currentScene];
|
||||
|
||||
NSLog(@"✅ [WechatShareManager] 链接分享请求已发送");
|
||||
}
|
||||
|
||||
// 模拟分享成功回调(微信分享结果需要在AppDelegate中处理)
|
||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
|
||||
NSLog(@"✅ [WechatShareManager] 分享完成");
|
||||
if (completion) {
|
||||
completion(YES);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
+ (void)shareLinkToWechat:(NSString *)url
|
||||
title:(NSString * _Nullable)title
|
||||
description:(NSString * _Nullable)description
|
||||
thumbImage:(UIImage * _Nullable)thumbImage
|
||||
scene:(WechatScene)scene
|
||||
completion:(void (^ _Nullable)(BOOL success))completion {
|
||||
|
||||
// 检查微信是否已安装
|
||||
if (![self isWechatInstalled]) {
|
||||
NSLog(@"❌ [WechatShareManager] 微信未安装");
|
||||
if (completion) {
|
||||
completion(NO);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
enum WXScene wxScene = (scene == WechatSceneSession) ? WXSceneSession : WXSceneTimeline;
|
||||
|
||||
[WXApiRequestHandler sendLinkURL:url ?: @""
|
||||
TagName:@"游戏分享"
|
||||
Title:title ?: @""
|
||||
Description:description ?: @""
|
||||
ThumbImage:thumbImage ?: [self getAppIconImage]
|
||||
InScene:wxScene];
|
||||
|
||||
// 模拟分享成功回调
|
||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
|
||||
if (completion) {
|
||||
completion(YES);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
+ (void)shareImageToWechat:(UIImage *)image
|
||||
tagName:(NSString * _Nullable)tagName
|
||||
messageExt:(NSString * _Nullable)messageExt
|
||||
thumbImage:(UIImage * _Nullable)thumbImage
|
||||
scene:(WechatScene)scene
|
||||
completion:(void (^ _Nullable)(BOOL success))completion {
|
||||
|
||||
// 检查微信是否已安装
|
||||
if (![self isWechatInstalled]) {
|
||||
NSLog(@"❌ [WechatShareManager] 微信未安装");
|
||||
if (completion) {
|
||||
completion(NO);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (!image) {
|
||||
NSLog(@"❌ [WechatShareManager] 图片为空");
|
||||
if (completion) {
|
||||
completion(NO);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
enum WXScene wxScene = (scene == WechatSceneSession) ? WXSceneSession : WXSceneTimeline;
|
||||
NSData *imageData = UIImageJPEGRepresentation(image, 0.6);
|
||||
|
||||
[WXApiRequestHandler sendImageData:imageData
|
||||
TagName:tagName ?: @"图片分享"
|
||||
MessageExt:messageExt ?: @""
|
||||
Action:@"share_action"
|
||||
ThumbImage:thumbImage ?: [self getAppIconImage]
|
||||
InScene:wxScene];
|
||||
|
||||
// 模拟分享成功回调
|
||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
|
||||
if (completion) {
|
||||
completion(YES);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
+ (UIImage *)getAppIconImage {
|
||||
// 获取应用图标
|
||||
UIImage *appIcon = nil;
|
||||
|
||||
// 方法1: 从Info.plist获取图标文件名
|
||||
NSDictionary *infoPlist = [[NSBundle mainBundle] infoDictionary];
|
||||
NSArray *iconFiles = infoPlist[@"CFBundleIconFiles"];
|
||||
if (iconFiles && iconFiles.count > 0) {
|
||||
NSString *iconFileName = [iconFiles lastObject]; // 取最后一个,通常是最大尺寸的
|
||||
appIcon = [UIImage imageNamed:iconFileName];
|
||||
}
|
||||
|
||||
// 方法2: 尝试常见的图标文件名
|
||||
if (!appIcon) {
|
||||
NSArray *commonIconNames = @[@"Icon-180", @"Icon180", @"AppIcon60x60@3x", @"AppIcon"];
|
||||
for (NSString *iconName in commonIconNames) {
|
||||
appIcon = [UIImage imageNamed:iconName];
|
||||
if (appIcon) break;
|
||||
}
|
||||
}
|
||||
|
||||
// 方法3: 从SharePanel中查找sharelogo
|
||||
if (!appIcon) {
|
||||
appIcon = [UIImage imageNamed:@"sharelogo"];
|
||||
}
|
||||
|
||||
// 兜底: 生成默认图标
|
||||
if (!appIcon) {
|
||||
// 创建一个简单的默认图标
|
||||
UIGraphicsBeginImageContextWithOptions(CGSizeMake(60, 60), NO, 0.0);
|
||||
CGContextRef context = UIGraphicsGetCurrentContext();
|
||||
|
||||
// 设置背景色
|
||||
CGContextSetFillColorWithColor(context, [UIColor colorWithRed:0.2 green:0.6 blue:1.0 alpha:1.0].CGColor);
|
||||
CGContextFillEllipseInRect(context, CGRectMake(0, 0, 60, 60));
|
||||
|
||||
// 添加文字
|
||||
NSString *text = @"游戏";
|
||||
NSDictionary *attributes = @{
|
||||
NSFontAttributeName: [UIFont boldSystemFontOfSize:16],
|
||||
NSForegroundColorAttributeName: [UIColor whiteColor]
|
||||
};
|
||||
CGSize textSize = [text sizeWithAttributes:attributes];
|
||||
CGPoint textPoint = CGPointMake((60 - textSize.width) / 2, (60 - textSize.height) / 2);
|
||||
[text drawAtPoint:textPoint withAttributes:attributes];
|
||||
|
||||
appIcon = UIGraphicsGetImageFromCurrentImageContext();
|
||||
UIGraphicsEndImageContext();
|
||||
}
|
||||
|
||||
return appIcon;
|
||||
}
|
||||
|
||||
@end
|
||||
Reference in New Issue
Block a user