This commit is contained in:
2025-11-10 15:38:30 +08:00
parent 97316c7989
commit 1cdc17b710
18 changed files with 337 additions and 20 deletions

View File

@@ -0,0 +1,200 @@
//
// 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