当前位置: 首页 > 工具软件 > SVProgressHUD > 使用案例 >

SVProgressHUD源码精讲(二) -- Dismiss

淳于熙云
2023-12-01

SVProgressHUD Dismiss

前面一篇,我们分析了SVProgressHUD显示的源码部分,结尾处还留有dismiss方法,这篇文章,我们一起来看一下dismiss方法的具体分析。

源码

这里可以清晰的看到,dimiss做了一层封装,实际上需要调用dimissWithDelay: completion:方法实现取消操作。

- (void)dismiss {
    [self dismissWithDelay:0.0 completion:nil];
}

传入的参数有两个

  1. 时间参数;
  2. 完成后的操作
- (void)dismissWithDelay:(NSTimeInterval)delay completion:(SVProgressHUDDismissCompletion)completion {
    __weak SVProgressHUD *weakSelf = self;
    [[NSOperationQueue mainQueue] addOperationWithBlock:^{
        __strong SVProgressHUD *strongSelf = weakSelf;
        if(strongSelf){
            
            // Post notification to inform user
            [[NSNotificationCenter defaultCenter] postNotificationName:SVProgressHUDWillDisappearNotification
                                                                object:nil
                                                              userInfo:[strongSelf notificationUserInfo]];
            
            // Reset activity count
            // 重新设置活动数量 -- 清零
            strongSelf.activityCount = 0;
            
            // 后面在block中调用,因此将这写功能封装成为block块然后方便统一管理
            // 声明block块
            // 动画block块
            __block void (^animationsBlock)(void) = ^{
                // Shrink HUD a little to make a nice disappear animation
                // 动画缩放变化
                strongSelf.hudView.transform = CGAffineTransformScale(strongSelf.hudView.transform, 1/1.3f, 1/1.3f);
                
                // Fade out all effects (colors, blur, etc.)
                // 清除颜色,设置透明度
                [strongSelf fadeOutEffects];
            };
            // 完成block块
            __block void (^completionBlock)(void) = ^{
                // Check if we really achieved to dismiss the HUD (<=> alpha values are applied)
                // and the change of these values has not been cancelled in between e.g. due to a new show
                // 当背景视图为透明的时候,移除所有控件
                if(self.backgroundView.alpha == 0.0f){
                    // Clean up view hierarchy (overlays)
                    // 先移除自己所有控件的内容
                    [strongSelf.controlView removeFromSuperview];
                    [strongSelf.backgroundView removeFromSuperview];
                    [strongSelf.hudView removeFromSuperview];
                    // 移除自身
                    [strongSelf removeFromSuperview];
                    
                    // Reset progress and cancel any running animation
                    // 清空进度内容和取消所有的动画
                    strongSelf.progress = SVProgressHUDUndefinedProgress;
                    [strongSelf cancelRingLayerAnimation];
                    [strongSelf cancelIndefiniteAnimatedViewAnimation];
                    
                    // Remove observer <=> we do not have to handle orientation changes etc.
                    [[NSNotificationCenter defaultCenter] removeObserver:strongSelf];
                    
                    // Post notification to inform user
                    [[NSNotificationCenter defaultCenter] postNotificationName:SVProgressHUDDidDisappearNotification
                                                                        object:strongSelf
                                                                      userInfo:[strongSelf notificationUserInfo]];
                    
                    // Tell the rootViewController to update the StatusBar appearance
#if !defined(SV_APP_EXTENSIONS) && TARGET_OS_IOS
                    UIViewController *rootController = [[UIApplication sharedApplication] keyWindow].rootViewController;
                    /* This should be called whenever the return values for the view controller's status bar attributes have changed. If it is called from within an animation block, the changes will be animated along with the rest of the animation block. */
                    //更新控制器
                    [rootController setNeedsStatusBarAppearanceUpdate];
#endif
                    
                    // Run an (optional) completionHandler
                    // 完成用户定义的block内容
                    if (completion) {
                        completion();
                    }
                }
            };
            
            // UIViewAnimationOptionBeginFromCurrentState AND a delay doesn't always work as expected
            // When UIViewAnimationOptionBeginFromCurrentState is set, animateWithDuration: evaluates the current
            // values to check if an animation is necessary. The evaluation happens at function call time and not
            // after the delay => the animation is sometimes skipped. Therefore we delay using dispatch_after.
            
            //  delay是用户进行输入延迟的时间
            dispatch_time_t dipatchTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delay * NSEC_PER_SEC));
            // 延迟实现取消动画和完成动画
            dispatch_after(dipatchTime, dispatch_get_main_queue(), ^{
                
                // Stop timer
                strongSelf.graceTimer = nil;
                
                // strongSelf.fadeOutAnimationDuration默认是0.15 大于 0
                if (strongSelf.fadeOutAnimationDuration > 0) {
                    // Animate appearance
                    [UIView animateWithDuration:strongSelf.fadeOutAnimationDuration
                                          delay:0
                                        options:(UIViewAnimationOptions) (UIViewAnimationOptionAllowUserInteraction | UIViewAnimationCurveEaseOut | UIViewAnimationOptionBeginFromCurrentState)
                                     animations:^{
                                         //动画调用上面声明的block块
                                         animationsBlock();
                                     } completion:^(BOOL finished) {
                                         //完成实现的block块
                                         completionBlock();
                                     }];
                } else {
                    //执行block块内的操作
                    animationsBlock();
                    completionBlock();
                }
            });
            
            // Inform iOS to redraw the view hierarchy
            // 重新显示视图
            [strongSelf setNeedsDisplay];
        }
    }];
}

SVProgressHUDDismissCompletion 只是定一个的一个Block块
typedef void (^SVProgressHUDDismissCompletion)(void);

与之前相同,我们对于UI操作必须放到主线程中去操作,因此,[NSOperationQueue mainQueue]是必不可少的。

第一部分:

	// Reset activity count
    strongSelf.activityCount = 0;
  	 __block void (^animationsBlock)(void) = ^{
    // Shrink HUD a little to make a nice disappear animation
    strongSelf.hudView.transform = CGAffineTransformScale(strongSelf.hudView.transform, 1/1.3f, 1/1.3f);
                
    // Fade out all effects (colors, blur, etc.)
    [strongSelf fadeOutEffects];
  1. 设置活动计数器为0,这个操作说明取消SVProgressHUD后,不能存在活动的SVProgressHUD;
  2. 然后对于缩放的动画做了一个处理;
  3. 最后,设置了背景颜色为透明;
- (void)fadeOutEffects
{
    if(self.defaultStyle != SVProgressHUDStyleCustom) {
        // Remove blur effect
        self.hudView.effect = nil;
    }

    // Remove background color
    // 清除背景颜色作为透明颜色
    self.hudView.backgroundColor = [UIColor clearColor];
    
    // Fade out views
    // 所有控件都设置为透明
    self.backgroundView.alpha = 0.0f;
    
    self.imageView.alpha = 0.0f;
    self.statusLabel.alpha = 0.0f;
    self.indefiniteAnimatedView.alpha = 0.0f;
    self.ringView.alpha = self.backgroundRingView.alpha = 0.0f;
}

@property (assign, nonatomic) SVProgressHUDStyle defaultStyle UI_APPEARANCE_SELECTOR; // default is SVProgressHUDStyleLight

默认值是SVProgressHUDStyleLight

这里面清除了毛玻璃效果,将背景颜色和所有控价颜色设置为透明。
注意,这个时候并没有移除视图,只是单纯的设置颜色为透明。

completionBlock

__block void (^completionBlock)(void) = ^{
                // Check if we really achieved to dismiss the HUD (<=> alpha values are applied)
                // and the change of these values has not been cancelled in between e.g. due to a new show
                // 当背景视图为透明的时候,移除所有控件
                if(self.backgroundView.alpha == 0.0f){
                    // Clean up view hierarchy (overlays)
                    // 先移除自己所有控件的内容
                    [strongSelf.controlView removeFromSuperview];
                    [strongSelf.backgroundView removeFromSuperview];
                    [strongSelf.hudView removeFromSuperview];
                    // 移除自身
                    [strongSelf removeFromSuperview];
                    
                    // Reset progress and cancel any running animation
                    // 清空进度内容和取消所有的动画
                    strongSelf.progress = SVProgressHUDUndefinedProgress;
                    [strongSelf cancelRingLayerAnimation];
                    [strongSelf cancelIndefiniteAnimatedViewAnimation];
                    
                    // Remove observer <=> we do not have to handle orientation changes etc.
                    [[NSNotificationCenter defaultCenter] removeObserver:strongSelf];
                    
                    // Post notification to inform user
                    [[NSNotificationCenter defaultCenter] postNotificationName:SVProgressHUDDidDisappearNotification
                                                                        object:strongSelf
                                                                      userInfo:[strongSelf notificationUserInfo]];
                    
                    // Tell the rootViewController to update the StatusBar appearance
#if !defined(SV_APP_EXTENSIONS) && TARGET_OS_IOS
                    UIViewController *rootController = [[UIApplication sharedApplication] keyWindow].rootViewController;
                    /* This should be called whenever the return values for the view controller's status bar attributes have changed. If it is called from within an animation block, the changes will be animated along with the rest of the animation block. */
                    //更新控制器
                    [rootController setNeedsStatusBarAppearanceUpdate];
#endif
                    
                    // Run an (optional) completionHandler
                    // 完成用户定义的block内容
                    if (completion) {
                        completion();
                    }
                }
            };

这一部分的block块操作就是当前面设置的背景颜色为透明时候,开始移除自身的所有控件,最终,将自身也移除。
这里面还做了清除动画和进度条内容的处理。
最后,如果用户还有操作,则执行传入的completion(),显然,如果直接调用dismiss是没有的。

 //  delay是用户进行输入延迟的时间
            dispatch_time_t dipatchTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delay * NSEC_PER_SEC));
            // 延迟实现取消动画和完成动画
            dispatch_after(dipatchTime, dispatch_get_main_queue(), ^{
                
                // Stop timer
                strongSelf.graceTimer = nil;
                
                // strongSelf.fadeOutAnimationDuration默认是0.15 大于 0
                if (strongSelf.fadeOutAnimationDuration > 0) {
                    // Animate appearance
                    [UIView animateWithDuration:strongSelf.fadeOutAnimationDuration
                                          delay:0
                                        options:(UIViewAnimationOptions) (UIViewAnimationOptionAllowUserInteraction | UIViewAnimationCurveEaseOut | UIViewAnimationOptionBeginFromCurrentState)
                                     animations:^{
                                         //动画调用上面声明的block块
                                         animationsBlock();
                                     } completion:^(BOOL finished) {
                                         //完成实现的block块
                                         completionBlock();
                                     }];
                } else {
                    //执行block块内的操作
                    animationsBlock();
                    completionBlock();
                }
            });
            
            // Inform iOS to redraw the view hierarchy
            // 重新显示视图
            [strongSelf setNeedsDisplay];

最后,根据延迟效果,分别执行上面定义的两个block块,在这里面注意顺序性,根据传入参数为0直接执行else部分。(执行完成刷新视图)

animationsBlock();
completionBlock();

至此,就是SVProgressHUD的全部核心代码讲解,个人认为,SVProgressHUD主要教会的思想是清除机制,你可以看到,SVProgressHUD并不是粗鲁般的直接removeFromSuperview,而是将所有之前设置的属性先还原,然后在removeFromSuperview。这是值得我们学习的一部分。

对于属性参数设置这一部分,SVProgressHUD也做的十分细致。尤其需要注意,主线程的问题。

源码下载

中文源码: GitHub.

 类似资料: