From 1cdc17b7104fc6578b619974ea59d7bb4bd37f08 Mon Sep 17 00:00:00 2001 From: CodeST <694468528@qq.com> Date: Mon, 10 Nov 2025 15:38:30 +0800 Subject: [PATCH] 1 --- CustomKeyboard/KeyboardViewController.m | 2 +- CustomKeyboard/View/KBFunctionView.m | 2 +- CustomKeyboard/View/KBKeyboardView.m | 1 - keyBoard.xcodeproj/project.pbxproj | 10 +- .../Categories/UIViewController+Extension.m | 24 +- keyBoard/Class/Common/V/KBAlert.h | 75 ++++++ keyBoard/Class/Common/V/KBAlert.m | 224 ++++++++++++++++++ .../Class/{Categories => Common/V}/KBHUD.h | 0 .../Class/{Categories => Common/V}/KBHUD.m | 0 keyBoard/Class/Home/VC/HomeRankContentVC.m | 2 +- .../Login/VC/KBLoginSheetViewController.m | 2 +- keyBoard/Class/Login/VC/LoginViewController.m | 2 +- keyBoard/Class/Me/VC/KBSkinDetailVC.m | 2 +- keyBoard/Class/Me/VC/MySkinVC.m | 2 +- keyBoard/Class/Search/VC/KBSearchResultVC.m | 2 +- keyBoard/Class/Search/VC/KBSearchVC.m | 4 +- keyBoard/Class/Shop/VC/KBShopItemVC.m | 2 +- keyBoard/KeyBoardPrefixHeader.pch | 1 + 18 files changed, 337 insertions(+), 20 deletions(-) create mode 100644 keyBoard/Class/Common/V/KBAlert.h create mode 100644 keyBoard/Class/Common/V/KBAlert.m rename keyBoard/Class/{Categories => Common/V}/KBHUD.h (100%) rename keyBoard/Class/{Categories => Common/V}/KBHUD.m (100%) diff --git a/CustomKeyboard/KeyboardViewController.m b/CustomKeyboard/KeyboardViewController.m index e27dcff..9b85403 100644 --- a/CustomKeyboard/KeyboardViewController.m +++ b/CustomKeyboard/KeyboardViewController.m @@ -234,7 +234,7 @@ static CGFloat KEYBOARDHEIGHT = 256 + 20; // 使用与主 App 一致的自定义 Scheme NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:@"%@@//login?src=keyboard", KB_APP_SCHEME]]; if (!url) return; - __weak typeof(self) weakSelf = self; + KBWeakSelf [self.extensionContext openURL:url completionHandler:^(__unused BOOL success) { // 即使失败也不重复尝试;避免打扰。 __unused typeof(weakSelf) selfStrong = weakSelf; diff --git a/CustomKeyboard/View/KBFunctionView.m b/CustomKeyboard/View/KBFunctionView.m index 5612ba7..cfe0dd4 100644 --- a/CustomKeyboard/View/KBFunctionView.m +++ b/CustomKeyboard/View/KBFunctionView.m @@ -259,7 +259,7 @@ static NSString * const kKBFunctionTagCellId = @"KBFunctionTagCellId"; // 未开启“完全访问”时不做自动读取,避免宿主/系统拒绝并刷错误日志 if (![[KBFullAccessManager shared] hasFullAccess]) return; if (self.pasteboardTimer) return; - __weak typeof(self) weakSelf = self; + KBWeakSelf self.pasteboardTimer = [NSTimer scheduledTimerWithTimeInterval:0.5 repeats:YES block:^(NSTimer * _Nonnull timer) { __strong typeof(weakSelf) self = weakSelf; if (!self) return; UIPasteboard *pb = [UIPasteboard generalPasteboard]; diff --git a/CustomKeyboard/View/KBKeyboardView.m b/CustomKeyboard/View/KBKeyboardView.m index 8c3fce9..f9b53d7 100644 --- a/CustomKeyboard/View/KBKeyboardView.m +++ b/CustomKeyboard/View/KBKeyboardView.m @@ -338,7 +338,6 @@ if (before.length <= 0) { self.backspaceHoldActive = NO; return; } [proxy deleteBackward]; // 每次仅删 1 个 - // 轻量递归调度下一次;不使用 NSTimer,避免复杂状态管理 __weak typeof(self) weakSelf = self; dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.06 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ __strong typeof(weakSelf) selfStrong = weakSelf; diff --git a/keyBoard.xcodeproj/project.pbxproj b/keyBoard.xcodeproj/project.pbxproj index 997a0c0..befa9a4 100644 --- a/keyBoard.xcodeproj/project.pbxproj +++ b/keyBoard.xcodeproj/project.pbxproj @@ -76,6 +76,7 @@ 04890A042EC0BBBB00FABA60 /* KBCategoryTitleImageCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 04890A012EC0BBBB00FABA60 /* KBCategoryTitleImageCell.m */; }; 04890A052EC0BBBB00FABA60 /* KBCategoryTitleImageView.m in Sources */ = {isa = PBXBuildFile; fileRef = 04890A032EC0BBBB00FABA60 /* KBCategoryTitleImageView.m */; }; 049FB20B2EC1C13800FAB05D /* KBSkinBottomActionView.m in Sources */ = {isa = PBXBuildFile; fileRef = 049FB20A2EC1C13800FAB05D /* KBSkinBottomActionView.m */; }; + 049FB20E2EC1CD2800FAB05D /* KBAlert.m in Sources */ = {isa = PBXBuildFile; fileRef = 049FB20D2EC1CD2800FAB05D /* KBAlert.m */; }; 04A9FE0F2EB481100020DB6D /* KBHUD.m in Sources */ = {isa = PBXBuildFile; fileRef = 04FC97082EB31B14007BD342 /* KBHUD.m */; }; 04A9FE132EB4D0D20020DB6D /* KBFullAccessManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 04A9FE112EB4D0D20020DB6D /* KBFullAccessManager.m */; }; 04A9FE162EB873C80020DB6D /* UIViewController+Extension.m in Sources */ = {isa = PBXBuildFile; fileRef = 04A9FE152EB873C80020DB6D /* UIViewController+Extension.m */; }; @@ -266,6 +267,8 @@ 04890A032EC0BBBB00FABA60 /* KBCategoryTitleImageView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KBCategoryTitleImageView.m; sourceTree = ""; }; 049FB2092EC1C13800FAB05D /* KBSkinBottomActionView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KBSkinBottomActionView.h; sourceTree = ""; }; 049FB20A2EC1C13800FAB05D /* KBSkinBottomActionView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KBSkinBottomActionView.m; sourceTree = ""; }; + 049FB20C2EC1CD2800FAB05D /* KBAlert.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KBAlert.h; sourceTree = ""; }; + 049FB20D2EC1CD2800FAB05D /* KBAlert.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KBAlert.m; sourceTree = ""; }; 04A9A67D2EB9E1690023B8F4 /* KBResponderUtils.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KBResponderUtils.h; sourceTree = ""; }; 04A9FE102EB4D0D20020DB6D /* KBFullAccessManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KBFullAccessManager.h; sourceTree = ""; }; 04A9FE112EB4D0D20020DB6D /* KBFullAccessManager.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KBFullAccessManager.m; sourceTree = ""; }; @@ -544,6 +547,10 @@ 048908B82EBDC11200FABA60 /* V */ = { isa = PBXGroup; children = ( + 04FC97072EB31B14007BD342 /* KBHUD.h */, + 04FC97082EB31B14007BD342 /* KBHUD.m */, + 049FB20C2EC1CD2800FAB05D /* KBAlert.h */, + 049FB20D2EC1CD2800FAB05D /* KBAlert.m */, ); path = V; sourceTree = ""; @@ -968,8 +975,6 @@ 04FC970B2EB334F8007BD342 /* UIImageView+KBWebImage.m */, 04FC970C2EB334F8007BD342 /* KBWebImageManager.h */, 04FC970D2EB334F8007BD342 /* KBWebImageManager.m */, - 04FC97072EB31B14007BD342 /* KBHUD.h */, - 04FC97082EB31B14007BD342 /* KBHUD.m */, 04A9FE142EB873C80020DB6D /* UIViewController+Extension.h */, 04A9FE152EB873C80020DB6D /* UIViewController+Extension.m */, 047C655A2EBCD08E0035E841 /* UIView+KBShadow.h */, @@ -1338,6 +1343,7 @@ 0477BE002EBC6A330055D639 /* HomeRankVC.m in Sources */, 047C650D2EBC8A840035E841 /* KBPanModalView.m in Sources */, 043FBCD22EAF97630036AFE1 /* KBPermissionViewController.m in Sources */, + 049FB20E2EC1CD2800FAB05D /* KBAlert.m in Sources */, 04A9FE162EB873C80020DB6D /* UIViewController+Extension.m in Sources */, 04C6EABE2EAF86530089C901 /* AppDelegate.m in Sources */, 04FC95F12EB339A7007BD342 /* LoginViewController.m in Sources */, diff --git a/keyBoard/Class/Categories/UIViewController+Extension.m b/keyBoard/Class/Categories/UIViewController+Extension.m index de0def3..5f0441c 100644 --- a/keyBoard/Class/Categories/UIViewController+Extension.m +++ b/keyBoard/Class/Categories/UIViewController+Extension.m @@ -34,13 +34,25 @@ static inline __kindof UIWindow *KBActiveWindow(void) { + (UIViewController *)kb_topMostViewController { UIWindow *window = KBActiveWindow(); - UIViewController *root = window.rootViewController; - if (!root) return nil; - UIViewController *top = root; - while (top.presentedViewController) { - top = top.presentedViewController; + UIViewController *vc = window.rootViewController; + if (!vc) return nil; + // 展开容器控制器 + while (1) { + if ([vc isKindOfClass:UINavigationController.class]) { + vc = ((UINavigationController *)vc).visibleViewController ?: vc; + continue; + } + if ([vc isKindOfClass:UITabBarController.class]) { + vc = ((UITabBarController *)vc).selectedViewController ?: vc; + continue; + } + if (vc.presentedViewController) { + vc = vc.presentedViewController; + continue; + } + break; } - return top; + return vc; } @end diff --git a/keyBoard/Class/Common/V/KBAlert.h b/keyBoard/Class/Common/V/KBAlert.h new file mode 100644 index 0000000..a92b341 --- /dev/null +++ b/keyBoard/Class/Common/V/KBAlert.h @@ -0,0 +1,75 @@ +// +// KBAlert.h +// keyBoard +// +// 系统 UIAlertController 的轻量封装:更易用、更健壮。 +// 特性: +// - 任意线程调用;内部切回主线程 +// - 自动找到可展示的 VC(使用顶层 presented VC) +// - Alert 队列:避免“正在展示时再次 present 失败”的崩溃/警告 +// - iPad ActionSheet 自动设置 popover 锚点(无需关心 sourceView) +// - 常见场景一行调用:提示/确认/输入框/操作表 +// - 可选指定展示的 VC(如在 Extension 内) +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +typedef void (^KBAlertBoolHandler)(BOOL ok); +typedef void (^KBAlertIndexHandler)(NSInteger index); +typedef void (^KBAlertTextHandler)(NSString * _Nullable text, BOOL ok); + +@interface KBAlert : NSObject + +/// 指定一个缺省的“用于 present 的 VC”。 +/// - 适用:App Extension 或自管理容器场景;App 内一般无需设置。 +/// - 注意:弱引用;随时可被释放。 ++ (void)setDefaultPresenter:(nullable __kindof UIViewController *)presenter; + +#pragma mark - 简单提示(单按钮) +/// 标准 OK 提示;按钮文案默认为“好”或“OK”。 ++ (void)showTitle:(nullable NSString *)title message:(nullable NSString *)message; +/// 自定义按钮文案与回调。 ++ (void)showTitle:(nullable NSString *)title + message:(nullable NSString *)message + button:(nullable NSString *)button + completion:(dispatch_block_t)completion; + +#pragma mark - 确认对话框(双按钮) +/// 默认“取消/确定”按钮,回调 ok=YES 表示点击了确定。 ++ (void)confirmTitle:(nullable NSString *)title + message:(nullable NSString *)message + completion:(KBAlertBoolHandler)completion; +/// 自定义按钮文案。 ++ (void)confirmTitle:(nullable NSString *)title + message:(nullable NSString *)message + ok:(nullable NSString *)okTitle + cancel:(nullable NSString *)cancelTitle + completion:(KBAlertBoolHandler)completion; + +#pragma mark - 输入框(单行) +/// 带单个输入框;ok=YES 时返回输入内容。 ++ (void)promptTitle:(nullable NSString *)title + message:(nullable NSString *)message + placeholder:(nullable NSString *)placeholder + ok:(nullable NSString *)okTitle + cancel:(nullable NSString *)cancelTitle + keyboardType:(UIKeyboardType)type + configuration:(void (^ _Nullable)(UITextField *tf))config + completion:(KBAlertTextHandler)completion; + +#pragma mark - 操作表(ActionSheet) +/// 操作表;index 为点击项序号,按 actions 顺序从 0 开始;取消返回 -1。 ++ (void)actionSheetTitle:(nullable NSString *)title + message:(nullable NSString *)message + actions:(NSArray *)actions + cancel:(nullable NSString *)cancelTitle + destructiveIndex:(NSInteger)destructiveIndex // 传入 -1 表示无 + fromView:(nullable UIView *)anchorView // iPad 可指定锚点;传 nil 自动居中 + completion:(KBAlertIndexHandler)completion; + +@end + +NS_ASSUME_NONNULL_END + diff --git a/keyBoard/Class/Common/V/KBAlert.m b/keyBoard/Class/Common/V/KBAlert.m new file mode 100644 index 0000000..1e85a38 --- /dev/null +++ b/keyBoard/Class/Common/V/KBAlert.m @@ -0,0 +1,224 @@ +// +// KBAlert.m +// keyBoard +// + +#import "KBAlert.h" +#import +#import "UIViewController+Extension.h" + +@implementation KBAlert + +// 简单的串行展示队列,避免重复 present 引发的异常 +static NSMutableArray *sQueue; +static BOOL sPresenting = NO; +static __weak UIViewController *sDefaultPresenter = nil; // 可选外部指定的 presenter(弱引用) + +#pragma mark - Helpers + ++ (void)initialize { + if (self == KBAlert.class) { + sQueue = [NSMutableArray array]; + } +} + ++ (void)onMain:(dispatch_block_t)blk { + if (NSThread.isMainThread) { blk(); } else { dispatch_async(dispatch_get_main_queue(), blk); } +} + ++ (UIViewController *)presentingVC { + // 优先使用外部指定的 presenter;否则找顶层 + UIViewController *vc = sDefaultPresenter; + if (!vc) { + vc = [UIViewController kb_topMostViewController]; + } + return vc; +} + ++ (void)enqueuePresent:(dispatch_block_t)presenter { + [self onMain:^{ + [sQueue addObject:[presenter copy]]; + if (!sPresenting) { [self _dequeueAndPresentNext]; } + }]; +} + ++ (void)_dequeueAndPresentNext { + if (sPresenting || sQueue.count == 0) { return; } + sPresenting = YES; + dispatch_block_t blk = sQueue.firstObject; + [sQueue removeObjectAtIndex:0]; + blk(); +} + +// 在任意 action 执行后调用,标记结束并尝试继续队列 ++ (void)_markFinishedAndContinue { + // 延迟到下一个 runloop 以确保系统完成 dismiss 流程 + dispatch_async(dispatch_get_main_queue(), ^{ + sPresenting = NO; + [self _dequeueAndPresentNext]; + }); +} + +// iPad 的 ActionSheet 需要 popover 锚点;此处自动兜底 ++ (void)configurePopoverIfNeeded:(UIAlertController *)ac anchorView:(UIView *)anchorView inVC:(UIViewController *)vc { + if (ac.preferredStyle != UIAlertControllerStyleActionSheet) { return; } + UIPopoverPresentationController *pop = ac.popoverPresentationController; + if (!pop) { return; } + if (anchorView) { + pop.sourceView = anchorView; + pop.sourceRect = anchorView.bounds; + } else if (vc.view) { + pop.sourceView = vc.view; + CGSize sz = vc.view.bounds.size; + CGRect r = CGRectMake(MAX(0, sz.width * 0.5 - 1), MAX(0, sz.height * 0.5 - 1), 2, 2); + pop.sourceRect = r; // 居中一个很小的矩形,视觉近似居中弹出 + pop.permittedArrowDirections = 0; + } +} + +#pragma mark - Public API + ++ (void)setDefaultPresenter:(UIViewController *)presenter { sDefaultPresenter = presenter; } + ++ (void)showTitle:(NSString *)title message:(NSString *)message { + [self showTitle:title message:message button:nil completion:nil]; +} + ++ (void)showTitle:(NSString *)title + message:(NSString *)message + button:(NSString *)button + completion:(dispatch_block_t)completion { + [self enqueuePresent:^{ + UIViewController *vc = [self presentingVC]; + if (!vc) { // 无法获取 presenter,直接结束队列 + [self _markFinishedAndContinue]; + return; + } + NSString *ok = button ?: (NSLocalizedString(@"OK", nil).length ? NSLocalizedString(@"OK", nil) : @"好"); + UIAlertController *ac = [UIAlertController alertControllerWithTitle:(title ?: @"") + message:(message ?: @"") + preferredStyle:UIAlertControllerStyleAlert]; + __weak UIAlertController *wac = ac; + UIAlertAction *okAct = [UIAlertAction actionWithTitle:ok style:UIAlertActionStyleDefault handler:^(__unused UIAlertAction * _Nonnull action) { + if (completion) { completion(); } + // 防止异常:确保 alert 被关闭 + [wac dismissViewControllerAnimated:YES completion:nil]; + [KBAlert _markFinishedAndContinue]; + }]; + [ac addAction:okAct]; + [vc presentViewController:ac animated:YES completion:nil]; + }]; +} + ++ (void)confirmTitle:(NSString *)title message:(NSString *)message completion:(KBAlertBoolHandler)completion { + [self confirmTitle:title message:message ok:nil cancel:nil completion:completion]; +} + ++ (void)confirmTitle:(NSString *)title + message:(NSString *)message + ok:(NSString *)okTitle + cancel:(NSString *)cancelTitle + completion:(KBAlertBoolHandler)completion { + [self enqueuePresent:^{ + UIViewController *vc = [self presentingVC]; + if (!vc) { [self _markFinishedAndContinue]; return; } + NSString *ok = okTitle ?: @"确定"; + NSString *cancel = cancelTitle ?: @"取消"; + UIAlertController *ac = [UIAlertController alertControllerWithTitle:(title ?: @"") + message:(message ?: @"") + preferredStyle:UIAlertControllerStyleAlert]; + __weak UIAlertController *wac = ac; + UIAlertAction *cancelAct = [UIAlertAction actionWithTitle:cancel style:UIAlertActionStyleCancel handler:^(__unused UIAlertAction * _Nonnull a) { + if (completion) { completion(NO); } + [wac dismissViewControllerAnimated:YES completion:nil]; + [KBAlert _markFinishedAndContinue]; + }]; + UIAlertAction *okAct = [UIAlertAction actionWithTitle:ok style:UIAlertActionStyleDefault handler:^(__unused UIAlertAction * _Nonnull a) { + if (completion) { completion(YES); } + [wac dismissViewControllerAnimated:YES completion:nil]; + [KBAlert _markFinishedAndContinue]; + }]; + [ac addAction:cancelAct]; + [ac addAction:okAct]; + [vc presentViewController:ac animated:YES completion:nil]; + }]; +} + ++ (void)promptTitle:(NSString *)title + message:(NSString *)message + placeholder:(NSString *)placeholder + ok:(NSString *)okTitle + cancel:(NSString *)cancelTitle + keyboardType:(UIKeyboardType)type + configuration:(void (^)(UITextField * _Nonnull))config + completion:(KBAlertTextHandler)completion { + [self enqueuePresent:^{ + UIViewController *vc = [self presentingVC]; + if (!vc) { [self _markFinishedAndContinue]; return; } + NSString *ok = okTitle ?: @"确定"; + NSString *cancel = cancelTitle ?: @"取消"; + UIAlertController *ac = [UIAlertController alertControllerWithTitle:(title ?: @"") + message:(message ?: @"") + preferredStyle:UIAlertControllerStyleAlert]; + __weak UIAlertController *wac = ac; + [ac addTextFieldWithConfigurationHandler:^(UITextField * _Nonnull tf) { + tf.placeholder = placeholder; + tf.keyboardType = type; + if (config) { config(tf); } + }]; + UIAlertAction *cancelAct = [UIAlertAction actionWithTitle:cancel style:UIAlertActionStyleCancel handler:^(__unused UIAlertAction * _Nonnull a) { + if (completion) { completion(nil, NO); } + [wac dismissViewControllerAnimated:YES completion:nil]; + [KBAlert _markFinishedAndContinue]; + }]; + UIAlertAction *okAct = [UIAlertAction actionWithTitle:ok style:UIAlertActionStyleDefault handler:^(__unused UIAlertAction * _Nonnull a) { + NSString *text = wac.textFields.firstObject.text; + if (completion) { completion(text, YES); } + [wac dismissViewControllerAnimated:YES completion:nil]; + [KBAlert _markFinishedAndContinue]; + }]; + [ac addAction:cancelAct]; + [ac addAction:okAct]; + [vc presentViewController:ac animated:YES completion:nil]; + }]; +} + ++ (void)actionSheetTitle:(NSString *)title + message:(NSString *)message + actions:(NSArray *)actions + cancel:(NSString *)cancelTitle + destructiveIndex:(NSInteger)destructiveIndex + fromView:(UIView *)anchorView + completion:(KBAlertIndexHandler)completion { + [self enqueuePresent:^{ + UIViewController *vc = [self presentingVC]; + if (!vc) { [self _markFinishedAndContinue]; return; } + UIAlertController *ac = [UIAlertController alertControllerWithTitle:(title ?: @"") + message:(message ?: @"") + preferredStyle:UIAlertControllerStyleActionSheet]; + __weak UIAlertController *wac = ac; + [actions enumerateObjectsUsingBlock:^(NSString * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { + UIAlertActionStyle style = (destructiveIndex >= 0 && (NSInteger)idx == destructiveIndex) ? UIAlertActionStyleDestructive : UIAlertActionStyleDefault; + UIAlertAction *act = [UIAlertAction actionWithTitle:obj ?: @"" style:style handler:^(__unused UIAlertAction * _Nonnull a) { + if (completion) { completion((NSInteger)idx); } + [wac dismissViewControllerAnimated:YES completion:nil]; + [KBAlert _markFinishedAndContinue]; + }]; + [ac addAction:act]; + }]; + if (cancelTitle.length) { + UIAlertAction *cancel = [UIAlertAction actionWithTitle:cancelTitle style:UIAlertActionStyleCancel handler:^(__unused UIAlertAction * _Nonnull a) { + if (completion) { completion(-1); } + [wac dismissViewControllerAnimated:YES completion:nil]; + [KBAlert _markFinishedAndContinue]; + }]; + [ac addAction:cancel]; + } + + [self configurePopoverIfNeeded:ac anchorView:anchorView inVC:vc]; + [vc presentViewController:ac animated:YES completion:nil]; + }]; +} + +@end + diff --git a/keyBoard/Class/Categories/KBHUD.h b/keyBoard/Class/Common/V/KBHUD.h similarity index 100% rename from keyBoard/Class/Categories/KBHUD.h rename to keyBoard/Class/Common/V/KBHUD.h diff --git a/keyBoard/Class/Categories/KBHUD.m b/keyBoard/Class/Common/V/KBHUD.m similarity index 100% rename from keyBoard/Class/Categories/KBHUD.m rename to keyBoard/Class/Common/V/KBHUD.m diff --git a/keyBoard/Class/Home/VC/HomeRankContentVC.m b/keyBoard/Class/Home/VC/HomeRankContentVC.m index 43bf2f8..2f6687e 100644 --- a/keyBoard/Class/Home/VC/HomeRankContentVC.m +++ b/keyBoard/Class/Home/VC/HomeRankContentVC.m @@ -75,7 +75,7 @@ desc:d[@"desc"] people:d[@"people"] added:added]; - __weak typeof(self) weakSelf = self; + KBWeakSelf cell.onTapAction = ^{ // 切换添加/已添加状态并刷新该项 NSMutableArray *m = [weakSelf.dataSource mutableCopy]; diff --git a/keyBoard/Class/Login/VC/KBLoginSheetViewController.m b/keyBoard/Class/Login/VC/KBLoginSheetViewController.m index 04b6629..ac631c1 100644 --- a/keyBoard/Class/Login/VC/KBLoginSheetViewController.m +++ b/keyBoard/Class/Login/VC/KBLoginSheetViewController.m @@ -132,7 +132,7 @@ - (void)onContinue { LoginViewController *login = [LoginViewController new]; - __weak typeof(self) weakSelf = self; + KBWeakSelf login.onLoginSuccess = ^(NSDictionary * _Nonnull userInfo) { __strong typeof(weakSelf) self = weakSelf; if (!self) return; [self dismissViewControllerAnimated:YES completion:^{ diff --git a/keyBoard/Class/Login/VC/LoginViewController.m b/keyBoard/Class/Login/VC/LoginViewController.m index 7d4fc9a..ee9e78c 100644 --- a/keyBoard/Class/Login/VC/LoginViewController.m +++ b/keyBoard/Class/Login/VC/LoginViewController.m @@ -114,7 +114,7 @@ } - (void)handleAppleIDButtonPress API_AVAILABLE(ios(13.0)) { - __weak typeof(self) weakSelf = self; + KBWeakSelf [[AppleSignInManager shared] signInFromViewController:self completion:^(ASAuthorizationAppleIDCredential * _Nullable credential, NSError * _Nullable error) { __strong typeof(weakSelf) selfStrong = weakSelf; if (error) { diff --git a/keyBoard/Class/Me/VC/KBSkinDetailVC.m b/keyBoard/Class/Me/VC/KBSkinDetailVC.m index fbc239c..1a9ab8e 100644 --- a/keyBoard/Class/Me/VC/KBSkinDetailVC.m +++ b/keyBoard/Class/Me/VC/KBSkinDetailVC.m @@ -182,7 +182,7 @@ typedef NS_ENUM(NSInteger, KBSkinDetailSection) { _bottomBar = [[KBSkinBottomActionView alloc] init]; // 中文注释:配置文案与图标,可根据业务传入金币图/价格 [_bottomBar configWithTitle:@"Download" price:@"20" icon:nil]; - __weak typeof(self) weakSelf = self; + KBWeakSelf _bottomBar.tapHandler = ^{ // 示例:点击下载/购买 // [KBHUD showText:@"点击了下载"]; diff --git a/keyBoard/Class/Me/VC/MySkinVC.m b/keyBoard/Class/Me/VC/MySkinVC.m index b700196..511c630 100644 --- a/keyBoard/Class/Me/VC/MySkinVC.m +++ b/keyBoard/Class/Me/VC/MySkinVC.m @@ -55,7 +55,7 @@ static NSString * const kMySkinCellId = @"kMySkinCellId"; }]; // 空态视图(LYEmptyView)统一样式 + 重试按钮 - __weak typeof(self) weakSelf = self; + KBWeakSelf [self.collectionView kb_makeDefaultEmptyViewWithImage:nil title:@"暂无皮肤" detail:@"下拉刷新试试" diff --git a/keyBoard/Class/Search/VC/KBSearchResultVC.m b/keyBoard/Class/Search/VC/KBSearchResultVC.m index fbec781..66847c9 100644 --- a/keyBoard/Class/Search/VC/KBSearchResultVC.m +++ b/keyBoard/Class/Search/VC/KBSearchResultVC.m @@ -152,7 +152,7 @@ static NSString * const kResultCellId = @"KBSkinCardCell"; if (!_searchBarView) { _searchBarView = [[KBSearchBarView alloc] init]; _searchBarView.placeholder = @"Themes"; - __weak typeof(self) weakSelf = self; + KBWeakSelf _searchBarView.onSearch = ^(NSString * _Nonnull keyword) { [weakSelf performSearch:keyword]; }; diff --git a/keyBoard/Class/Search/VC/KBSearchVC.m b/keyBoard/Class/Search/VC/KBSearchVC.m index bb6c52b..6382757 100644 --- a/keyBoard/Class/Search/VC/KBSearchVC.m +++ b/keyBoard/Class/Search/VC/KBSearchVC.m @@ -285,7 +285,7 @@ typedef NS_ENUM(NSInteger, KBSearchSection) { if (indexPath.section == KBSearchSectionHistory) { // 当没有历史时,外部通过 sizeForHeader 返回 0,这里仍设置但不会显示 [header configWithTitle:@"Historical Search" showTrash:self.historyWords.count > 0]; - __weak typeof(self) weakSelf = self; + KBWeakSelf header.onTapTrash = ^{ [weakSelf clearHistory]; }; } else { [header configWithTitle:@"Recommended Skin" showTrash:NO]; @@ -363,7 +363,7 @@ typedef NS_ENUM(NSInteger, KBSearchSection) { if (!_searchBarView) { _searchBarView = [[KBSearchBarView alloc] init]; _searchBarView.placeholder = @"Themes"; - __weak typeof(self) weakSelf = self; + KBWeakSelf _searchBarView.onSearch = ^(NSString * _Nonnull keyword) { // 置顶到历史 + 打开结果页 [weakSelf performSearch:keyword]; diff --git a/keyBoard/Class/Shop/VC/KBShopItemVC.m b/keyBoard/Class/Shop/VC/KBShopItemVC.m index 55e252f..b5be936 100644 --- a/keyBoard/Class/Shop/VC/KBShopItemVC.m +++ b/keyBoard/Class/Shop/VC/KBShopItemVC.m @@ -27,7 +27,7 @@ }]; // 刷新组件(演示:2 秒后结束) - __weak typeof(self) weakSelf = self; + KBWeakSelf if (self.isNeedHeader) { self.collectionView.mj_header = [MJRefreshNormalHeader headerWithRefreshingBlock:^{ dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ diff --git a/keyBoard/KeyBoardPrefixHeader.pch b/keyBoard/KeyBoardPrefixHeader.pch index 7a7c720..ec0d2b3 100644 --- a/keyBoard/KeyBoardPrefixHeader.pch +++ b/keyBoard/KeyBoardPrefixHeader.pch @@ -21,6 +21,7 @@ /// 系统 #import #import "KBHUD.h" +#import "KBAlert.h" #import "KBNetworkManager.h"