iOS - write a mask View with RunTime!

A common list page needs a mask view when there is no data. Take a chestnut, as shown below:

Screenshot 10.24.21.png on September 17, 2018

The methods in the runtime function library used here are:

objc_setAssociatedObject(<#id  _Nonnull object#>, <#const void * _Nonnull key#>, <#id  _Nullable value#>, <#objc_AssociationPolicy policy#>)

id object: refers to the associated person. It is an object. The variable name is also an object
const void *key: get the index key of the associated person
id value: associated person, here is a block
Objc ﹣ associationpolicy policy: the protocol used in association, including assign ment, retain, copy and other protocols. Objc ﹣ Association ﹣ retain ﹣ nonatomic is generally used

Upper code

Create a new class to inherit ViewController (I will not show the UI built by Xib file here)
.h file

#import <UIKit/UIKit.h>

@class DXMaskViewController;

@interface UIViewController (DXMaskView)

@property (nonatomic,strong) DXMaskViewController* dxHudViewController;
@property (nonatomic,strong) DXMaskViewController* dxNoDataViewController;
// Loading
- (DXMaskViewController *)dxShowHUDIndeterminate;
- (DXMaskViewController *)dxShowHUDIndeterminateWithOffset:(float)offset;

- (void)dxHideHUDIndeterminate;
- (void)dxHideNoDataView;

//Newly added
- (DXMaskViewController *)dxShowNoDataViewOfTitle:(NSString *)title Detail:(NSString *)detail WithOffset:(float)offset;

@end


@interface DXMaskViewController : UIViewController

@property (weak, nonatomic) IBOutlet UIImageView *maskImageView;
@property (weak, nonatomic) IBOutlet UILabel *maskTitleLabel;
@property (weak, nonatomic) IBOutlet UIActivityIndicatorView *maskIndicatorView;
@property (weak, nonatomic) IBOutlet UILabel *maskDetailTitleLabel;
@property (weak, nonatomic) IBOutlet UIButton *maskOnTapView;
@property (nonatomic,copy) void(^onClick)(void);

@end
.m file

#import "DXMaskViewController.h"
#import <objc/runtime.h>
#import <Masonry/Masonry.h>
@interface DXMaskViewController ()

@end

@implementation DXMaskViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];

}
- (IBAction)onTapMask:(id)sender {
    if (self.onClick) {
        self.onClick();
    }
}
@end


@implementation UIViewController (DXMaskView)

-(void)setDxHudViewController:(DXMaskViewController *)dxHudViewController {
    objc_setAssociatedObject(self, @selector(dxHudViewController), dxHudViewController, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    
}

- (DXMaskViewController *)dxHudViewController {
    return objc_getAssociatedObject(self, @selector(dxHudViewController));
}

-(void)setDxNoDataViewController:(DXMaskViewController *)dxNoDataViewController {
    objc_setAssociatedObject(self, @selector(dxNoDataViewController), dxNoDataViewController, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
   
}
-(DXMaskViewController *)dxNoDataViewController {
    return objc_getAssociatedObject(self, @selector(dxNoDataViewController));
}


// Loading
- (DXMaskViewController*)dxShowHUDIndeterminate {
    return [self dxShowHUDIndeterminateWithOffset:0];
}

- (DXMaskViewController*)dxShowHUDIndeterminateWithOffset:(float)offset {
    DXMaskViewController *maskVc = self.dxHudViewController;
    if (!maskVc) {
        maskVc = [[DXMaskViewController alloc] initWithNibName:@"DXMaskViewController" bundle:nil];
        self.dxHudViewController = maskVc;
    }
    if ([self isKindOfClass:[UICollectionViewController class]] ) {
        if (!maskVc.view.superview) {
            [self addChildViewController:maskVc];
            [self.view addSubview:maskVc.view];
        }
        maskVc.view.frame = CGRectMake(0,0, self.view.frame.size.width, self.view.frame.size.height);
        
        
    }else if ([self isKindOfClass:[UITableViewController class]]) {
        if (!maskVc.view.superview) {
            [self addChildViewController:maskVc];
            [self.view addSubview:maskVc.view];
        }
        maskVc.view.frame = CGRectMake(0,0, self.view.frame.size.width, self.view.frame.size.height);

    }else {
        if (!maskVc.view.superview) {
            [self addChildViewController:maskVc];
            [self.view addSubview:maskVc.view];
        }
        [maskVc.view mas_makeConstraints:^(MASConstraintMaker *make) {
            make.top.equalTo(self.mas_topLayoutGuide).offset(offset);
            make.left.right.bottom.equalTo(self.view);
        }];
    }

    maskVc.maskImageView.image = [UIImage imageNamed:@""];
    maskVc.maskTitleLabel.text = @"Loading! Please wait a moment";//Loading! Please wait a moment
    maskVc.maskDetailTitleLabel.text = @"";//Maybe it's a surprise
    maskVc.maskIndicatorView.hidden = false;
    [maskVc.maskIndicatorView startAnimating];
    maskVc.maskOnTapView.hidden = true;
    maskVc.view.alpha = 1;
    
    return maskVc;
}
- (void)dxHideHUDIndeterminate {
    
    DXMaskViewController *maskVc = self.dxHudViewController;
    self.dxHudViewController = nil;
    if(maskVc) {
        [UIView animateWithDuration:0.2 animations:^{
            maskVc.view.alpha = 0;
        } completion:^(BOOL finished) {
            [maskVc.view removeFromSuperview];
            [maskVc removeFromParentViewController];
        }];
    }

}



- (DXMaskViewController*)dxShowNoDataViewOfTitle:(NSString *)title Detail:(NSString *)detail WithOffset:(float)offset {
    return [self dxShowNoDataViewWithTitle:title detail:detail image:[UIImage imageNamed:@"Empty page"] animating:false offset:offset];
}



- (DXMaskViewController*)dxShowNoDataViewWithTitle:(NSString*)title detail:(NSString*)detail image:(UIImage*)image animating:(BOOL)animating {
    
    return [self dxShowNoDataViewWithTitle:@"" detail:@"" image:[UIImage imageNamed:@"Empty page"] animating:false offset:0];

}

- (DXMaskViewController*)dxShowNoDataViewWithTitle:(NSString*)title detail:(NSString*)detail image:(UIImage*)image animating:(BOOL)animating offset:(float)offset {
    
    DXMaskViewController *maskVc = self.dxNoDataViewController;
    if (!maskVc) {
        maskVc = [[DXMaskViewController alloc] initWithNibName:@"DXMaskViewController" bundle:nil];
        self.dxNoDataViewController = maskVc;
    }
    if ([self isKindOfClass:[UICollectionViewController class]] ) {
        if (!maskVc.view.superview) {
            [self addChildViewController:maskVc];
            [self.view addSubview:maskVc.view];
        }
        maskVc.view.frame = CGRectMake(0,0, self.view.frame.size.width, self.view.frame.size.height);

        
    }else if ([self isKindOfClass:[UITableViewController class]]) {
        if (!maskVc.view.superview) {
            [self addChildViewController:maskVc];
            [self.view addSubview:maskVc.view];
        }
        maskVc.view.frame = CGRectMake(0,0, self.view.frame.size.width, self.view.frame.size.height);

    }else {
        if (!maskVc.view.superview) {
            [self addChildViewController:maskVc];
            [self.view addSubview:maskVc.view];
        }
        [maskVc.view mas_makeConstraints:^(MASConstraintMaker *make) {
            make.top.equalTo(self.mas_topLayoutGuide).offset(offset);
            make.left.right.bottom.equalTo(self.view);
        }];
    }
    maskVc.maskImageView.image = image;
    maskVc.maskTitleLabel.text = title;
    maskVc.maskDetailTitleLabel.text = detail;
    if (animating) {
        maskVc.maskIndicatorView.hidden = false;
        [maskVc.maskIndicatorView startAnimating];
    }else{
        maskVc.maskIndicatorView.hidden = true;
        [maskVc.maskIndicatorView stopAnimating];
    }
    maskVc.view.alpha = 1;
    maskVc.maskOnTapView.hidden = false;
    
    return maskVc;
}


- (void)dxHideNoDataView {
    
    DXMaskViewController *maskVc = self.dxNoDataViewController;
    self.dxNoDataViewController = nil;

    if (maskVc) {
        [UIView animateWithDuration:0.2 animations:^{
            maskVc.view.alpha = 0;
        } completion:^(BOOL finished) {
            [maskVc.view removeFromSuperview];
            [maskVc removeFromParentViewController];
        }];
    }
}


@end
When the list page requests data, use the following

Note: offset can set the view offset

//Default mask
                        if (self.dataArray.count) {
                            [self dxHideNoDataView];
                        }else {
                            DXMaskViewController *maskView = [self dxShowNoDataViewOfTitle:@"No listings found" Detail:@"" WithOffset:60];
                            [maskView setOnClick:^{
                                //The callback here can be re requested
                            }];
                        }

I hope you have a good time

Posted by vahidi on Mon, 30 Dec 2019 20:25:33 -0800