201 lines
7.1 KiB
Objective-C
201 lines
7.1 KiB
Objective-C
//
|
||
// KBHUD.m
|
||
// keyBoard
|
||
//
|
||
|
||
#import "KBHUD.h"
|
||
#import <MBProgressHUD/MBProgressHUD.h>
|
||
#import <UIKit/UIKit.h>
|
||
|
||
#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
|