ProgressHUD is easy to use, typically taking only one flower indicator to load and a one-second text popover

First example, UIActivityIndicatorView, chrysanthemum

Call part

Swift call code

let hud = ProgressHUD.show() DispatchQueue.main.asyncAfter(deadline: .now() + 1) { hud? .hide() }Copy the code

Swift encapsulation code

func getWindow() -> UIView? {// SceneDelegate exists, So take the window let Windows = UIApplication. Shared. Windows if let vue = Windows. The first {return vue} return nil} the extension Static func show() -> ProgressHUD? { guard let view = getWindow() else{ return nil } let hud = ProgressHUD.showAdded(to: View) return hud} // hide func hide(){hideAnimated()}}Copy the code

The source code

  • MBProgressHUDMode has six modes, two of which are used by default and custom views

The first example, the mode is the default ProgressHUDModeIndeterminate

  • MBProgressHUD is a custom UIView,

His frame is the bounds passed into the view

Convenience method called

+ (instancetype)showHUDAddedTo:(UIView *)view{
    ProgressHUD *hud = [[self alloc] initWithView:view];
    [view addSubview:hud];
    [hud showAnimated];
    return hud;
}

Copy the code

The actual instantiation method,

Determines that the frame of ProgressHUD = the frame of the superview

- (id)initWithView:(UIView *)view {
    NSAssert(view, @"View must not be nil.");
    return [self initWithFrame:view.bounds];
}
Copy the code
  • The view hierarchy

MBProgressHUD, MBBackgroundView,

Frame of MBBackgroundView = Frame of MBProgressHUD

Chrysanthemum corresponds to the middle square, which is the second background view MBBackgroundView *bezelView

On top of _bezelView, I put a UILabel,

In this example, only chrysanthemum is displayed, but no Label is displayed, because the text of the Label is empty

  • Chrysanthemums in sight

Initialization continues


- (instancetype)initWithFrame:(CGRect)frame {
    if ((self = [super initWithFrame:frame])) {
        [self commonInit];
    }
    return self;
}
Copy the code

Basic configuration

- (void) commonInit {/ / Set the default values for the properties / / basic pattern _mode = ProgressHUDModeIndeterminate; // Basic configuration //... self.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; [self setupViews]; // View hierarchy [self updateIndicators]; }Copy the code

Update the prompt style

- (void)updateIndicators { UIView *indicator = self.indicator; BOOL isActivityIndicator = [indicator isKindOfClass:[UIActivityIndicatorView class]]; // ProgressHUDMode mode = self. Mode; if (mode == ProgressHUDModeIndeterminate) { if (! IsActivityIndicator) {// Update to indeterminate indicator StartAnimating [indicator removeFromSuperview]; indicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleLarge]; ((UIActivityIndicatorView *)indicator).color = UIColor.blackColor; [(UIActivityIndicatorView *)indicator startAnimating]; [self.bezelView addSubview:indicator]; }} / /... // Other modes}Copy the code
The layout is very distinctive

Multiple constraints, including visual constraints

- (void)updateConstraints {// bezelView second background view, Horizontally centered [centeringConstraints addObject:[NSLayoutConstraint constraintWithItem:bezel_pecs attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeCenterX multiplier:1.f constant: offset.x]]; // bezelView second layer background view, CenteringConstraints addObject:[NSLayoutConstraint constraintWithItem:bezel_pecs attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeCenterY multiplier:1.f constant: offset.y]]; NSMutableArray *subviews = [[NSMutableArray alloc] initWithArray: @[self.topSpacer, self.label, self.bottomSpacer]]; If (self. Indicator) {/ / child views, in the vertical direction, from top to bottom in turn is placeholder / / the top figure, chrysanthemum, text labels, at the bottom of the placeholder figure [subviews insertObject: self. The indicator atIndex: 1); } // Iterate over the setup layout NSMutableArray *paddingConstraints = [NSMutableArray new]; [subviews enumerateObjectsUsingBlock:^(UIView *view, NSUInteger idx, BOOL *stop) {// Center in bezel_pecs // [bezelConstraints addObject:[NSLayoutConstraint constraintWithItem:view attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:bezel_pecs attribute:NSLayoutAttributeCenterX multiplier:1.f constant:0.f]]; // Ensure the minimum edge margin is kept // Set the superview to [bezelConstraints addObjectsFromArray:[NSLayoutConstraint] constraintsWithVisualFormat:@"|-(>=margin)-[view]-(>=margin)-|" options:0 metrics:metrics views:NSDictionaryOfVariableBindings(view)]]; // Element spacing if (idx == 0) { ensure spacing to bezel_pecs edge [bezelConstraints addObject:[NSLayoutConstraint constraintWithItem:view attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:bezel_pecs attribute:NSLayoutAttributeTop multiplier:1.f constant:0.f]]; } else if (idx == subviews.count - 1) { ensure spacing to bezel_pecs edge [bezelConstraints addObject:[NSLayoutConstraint constraintWithItem:view attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:bezel_pecs attribute:NSLayoutAttributeBottom multiplier:1.f constant:0.f]]; } if (idx > 0) {// Has previous view NSLayoutConstraint *padding = [NSLayoutConstraint constraintWithItem:view attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:subviews[idx - 1] attribute:NSLayoutAttributeBottom multiplier:1.f constant:0.f]; [bezelConstraints addObject:padding];  [paddingConstraints addObject:padding]; } }]; // Iterate over the layout adjustment [self updatePaddingConstraints]; [super updateConstraints]; }Copy the code

Iterate to adjust the layout

- (void)updatePaddingConstraints { // Set padding dynamically, depending on whether the view is visible or not __block BOOL hasVisibleAncestors = NO; [self.paddingConstraints enumerateObjectsUsingBlock:^(NSLayoutConstraint *padding, NSUInteger idx, BOOL *stop) { UIView *firstView = (UIView *)padding.firstItem; UIView *secondView = (UIView *)padding.secondItem;  BOOL firstVisible = !firstView.hidden && ! CGSizeEqualToSize(firstView.intrinsicContentSize, CGSizeZero); BOOL secondVisible = !secondView.hidden && ! CGSizeEqualToSize(secondView.intrinsicContentSize, CGSizeZero);  // Set if both views are visible or if there's a visible view on top that doesn't have padding // added relative to the Current view yet / / a view, there is a distance between the padding. The constant = (firstVisible && (secondVisible | | hasVisibleAncestors))? 4. F: 0.f; hasVisibleAncestors |= secondVisible; }]; }Copy the code
  • Hide the prompt

As described above, display prompts

- (void)hideAnimated{// Check MBMainThreadAssert(); Self. finished = YES; // Hide the animation [self hideUsingAnimation]; }Copy the code

Hide the animation

- (void)hideUsingAnimation{// This timer, the main second example is useful to [self.hideDelayTimer invalidate]; If (self.showStarted) {// If (self.showStarted) = nil; [self animateIn:NO completion:^(BOOL finished) {self done;}]; }else { self.showStarted = nil; self.bezelView.alpha = 0.f; self.backgroundView.alpha = 1.f; [self done]; }}Copy the code

End prompt, remove view

- (void)done{if (self.hasFinished) {self.alpha = 0.0f; [self removeFromSuperview]; }}Copy the code

The second example, ProgressHUDModeCustomView, tooltip text

Call part

Swift call code

Progresshud. show(" defender ")Copy the code

Swift encapsulation code

@discardableResult static func show(_ message: String) -> ProgressHUD? { guard let view = getWindow(), message ! = "" else{ return nil } let hud = ProgressHUD.showAdded(to: View) // At this point, we see text because we gave the string hud.label.text = message // Custom view mode, However customView is empty hud. CustomView = nil hud. Mode = ProgressHUDMode. CustomView hud. Hide (afterDelay: 1.5) return hud}Copy the code

The source code

  • According to
- (void)setMode:(ProgressHUDMode)mode {if (mode! = _mode) { _mode = mode; [self updateIndicators]; }}Copy the code

Update indicator style

- (void)updateIndicators {// initWithFrame, run this method once, // self.indicator, UIActivityIndicatorView UIView *indicator = self.indicator; BOOL isActivityIndicator = [indicator isKindOfClass:[UIActivityIndicatorView class]]; ProgressHUDMode mode = self.mode; / /... / / the first example, bypassing / / this is the second example if (mode = = ProgressHUDModeCustomView && self. The customView! = indicator) { // Update custom view indicator [indicator removeFromSuperview]; // Indicator = self.customView; indicator.tintColor = UIColor.blackColor; [self.bezelView addSubview:indicator]; } indicator.translatesAutoresizingMaskIntoConstraints = NO; self.indicator = indicator; // do the layout //... [self setNeedsUpdateConstraints]; }Copy the code

You can see the text because you’re given a string

  • hidden

MBProgressHUD has a timer property,

@property (nonatomic, weak) NSTimer *hideDelayTimer;
Copy the code

HideAfterDelay automatically hides

- (void)hideAfterDelay:(NSTimeInterval)delay { // Cancel any scheduled hideAnimated:afterDelay: calls [self.hideDelayTimer invalidate]; // Initialize the timer when it is automatically hidden, Just call an NSTimer * timer = [NSTimer timerWithTimeInterval: delay target: self selector: @ the selector (the userInfo hideAnimated) : nil repeats:NO]; [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes]; self.hideDelayTimer = timer; }Copy the code

Tag hidden

- (void)hideAnimated{ // ... Self. Finished = YES; [self hideUsingAnimation]; }Copy the code

The structure of MBProgressHUD looks like this, with the actual source code extension a little richer

github repo