经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 移动开发 » iOS » 查看文章
MBProgressHUD源码(上)
来源:cnblogs  作者:苍穹Layne  时间:2019/6/19 11:22:36  对本文有异议

本篇博文记录MBProgressHUD源码学习过程,从官方提供的Demo项目入手,一步步了解其代码结构,学习它使用的技术,体会作者的编程思想。

一、结构

我们先来看下MBProgressHUD的结构,查看其类的定义。
1.MBProgressHUD是UIView的子类。
2.属性:

  1. 1.
  2. //代理,<MBProgressHUDDelegate>仅定义了一个方法:- (void)hudWasHidden:(MBProgressHUD *)hud;用于执行HUD隐藏之后的操作
  3. @property (weak, nonatomic) id<MBProgressHUDDelegate> delegate;
  4. //执行HUD隐藏之后的操作的Block,目的同上
  5. @property (copy, nullable) MBProgressHUDCompletionBlock completionBlock;
  6. 2.
  7. //延迟时间,若任务在graceTime到时之前就完成了,HUD不再展示,即防止为短时间任务显示HUD
  8. @property (assign, nonatomic) NSTimeInterval graceTime;
  9. //最短展示时间,防止HUD隐藏的过快
  10. @property (assign, nonatomic) NSTimeInterval minShowTime;
  11. //配置HUD是否隐藏之后就从其superview上移除。默认NO
  12. @property (assign, nonatomic) BOOL removeFromSuperViewOnHide;
  13. 3.
  14. //指定进度条的样式,包括菊花、圆饼、环形、水平进度条、自定义样式和纯文本等
  15. @property (assign, nonatomic) MBProgressHUDMode mode;
  16. //内容(label+indicator+customView)颜色
  17. @property (strong, nonatomic, nullable) UIColor *contentColor;
  18. //显示和隐藏时的动画类型:Fade(淡入淡出)、Zoom(放大显示缩小隐藏)、ZoomIn、ZoomOut
  19. @property (assign, nonatomic) MBProgressHUDAnimation animationType;
  20. //内容框(bezelView)距离中心位置的偏移,例如CGPointMake(0.f, MBProgressMaxOffset),内容框会在底部居中
  21. @property (assign, nonatomic) CGPoint offset;
  22. @property (assign, nonatomic) CGFloat margin;//各元素到HUD的边距
  23. @property (assign, nonatomic) CGSize minSize;//内容框的最小尺寸
  24. @property (assign, nonatomic, getter = isSquare) BOOL square;//强制HUD为方形
  25. @property (assign, nonatomic, getter=areDefaultMotionEffectsEnabled) BOOL defaultMotionEffectsEnabled;//内容框(bezelView)是否受设备加速计的影响,默认YES
  26. 4.
  27. @property (assign, nonatomic) float progress;//进度
  28. @property (strong, nonatomic, nullable) NSProgress *progressObject;//进度对象,用于更新进度条
  29. 5.
  30. //内容框,即展示实际内容(文本、indicator)的矩形框
  31. @property (strong, nonatomic, readonly) MBBackgroundView *bezelView;
  32. //背景试图,会覆盖整个屏幕
  33. @property (strong, nonatomic, readonly) MBBackgroundView *backgroundView;
  34. //自定义视图用于展示
  35. @property (strong, nonatomic, nullable) UIView *customView;
  36. @property (strong, nonatomic, readonly) UILabel *label;//文本
  37. @property (strong, nonatomic, readonly) UILabel *detailsLabel;//文本下面的详细文本
  38. @property (strong, nonatomic, readonly) UIButton *button;//文本下面的action button

3.其他相关类

(1) MBBackgroundView

  • UIView的子类,在MBPRogressHUD中充当内容框(bezelView)背景视图(backgroundView)两种角色。
  • 提供两种样式:清晰样式(MBProgressHUDBackgroundStyleSolidColor)模糊样式(MBProgressHUDBackgroundStyleBlur)
  • 模糊样式是通过UIVisualEffectViewUIBlurEffect实现的。

(2) MBRoundProgressView

  • UIView的子类,展示为饼状/环形的进度条。

(3) MBBarProgressView

  • UIView的子类,展示为条状的进度条。

(4) MBProgressHUDRoundedButton

  • UIButton的子类,展示位圆角button,作为HUD上的功能按钮。

知识点:HUD中有个button属性如下:

  1. /**
  2. * A button that is placed below the labels. Visible only if a target / action is added.
  3. */
  4. @property (strong, nonatomic, readonly) UIButton *button;

注意它的注释Visible only if a target / action is added。也就是说,只有给button添加事件之后,该按钮才会展示出来。这是如何做到的呢?那就是重写UIView的函数- (CGSize)intrinsicContentSize:

  1. - (CGSize)intrinsicContentSize {
  2. // Only show if we have associated control events
  3. if (self.allControlEvents == 0) return CGSizeZero;
  4. CGSize size = [super intrinsicContentSize];
  5. // Add some side padding
  6. size.width += 20.f;
  7. return size;
  8. }

这个函数用来设置控件的内置尺寸。可以看到,通过判断allControlEvents的个数来判断button上是否有事件,如果有事件,就在原来内置的尺寸上加20。

二、代码追踪

了解了MBProgressHUD的基本结构之后,接下来我们就看看具体的功能是如何实现的。HUDDemo提供了15个样例,我们选取纯文本加载(菊花)条状进度条自定义视图进行分析,其他的样例与它们类似。

1.纯文本(Text)

我们先从最简单的纯文本开始。启动HUDDemo项目,点开MBHudDemoViewController.m文件,找到函数- (void)textExample{…},这个函数就是显示纯文本的处理函数:

  1. - (void)textExample {
  2. MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:self.navigationController.view animated:YES];
  3. // Set the text mode to show only text.
  4. hud.mode = MBProgressHUDModeText;
  5. hud.label.text = NSLocalizedString(@"Message here!", @"HUD message title");
  6. // Move to bottm center.
  7. hud.offset = CGPointMake(0.f, MBProgressMaxOffset);
  8. [hud hideAnimated:YES afterDelay:3.f];
  9. }

① 进入到函数showHUDAddedTo:animated:中查看MBProgressHUD实例的创建过程:

  • initWithView:->initWithFrame:->commonInit

    使用self.navigationController.view的bounds初始化HUD,然后在commonInit里指定动画类型(Fade)、HUD模式(菊花)、间距(20)、内容颜色(黑色半透明)。除此之外,还设置HUD为完全透明,背景色为clear,配置HUD的尺寸自动调整

    1. //保证上下间距比例不变、左右间距比例不变,即防止横竖屏切换时HUD位置错误
    2. self.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
    3. //让HUD的各个子视图自己控制自己的透明度,使其不受HUD透明度的影响
    4. self.layer.allowsGroupOpacity = NO;
  • [self setupViews]

    在这个函数中真正执行子视图的创建工作。

    • 背景视图(backgroundView)

      为类MBBackgroundView的实例。MBBackgroundView实例默认会创建成白色半透明模糊效果,并覆盖全屏,但在本例中,创建完成之后会更改其styleMBProgressHUDBackgroundStyleSolidColor,并将背景色设置为透明(clear)。

    • 内容框(bezelView)

      同为类MBBackgroundView实例,是实际展示内容的View(即中间的黑框),包含文本、indicator、进度条等。bezelView会默认创建成白色半透明模糊效果,但frame为0。创建后会设置其边角半径为5。

      知识点:作者为bezelView添加了MotionEffect,也就是说在bezelView显示的时候,它会根据手机的倾斜方向调整自己的位置!

      1. - (void)updateBezelMotionEffects {
      2. #if __IPHONE_OS_VERSION_MAX_ALLOWED >= 70000 || TARGET_OS_TV
      3. MBBackgroundView *bezelView = self.bezelView;
      4. if (![bezelView respondsToSelector:@selector(addMotionEffect:)]) return;
      5. if (self.defaultMotionEffectsEnabled) {
      6. CGFloat effectOffset = 10.f;
      7. UIInterpolatingMotionEffect *effectX = [[UIInterpolatingMotionEffect alloc] initWithKeyPath:@"center.x" type:UIInterpolatingMotionEffectTypeTiltAlongHorizontalAxis];
      8. effectX.maximumRelativeValue = @(effectOffset);
      9. effectX.minimumRelativeValue = @(-effectOffset);
      10. UIInterpolatingMotionEffect *effectY = [[UIInterpolatingMotionEffect alloc] initWithKeyPath:@"center.y" type:UIInterpolatingMotionEffectTypeTiltAlongVerticalAxis];
      11. effectY.maximumRelativeValue = @(effectOffset);
      12. effectY.minimumRelativeValue = @(-effectOffset);
      13. UIMotionEffectGroup *group = [[UIMotionEffectGroup alloc] init];
      14. group.motionEffects = @[effectX, effectY];
      15. [bezelView addMotionEffect:group];
      16. } else {
      17. NSArray *effects = [bezelView motionEffects];
      18. for (UIMotionEffect *effect in effects) {
      19. [bezelView removeMotionEffect:effect];
      20. }
      21. }
      22. #endif
      23. }
    • label和detailsLabel

      设置显示文字的label,其中detailsLabel允许多行。

    • button

      MBProgressHUDRoundedButton的实例,作为HUD上的功能按钮,比如进度条下方可以显示一个"取消"按钮。

    • topSpacer和bottomSpacer

      均为UIView的实例,用于调节上下间距的辅助View。

    • 设置label、detailsLabel及button的抗压系数,并添加到父视图上。

      1. for (UIView *view in @[label, detailsLabel, button]) {
      2. view.translatesAutoresizingMaskIntoConstraints = NO;//自己手动管理约束
      3. [view setContentCompressionResistancePriority:998.f forAxis:UILayoutConstraintAxisHorizontal];//设置水平抗压缩系数,值越大,越不容易被压缩
      4. [view setContentCompressionResistancePriority:998.f forAxis:UILayoutConstraintAxisVertical];//设置垂直抗压缩系数,值越大,越不容易被压缩
      5. [bezelView addSubview:view];
      6. }
  • [self updateIndicators]

    HUD的indicatorUIView的实例,用来记录HUD上显示的视图,进度条、加载图标(菊花)、自定义视图等都是用HUD的indicator属性记录的。在函数- (void)updaetIndicators中,根据HUD的mode值配置不同的indicator。最后会调用[self setNeedsUpdateConstraints]触发约束更新函数-(void)updateConstraints来更新UI。

  • [self registerForNotifications]

    注册通知,处理屏幕旋转的问题。

② 在创建完HUD之后,会调用[hud showAnimated:animated];将HUD展示到屏幕上。事实上,虽然当前HUD已经在屏幕上了,但由于初始化HUD的时候bezelView的frame为0,用户看不到。

③ 配置HUD实例的属性

  1. hud.mode = MBProgressHUDModeText;//设置hud只显示纯文本
  2. hud.label.text = NSLocalizedString(@"Message here!", @"HUD message title");//设置文本内容
  3. hud.offset = CGPointMake(0.f, MBProgressMaxOffset);//设置hud相对于中心位置的偏移

在mode的setter函数中会调用- (void)updateIndicators,根据mode的新值重新配置indicator,然后调用- (void)setNeedsUpdateConstraints触发-(void)updateConstraints来更新UI。而在offset的setter函数中会直接调用- (void)setNeedsUpdateConstraints触发-(void)updateConstraints来更新UI。

④ 在函数- (void)updateConstraints中更新布局:

  • 删除所有控件的constraints
  • 通过NSLayoutConstraint重新设置constraints
  1. //1.以屏幕中心为基准,应用offset。priority = 998
  2. CGPoint offset = self.offset;
  3. NSMutableArray *centeringConstraints = [NSMutableArray array];
  4. //x
  5. [centeringConstraints addObject:[NSLayoutConstraint constraintWithItem:bezel attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeCenterX multiplier:1.f constant:offset.x]];
  6. //y
  7. [centeringConstraints addObject:[NSLayoutConstraint constraintWithItem:bezel attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeCenterY multiplier:1.f constant:offset.y]];
  8. //为每个constraints设置priority
  9. [self applyPriority:998.f toConstraints:centeringConstraints];
  10. [self addConstraints:centeringConstraints];
  11. //2.设置最小间距约束,priority = 999
  12. NSMutableArray *sideConstraints = [NSMutableArray array];
  13. [sideConstraints addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:@"|-(>=margin)-[bezel]-(>=margin)-|" options:0 metrics:metrics views:NSDictionaryOfVariableBindings(bezel)]];
  14. [sideConstraints addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-(>=margin)-[bezel]-(>=margin)-|" options:0 metrics:metrics views:NSDictionaryOfVariableBindings(bezel)]];
  15. [self applyPriority:999.f toConstraints:sideConstraints];
  16. [self addConstraints:sideConstraints];
  17. //3.bezel的最小尺寸约束 priority = 997
  18. CGSize minimumSize = self.minSize;
  19. if (!CGSizeEqualToSize(minimumSize, CGSizeZero)) {
  20. NSMutableArray *minSizeConstraints = [NSMutableArray array];
  21. [minSizeConstraints addObject:[NSLayoutConstraint constraintWithItem:bezel attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationGreaterThanOrEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.f constant:minimumSize.width]];
  22. [minSizeConstraints addObject:[NSLayoutConstraint constraintWithItem:bezel attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationGreaterThanOrEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.f constant:minimumSize.height]];
  23. [self applyPriority:997.f toConstraints:minSizeConstraints];
  24. [bezelConstraints addObjectsFromArray:minSizeConstraints];
  25. }
  26. //4.方形约束 priority=997
  27. if (self.square) {
  28. NSLayoutConstraint *square = [NSLayoutConstraint constraintWithItem:bezel attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:bezel attribute:NSLayoutAttributeWidth multiplier:1.f constant:0];
  29. square.priority = 997.f;
  30. [bezelConstraints addObject:square];
  31. }
  32. //5.根据margin和设置上下spacer的间距约束
  33. [topSpacer addConstraint:[NSLayoutConstraint constraintWithItem:topSpacer attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationGreaterThanOrEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.f constant:margin]];
  34. [bottomSpacer addConstraint:[NSLayoutConstraint constraintWithItem:bottomSpacer attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationGreaterThanOrEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.f constant:margin]];
  35. [bezelConstraints addObject:[NSLayoutConstraint constraintWithItem:topSpacer attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:bottomSpacer attribute:NSLayoutAttributeHeight multiplier:1.f constant:0.f]];
  36. //6.设置bezel子视图(topSpacer、label、detailLabel、button、bottomSpacer)的约束
  37. [subviews enumerateObjectsUsingBlock:^(UIView *view, NSUInteger idx, BOOL *stop) {
  38. // Center in bezel
  39. [bezelConstraints addObject:[NSLayoutConstraint constraintWithItem:view attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:bezel attribute:NSLayoutAttributeCenterX multiplier:1.f constant:0.f]];
  40. // Ensure the minimum edge margin is kept
  41. [bezelConstraints addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:@"|-(>=margin)-[view]-(>=margin)-|" options:0 metrics:metrics views:NSDictionaryOfVariableBindings(view)]];
  42. // Element spacing
  43. if (idx == 0) {
  44. // First, ensure spacing to bezel edge
  45. [bezelConstraints addObject:[NSLayoutConstraint constraintWithItem:view attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:bezel attribute:NSLayoutAttributeTop multiplier:1.f constant:0.f]];
  46. } else if (idx == subviews.count - 1) {
  47. // Last, ensure spacing to bezel edge
  48. [bezelConstraints addObject:[NSLayoutConstraint constraintWithItem:view attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:bezel attribute:NSLayoutAttributeBottom multiplier:1.f constant:0.f]];
  49. }
  50. if (idx > 0) {
  51. // Has previous
  52. NSLayoutConstraint *padding = [NSLayoutConstraint constraintWithItem:view attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:subviews[idx - 1] attribute:NSLayoutAttributeBottom multiplier:1.f constant:0.f];
  53. [bezelConstraints addObject:padding];
  54. [paddingConstraints addObject:padding];
  55. }
  56. }];
  57. [bezel addConstraints:bezelConstraints];
  58. self.bezelConstraints = bezelConstraints;
  59. self.paddingConstraints = [paddingConstraints copy];
  60. [self updatePaddingConstraints];//在该函数里,根据子视图的可视性(hidden),设置子视图的上下间距(为4)

通过上面的priority可以知道优先级:最小间距约束>bezel的偏移约束>bezel最小尺寸约束=方形约束。因此,如果你设置了hud.square = YES,但是实际bezel并没有变为方形,则很可能是因为上面的这几个约束之间存在冲突,系统采用了高优先级的约束而忽略了square约束。不信你可以把square优先级改为1000试试看:)

知识点:这里出现了一个宏NSDictionaryOfVariableBindings,它可以用来方便的创建NSDictionary:

  1. UIView *view1 = [UIView new];
  2. UIView *view2 = [UIView new];
  3. NSDictionary *dict = NSDictionaryOfVariableBindings(view1,view2);//{@"view1":view1,@"view2":view2}

总结:

? 至此,我们来总结下纯文本HUD的整个创建及显示流程:

  1. 调用[MBProgressHUD showHUDAddedTo:self.navigationController.view animated:YES]创建HUD实例:包括配置属性默认值(动画类型、HUD样式、间距、内容颜色等),初始化view(backgroundView、bezelView、label、detailLabel、button、topSpacer、bottomSpacer),且会默认创建一个indicator。之后hud会显示在屏幕上,但由于约束未触发,因此用户看不到。
  2. hud.mode = MBProgressHUDModeText。HUD会根据mode的值去隐藏indicator,并更新约束。
  3. hud.label.text = NSLocalizedString(@"Message here!", @"HUD message title")设置要显示的文字。
  4. hud.offset = CGPointMake(0.f, MBProgressMaxOffset)设置bezelView的偏移属性:让其显示在最底部。并更新约束。
  5. [hud hideAnimated:YES afterDelay:3.f]设置一个延迟timer,在3s之后隐藏hud。隐藏之后调用completionBlock和代理方法- (void)hudWasHidden:(MBProgressHUD *)hud;

2.加载(菊花)

加载样式表现为一个旋转的菊花,底部也可包含"Loading…"等字样提示。我们以包含"Loading…"字样的HUD为例剖析其内部原理。代码如下:

  1. - (void)labelExample {
  2. MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:self.navigationController.view animated:YES];
  3. // Set the label text.
  4. hud.label.text = NSLocalizedString(@"Loading...", @"HUD loading title");
  5. // You can also adjust other label properties if needed.
  6. // hud.label.font = [UIFont italicSystemFontOfSize:16.f];
  7. dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0), ^{
  8. [self doSomeWork];
  9. dispatch_async(dispatch_get_main_queue(), ^{
  10. [hud hideAnimated:YES];
  11. });
  12. });
  13. }
  • 调用MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:self.navigationController.view animated:YES];创建HUD实例,过程跟纯文本是一样的。
  • hud.label.text = NSLocalizedString(@"Loading...", @"HUD loading title");配置提示文案为"Loading"。
  • 之后在global_queue里面执行任务,完成任务之后回到主线程隐藏HUD。

通过分析纯文本HUD的创建过程我们知道,hud在初始化的时候,它的mode默认为MBProgressHUDModeIndeterminate,也就是说单纯的调用MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:self.navigationController.view animated:YES];创建出来的HUD就是带有菊花加载控件的HUD,我们接下来做的就是给它的label赋上文案即可。

3.条状进度条

MBProgressHUD提供了三种样式的进度条:条状、饼状、环状。其中饼状和环状差不多,接下来我们分析下条状进度条的实现原理:

  1. - (void)barDeterminateExample {
  2. MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:self.navigationController.view animated:YES];
  3. // Set the bar determinate mode to show task progress.
  4. hud.mode = MBProgressHUDModeDeterminateHorizontalBar;
  5. hud.label.text = NSLocalizedString(@"Loading...", @"HUD loading title");
  6. dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0), ^{
  7. // Do something useful in the background and update the HUD periodically.
  8. [self doSomeWorkWithProgress];
  9. dispatch_async(dispatch_get_main_queue(), ^{
  10. [hud hideAnimated:YES];
  11. });
  12. });
  13. }
  • 调用MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:self.navigationController.view animated:YES];创建HUD实例,过程跟纯文本是一样的。
  • hud.mode = MBProgressHUDModeDeterminateHorizontalBar;设置mode为条状进度条。在mode的setter方法中会调用- (void)updateIndicator创建进度条indicator。
  • 进度条indicator是类MBBarProgressView的实例。创建时默认宽为120,高为20,内容高度(intrinsicContentSize)为10。它的样式是在- (void)drawRect中绘制的。在其progress属性的setter方法中调用了-(void)setNeedsDisplay从而触发- (void)drawRect来更新进度。
  • hud.label.text = NSLocalizedString(@"Loading...", @"HUD loading title");配置HUD提示文案为"Loading"。

4.自定义视图

MBProgressHUD提供了显示自定义视图的功能。在Demo中是展示一个对勾。

  1. - (void)customViewExample {
  2. MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:self.navigationController.view animated:YES];
  3. // Set the custom view mode to show any view.
  4. hud.mode = MBProgressHUDModeCustomView;
  5. // Set an image view with a checkmark.
  6. UIImage *image = [[UIImage imageNamed:@"Checkmark"] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
  7. hud.customView = [[UIImageView alloc] initWithImage:image];
  8. // Looks a bit nicer if we make it square.
  9. hud.square = YES;
  10. // Optional label text.
  11. hud.label.text = NSLocalizedString(@"Done", @"HUD done title");
  12. [hud hideAnimated:YES afterDelay:3.f];
  13. }
  • 调用MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:self.navigationController.view animated:YES];创建HUD实例,过程跟纯文本是一样的。
  • hud.mode = MBProgressHUDModeCustomView;设置mode为自定义视图。接下来将需要展示的自定义视图赋值给hud的customView属性。在customView属性的setter方法中会调用- (void)updateIndicatorscustomView添加到HUD上。
  • 为了让界面美观,规定hud显示为方形:hud.square = YES;
  • hud.label.text = NSLocalizedString(@"Loading...", @"HUD loading title");配置HUD提示文案为"Loading"。

至此,我们已经简单了解了MBProgressHUD的整个代码结构及使用流程,这已经足够我们去创建和使用符合我们需求的HUD了。但其实MBProgressHUD的源码中还包含不少高级的技术细节,我们将在下篇文章中一个个分析学习。

原文链接:http://www.cnblogs.com/zlayne89/p/11049970.html

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号