// // KBHUD.m // keyBoard // #import "KBHUD.h" #import #import #ifndef KBSCREEN #define KBSCREEN [UIScreen mainScreen].bounds.size #endif @implementation KBHUD static __weak MBProgressHUD *sHUD = nil; static KBHUDMaskType sMaskType = KBHUDMaskTypeClear; // 全局默认遮罩 static BOOL sDefaultTapToDismiss = NO; // 全局默认:不允许点击关闭 static NSTimeInterval sAutoDismiss = 1.2; static __weak UIView *sContainerView = nil; // 缺省承载视图(扩展里必须设置) #pragma mark - Private Helpers + (void)onMain:(dispatch_block_t)blk { if (NSThread.isMainThread) { blk(); } else { dispatch_async(dispatch_get_main_queue(), blk); } } + (MBProgressHUD *)ensureHUDWithMask:(KBHUDMaskType)mask tap:(BOOL)tap { // 先尝试使用外部指定的容器视图(扩展环境推荐) UIView *hostView = sContainerView; #ifndef KB_APP_EXTENSION // App 内退回到 KeyWindow if (!hostView) { // KB_KeyWindow 在 App 目标的 PrefixHeader 中定义;在扩展内不依赖它 UIWindow *win = nil; // 避免强依赖某个前缀:这里以运行时方式访问 UIApplication 以规避编译期的 App Extension 限制 Class uiAppClass = NSClassFromString(@"UIApplication"); if (uiAppClass && [uiAppClass respondsToSelector:@selector(sharedApplication)]) { #pragma clang diagnostic push #pragma clang diagnostic ignored "-Warc-performSelector-leaks" id app = [uiAppClass performSelector:@selector(sharedApplication)]; if ([app respondsToSelector:@selector(keyWindow)]) { win = [app keyWindow]; } if (!win && [app respondsToSelector:@selector(windows)]) { NSArray *wins = [app windows]; win = wins.firstObject; } #pragma clang diagnostic pop } hostView = win; } #endif if (!hostView) { return nil; } MBProgressHUD *hud = sHUD; if (!hud) { hud = [MBProgressHUD showHUDAddedTo:hostView animated:YES]; sHUD = hud; // 外观:深色圆角,白色文字,模仿 SVProgressHUD 默认 hud.removeFromSuperViewOnHide = YES; hud.bezelView.style = MBProgressHUDBackgroundStyleSolidColor; hud.bezelView.layer.cornerRadius = 12.0; hud.bezelView.color = [UIColor colorWithWhite:0 alpha:0.8]; hud.contentColor = UIColor.whiteColor; hud.margin = 16; hud.label.numberOfLines = 0; hud.detailsLabel.numberOfLines = 0; } // 遮罩与交互(按本次 show 的入参应用) [self applyMaskType:mask hud:hud]; [self applyTapToDismiss:tap hud:hud]; return hud; } + (void)applyMaskType:(KBHUDMaskType)type hud:(MBProgressHUD *)hud { switch (type) { case KBHUDMaskTypeNone: { hud.userInteractionEnabled = NO; // 不拦截触摸 hud.backgroundView.style = MBProgressHUDBackgroundStyleSolidColor; hud.backgroundView.color = UIColor.clearColor; } break; case KBHUDMaskTypeClear: { hud.userInteractionEnabled = YES; // 拦截触摸但不变暗 hud.backgroundView.style = MBProgressHUDBackgroundStyleSolidColor; hud.backgroundView.color = [UIColor colorWithWhite:0 alpha:0.01]; } break; case KBHUDMaskTypeBlack: { hud.userInteractionEnabled = YES; // 变暗并拦截 hud.backgroundView.style = MBProgressHUDBackgroundStyleSolidColor; hud.backgroundView.color = [UIColor colorWithWhite:0 alpha:0.35]; } break; } } + (void)applyTapToDismiss:(BOOL)enabled hud:(MBProgressHUD *)hud { UIView *bg = hud.backgroundView; // 清理旧手势 for (UIGestureRecognizer *g in bg.gestureRecognizers.copy) { if ([g isKindOfClass:UITapGestureRecognizer.class] && g.view == bg) { [bg removeGestureRecognizer:g]; } } if (enabled) { UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(_kb_handleTap)]; [bg addGestureRecognizer:tap]; // 确保可拦截 hud.userInteractionEnabled = YES; if (hud.backgroundView.color == UIColor.clearColor) { hud.backgroundView.color = [UIColor colorWithWhite:0 alpha:0.01]; } } } + (void)_kb_handleTap { [self dismiss]; } + (void)_showText:(NSString *)text icon:(nullable UIImage *)icon { [self onMain:^{ MBProgressHUD *hud = [self ensureHUDWithMask:sMaskType tap:sDefaultTapToDismiss]; if (!hud) { return; } hud.mode = icon ? MBProgressHUDModeCustomView : MBProgressHUDModeText; hud.label.text = text ?: @""; hud.detailsLabel.text = nil; if (icon) { UIImageView *iv = [[UIImageView alloc] initWithImage:icon]; hud.customView = iv; } else { hud.customView = nil; } [hud hideAnimated:YES afterDelay:sAutoDismiss]; }]; } #pragma mark - Public API + (void)show { [self showWithStatus:nil]; } + (void)showWithStatus:(NSString *)status { [self showWithStatus:status allowTapToDismiss:NO]; } + (void)showWithStatus:(NSString *)status allowTapToDismiss:(BOOL)allow { [self onMain:^{ MBProgressHUD *hud = [self ensureHUDWithMask:sMaskType tap:allow]; if (!hud) { return; } hud.mode = MBProgressHUDModeIndeterminate; hud.label.text = status ?: @""; }]; } + (void)showProgress:(float)progress status:(NSString *)status { [self showProgress:progress status:status allowTapToDismiss:NO]; } + (void)showProgress:(float)progress status:(NSString *)status allowTapToDismiss:(BOOL)allow { [self onMain:^{ MBProgressHUD *hud = [self ensureHUDWithMask:sMaskType tap:allow]; if (!hud) { return; } hud.mode = MBProgressHUDModeDeterminate; hud.progress = progress; hud.label.text = status ?: @""; }]; } + (void)showInfo:(NSString *)status { [self _showText:status icon:nil]; } + (void)showSuccess:(NSString *)status { // 简单起见,使用文字模式;如需图标,可替换为自定义勾勾图片 [self _showText:status ?: @"成功" icon:nil]; } + (void)showError:(NSString *)status { [self _showText:status ?: @"失败" icon:nil]; } + (void)dismiss { [self onMain:^{ [sHUD hideAnimated:YES]; }]; } + (void)dismissWithDelay:(NSTimeInterval)delay { [self onMain:^{ [sHUD hideAnimated:YES afterDelay:delay]; }]; } #pragma mark - Config + (void)setDefaultMaskType:(KBHUDMaskType)maskType { sMaskType = maskType; } + (void)setTapToDismissEnabled:(BOOL)enabled { // 仅针对当前可见 HUD 生效,不修改默认值,满足“B 允许点击,A 默认不允许”的诉求 [self onMain:^{ if (sHUD) { [self applyTapToDismiss:enabled hud:sHUD]; } }]; } + (void)setAutoDismissInterval:(NSTimeInterval)interval { sAutoDismiss = MAX(0.2, interval); } + (void)showAllowTapToDismiss:(BOOL)allow { [self showWithStatus:nil allowTapToDismiss:allow]; } + (void)setContainerView:(UIView *)view { sContainerView = view; } @end