如何解决MBProgressHUD/SVProgressHUD多个弹窗重叠显示的问题
多请求场景下HUD重叠问题最佳解决方案
核心思路
本质是给HUD增加全局引用计数,避免重复创建实例,同一时间仅保留1个加载态HUD实例,从根源解决重叠导致背景加深的问题。
方案1:适配SVProgressHUD(利用原生单例特性)
SVProgressHUD本身是全局单例,仅需要新增计数逻辑统一管控调用即可:
- 维护全局的
NSInteger hudShowCount计数变量,每次调用加载展示接口时计数+1,仅当计数从0变为1时才真正调用SVProgressHUD原生展示方法 - 每次请求结束(无论成功失败)调用隐藏接口时计数-1,仅当计数减到0时才真正调用SVProgressHUD原生隐藏方法
- 错误提示类HUD设置为低优先级:仅当前无加载态HUD展示时才弹出,若有加载态则延迟等待加载消失后再展示
示例代码:
// 全局维护计数变量 static NSInteger kHUDShowCount = 0; /// 统一加载HUD展示接口 + (void)showLoading { kHUDShowCount++; if (kHUDShowCount == 1) { // 可在这里统一配置HUD样式 [SVProgressHUD show]; } } /// 统一加载HUD隐藏接口 + (void)dismissLoading { if (kHUDShowCount <= 0) { return; } kHUDShowCount--; if (kHUDShowCount == 0) { [SVProgressHUD dismiss]; } } /// 统一错误提示接口 + (void)showError:(NSString *)msg { // 等待所有加载HUD消失后再展示错误 if (kHUDShowCount > 0) { dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ [self showError:msg]; }); return; } [SVProgressHUD showErrorWithStatus:msg]; [SVProgressHUD dismissWithDelay:2]; }
后续所有业务代码统一调用上述封装接口,禁止直接调用SVProgressHUD原生方法即可解决重叠问题。
方案2:适配MBProgressHUD(封装全局管理单例)
MBProgressHUD默认支持多实例创建,需要额外封装全局管理类管控唯一实例:
- 封装
HUDManager全局单例,内部持有唯一的MBProgressHUD实例,统一添加到当前应用顶层window上 - 内部维护计数逻辑,规则和上述SVProgressHUD的计数逻辑完全一致
- 所有HUD的展示、隐藏操作都走HUDManager的统一接口,禁止业务侧直接创建MBProgressHUD实例
示例核心代码:
@interface HUDManager : NSObject + (instancetype)shared; - (void)showLoading; - (void)dismissLoading; - (void)showError:(NSString *)msg; @end @implementation HUDManager { NSInteger _showCount; MBProgressHUD *_hud; } + (instancetype)shared { static HUDManager *instance; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ instance = [[HUDManager alloc] init]; }); return instance; } - (instancetype)init { self = [super init]; if (self) { _showCount = 0; // 统一将HUD添加到顶层window _hud = [[MBProgressHUD alloc] initWithView:[UIApplication sharedApplication].keyWindow]; [[UIApplication sharedApplication].keyWindow addSubview:_hud]; // 统一配置HUD样式 _hud.removeFromSuperViewOnHide = NO; } return self; } - (void)showLoading { dispatch_async(dispatch_get_main_queue(), ^{ self->_showCount++; if (self->_showCount == 1) { self->_hud.mode = MBProgressHUDModeIndeterminate; self->_hud.label.text = @"loading"; [self->_hud showAnimated:YES]; } }); } - (void)dismissLoading { dispatch_async(dispatch_get_main_queue(), ^{ if (self->_showCount <= 0) return; self->_showCount--; if (self->_showCount == 0) { [self->_hud hideAnimated:YES]; } }); } - (void)showError:(NSString *)msg { dispatch_async(dispatch_get_main_queue(), ^{ if (self->_showCount > 0) { dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ [self showError:msg]; }); return; } self->_hud.mode = MBProgressHUDModeText; self->_hud.label.text = msg; [self->_hud showAnimated:YES]; [self->_hud hideAnimated:YES afterDelay:2]; }); } @end
注意事项
- 所有HUD操作必须放在主线程执行,避免多线程操作导致计数不准
- 页面销毁时要检查当前页面关联请求对应的HUD计数是否正确扣减,避免出现HUD永久不消失的问题
- 如有更高优先级的全局提示(如登录过期弹窗),可直接重置计数为0,隐藏当前HUD后再展示高优先级内容
内容的提问来源于stack exchange,提问作者jiexishede
相关产品推荐
相关产品推荐

