一.问题
对于B2C和B2B项目的开发者,可能会有一个订单列表为空,或者其他收藏页面为空,用户token失效,判断用户要重新登陆,以及后台服务错误等提示。本篇课文,看完大约10分钟。
原本自己不想写空页面的展示,网络加载失败或者重新登陆的封装,想从网上找一个第三方的view,但找了很多都是让我继承他人的控制器,而我自己写好了继承基本的控制器,如果继承他人的控制器,自己的代码需要改比较多的地方,所以决定自己写(如果其他开发者,也有同样的困扰,可以直接复用,不需要继承)
二.不谈虚的,直接上源代码
1.首先写一个协议,以后view布局遵守此协议,实现其方法实现布局。
#import <Foundation/Foundation.h> @protocol IOAProtocol <NSObject> @optional // 需要 [super addSubViews] - (void)addSubViews; // 不需要[super prepare] 用作布局 - (void)prepare; @end
2.订单空页面的UI
#import <UIKit/UIKit.h> #import "IOAProtocol.h" @interface IOAEmptyPageView : UIView <IOAProtocol>//遵守协议 @property (nonatomic, readonly, strong) UIImageView *imageView; @property (nonatomic, readonly, strong) UILabel *titleLabel; @property (nonatomic, readonly, strong) UILabel *subTitleLabel; //可能有的空页面需要点击一下跳转(备用) @property (nonatomic, readonly, strong) UIButton *refreshButton; @property (nonatomic, copy) void (^clickCallback)(id object); @end
.m文件
#import "IOAEmptyPageView.h" @interface IOAEmptyPageView () @property (nonatomic, readwrite, strong) UIImageView *imageView; @property (nonatomic, readwrite, strong) UILabel *titleLabel; @property (nonatomic, readwrite, strong) UILabel *subTitleLabel; @property (nonatomic, readwrite, strong) UIButton *refreshButton; @end @implementation IOAEmptyPageView - (void)dealloc { } - (instancetype)initWithFrame:(CGRect)frame { if (self = [super initWithFrame:frame]) { self.backgroundColor = [UIColor whiteColor]; [self addSubViews]; [self prepare]; } return self; } - (void)addSubViews { [self addSubview:self.imageView]; [self addSubview:self.titleLabel]; [self addSubview:self.subTitleLabel]; [self addSubview:self.refreshButton]; } //利用masonry进行适配 - (void)prepare { [self.imageView mas_makeConstraints:^(MASConstraintMaker *make) { make.centerX.equalTo(self); make.bottom.equalTo(self.mas_centerY).offset(-5); make.left.greaterThanOrEqualTo(self); make.right.lessThanOrEqualTo(self); }]; [self.titleLabel mas_makeConstraints:^(MASConstraintMaker *make) { make.centerX.equalTo(self.imageView); make.top.equalTo(self.imageView.mas_bottom).offset(10); make.left.greaterThanOrEqualTo(self); make.right.lessThanOrEqualTo(self); make.height.equalTo(@30); }]; [self.subTitleLabel mas_makeConstraints:^(MASConstraintMaker *make) { make.centerX.equalTo(self.titleLabel); make.top.equalTo(self.titleLabel.mas_bottom).offset(10); make.left.greaterThanOrEqualTo(self); make.right.lessThanOrEqualTo(self); make.height.equalTo(@25); }]; [self.refreshButton mas_makeConstraints:^(MASConstraintMaker *make) { make.centerX.equalTo(self.subTitleLabel); make.top.equalTo(self.subTitleLabel.mas_bottom).offset(10); // make.left.greaterThanOrEqualTo(self); // make.right.lessThanOrEqualTo(self); make.height.equalTo(@30); make.width.equalTo(@100); }]; } #pragma mark - Actions - (void)clickedRefreshButton:(id)button { if (self.clickCallback) { self.clickCallback(self); } } #pragma mark - Setter / Getter - (UIImageView *)imageView { if (!_imageView) { _imageView = [UIImageView new]; _imageView.contentMode = UIViewContentModeCenter; _imageView.image = [UIImage imageNamed:@"EmptyImage"]; } return _imageView; } - (UILabel *)titleLabel { if (!_titleLabel) { _titleLabel = [UILabel new]; _titleLabel.textColor = RGB_HEXString(@"#323232"); _titleLabel.font = [UIFont systemFontOfSize:18]; _titleLabel.textAlignment = NSTextAlignmentCenter; } return _titleLabel; } - (UILabel *)subTitleLabel { if (!_subTitleLabel) { _subTitleLabel = [UILabel new]; _subTitleLabel.textColor = RGB_HEXString(@"#323232"); _subTitleLabel.font = [UIFont systemFontOfSize:16]; _subTitleLabel.textAlignment = NSTextAlignmentCenter; } return _subTitleLabel; } - (UIButton *)refreshButton { if (!_refreshButton) { _refreshButton = [UIButton buttonWithType:UIButtonTypeSystem]; [_refreshButton setTitle:@"重新加载" forState:UIControlStateNormal]; [_refreshButton setTitleColor:RGB_HEXString(@"#323232") forState:UIControlStateNormal]; _refreshButton.titleLabel.font = [UIFont systemFontOfSize:16]; [_refreshButton addTarget:self action:@selector(clickedRefreshButton:) forControlEvents:UIControlEventTouchUpInside]; _refreshButton.layer.cornerRadius = 6; _refreshButton.layer.borderColor = RGB_HEXString(@"#323232").CGColor; _refreshButton.layer.borderWidth = 1.0f; } return _refreshButton; } @end
封装调用方法(无数据提示,无数据跳转,后台服务错误以及token失效,请重新登陆)
#import <UIKit/UIKit.h> #import "IOAEmptyPageView.h" @interface UIViewController (Progress) - (void)startProgress; - (void)stopProgress; - (void)startProgress:(BOOL)animate; - (void)stopProgress:(BOOL)animate; // 提示框 - (void)showAlertWithTitle:(NSString *)title detail:(NSString *)deatil clickBlock:(void (^)(NSInteger index))clickBlock; - (void)showCommonAlertWithTitle:(NSString *)title clickBlock:(void(^)(NSInteger index))clickBlock; //- (void)hideCommonAlert; // 无网络 - (UIView *)showNoNetworkPage; - (void)dismissNoNetworkPage; //- (void)clickedNoNetworkPage:(id)object; // 空页面 - (UIView *)showNoDatasPage; - (void)dismissNoDatasPage; //订单空页面的提示(目前我用的) - (UIView *)showNoOrderPage:(NSString *)str withImage:(NSString *)image; //- (void)clickedNoDatasPage:(id)object; // 服务后台错误 - (UIView *)showServerErrorPage; - (void)dismissServerErrorPage; - (UIView *)showEmptyPageWith:(UIImage *)image title:(NSString *)title subTitle:(NSString *)subTitle; - (void)dismissEmptyPage; - (void)clickedEmptyPage:(id)object; // token 失效 push // 先弹出当前VC - (void)showLoginViewController; // 不弹出当前VC - (void)showLoginViewControllerNoPop; /* * 跳转到登录界面 * @paramater isNeedPopCurVC: YES 要弹出当前VC NO 不需要弹出 */ - (void)showLoginViewcontrollerWithPopCurrentVC:(BOOL)isNeedPopCurVC; @end
.m文件实现
#import "UIViewController+Progress.h" //借助第三方 #import <MBProgressHUD/MBProgressHUD.h> #import <MMPopupView/MMAlertView.h> //用户登陆控制器(导入你们自己的loginVc) #import "IOAUserLoginViewController.h" @implementation UIViewController (Progress) - (void)startProgress { [MBProgressHUD showHUDAddedTo:self.view animated:YES]; } - (void)stopProgress { [MBProgressHUD hideHUDForView:self.view animated:YES]; } - (void)startProgress:(BOOL)animate { [MBProgressHUD showHUDAddedTo:self.view animated:animate]; } - (void)stopProgress:(BOOL)animate { [MBProgressHUD hideHUDForView:self.view animated:NO]; } - (void)showAlertWithTitle:(NSString *)title detail:(NSString *)deatil clickBlock:(void (^)(NSInteger index))clickBlock { NSArray *items = @[ MMItemMake(@"取消", MMItemTypeNormal, clickBlock), MMItemMake(@"确定", MMItemTypeHighlight, clickBlock)]; MMAlertView *alertView = [[MMAlertView alloc] initWithTitle:title detail:deatil items:items]; // alertView.attachedView = self.view; alertView.attachedView.mm_dimBackgroundBlurEnabled = NO; alertView.attachedView.mm_dimBackgroundBlurEffectStyle = UIBlurEffectStyleLight; [alertView show]; } - (void)showCommonAlertWithTitle:(NSString *)title clickBlock:(void(^)(NSInteger index))clickBlock { [self showAlertWithTitle:title detail:nil clickBlock:clickBlock]; } // 无网络 - (UIView *)showNoNetworkPage { return [self showEmptyPageWith:nil title:@"网络请求失败" subTitle:@"请检查网络"]; } - (void)dismissNoNetworkPage { [self dismissEmptyPage]; } // 空页面 - (UIView *)showNoDatasPage { return [self showEmptyPageWith:nil title:@"网络请求成功" subTitle:@"数据为空"]; } //无订单---主要用的 - (UIView *)showNoOrderPage:(NSString *)str withImage:(NSString *)image{ return [self showEmptyOrderWith:[UIImage imageNamed:image] title:nil subTitle:str]; } - (void)dismissNoDatasPage { [self dismissEmptyPage]; } - (UIView *)showServerErrorPage { return [self showEmptyPageWith:nil title:@"网络请求失败" subTitle:@"后台服务错误"]; } - (void)dismissServerErrorPage { [self dismissEmptyPage]; } - (UIView *)showEmptyPageWith:(UIImage *)image title:(NSString *)title subTitle:(NSString *)subTitle { for (UIView *view in self.view.subviews) { if ([view isKindOfClass:[IOAEmptyPageView class]]) { return view; } } IOAEmptyPageView *emptyView = [IOAEmptyPageView new]; // emptyView.backgroundColor = [UIColor redColor]; if (image) { emptyView.imageView.image = image; } emptyView.titleLabel.text = title; emptyView.subTitleLabel.text = subTitle; WS(weakSelf); emptyView.clickCallback = ^(id object) { [weakSelf clickedEmptyPage:weakSelf]; [weakSelf dismissEmptyPage]; }; [self.view addSubview:emptyView]; UINavigationBar *navBar = self.navigationController.navigationBar; UITabBar *tarBar = self.tabBarController.tabBar; CGFloat topOffset = 0; CGFloat bottomOffset = 0; if (!navBar.hidden) { topOffset = TopHeightOffset; } if (!tarBar.hidden) { bottomOffset = BottomHeightOffset; } [emptyView mas_remakeConstraints:^(MASConstraintMaker *make) { make.left.right.equalTo(self.view); make.top.equalTo(self.view).offset(topOffset); make.bottom.equalTo(self.view).offset(-bottomOffset); }]; return emptyView; } - (UIView *)showEmptyOrderWith:(UIImage *)image title:(NSString *)title subTitle:(NSString *)subTitle { for (UIView *view in self.view.subviews) { if ([view isKindOfClass:[IOAEmptyPageView class]]) { return view; } } IOAEmptyPageView *emptyView = [IOAEmptyPageView new]; if (image) { emptyView.imageView.image = image; } emptyView.titleLabel.text = title; emptyView.subTitleLabel.text = subTitle; emptyView.refreshButton.hidden = YES; WS(weakSelf); emptyView.clickCallback = ^(id object) { [weakSelf clickedEmptyPage:weakSelf]; [weakSelf dismissEmptyPage]; }; [self.view addSubview:emptyView]; UINavigationBar *navBar = self.navigationController.navigationBar; UITabBar *tarBar = self.tabBarController.tabBar; CGFloat topOffset = 0; CGFloat bottomOffset = 0; if (!navBar.hidden&&![subTitle isEqualToString:@"你还没有收藏的商品"]) { topOffset = TopHeightOffset; } if (!tarBar.hidden) { bottomOffset = BottomHeightOffset; } [emptyView mas_remakeConstraints:^(MASConstraintMaker *make) { make.left.right.equalTo(self.view); make.top.equalTo(self.view).offset(topOffset); make.bottom.equalTo(self.view).offset(-bottomOffset); }]; return emptyView; } - (void)dismissEmptyPage { for (UIView *view in self.view.subviews) { if ([view isKindOfClass:[IOAEmptyPageView class]]) { [view removeFromSuperview]; return; } } } - (void)clickedEmptyPage:(id)object { [self dismissEmptyPage]; } // token 失效 push - (void)showLoginViewController { [self showLoginViewcontrollerWithPopCurrentVC:YES]; } // 不弹出当前VC - (void)showLoginViewControllerNoPop { [self showLoginViewcontrollerWithPopCurrentVC:NO]; } - (void)showLoginViewcontrollerWithPopCurrentVC:(BOOL)isNeedPopCurVC { if (!self.navigationController) return; IOAUserLoginViewController *loginVC = [IOAUserLoginViewController new]; NSMutableArray *array = [NSMutableArray array]; NSArray *vcList = self.navigationController.viewControllers; if ([[vcList lastObject] isKindOfClass:[IOAUserLoginViewController class]]) { return; } [array addObjectsFromArray:vcList]; if (array.count > 1 && isNeedPopCurVC) { [array removeLastObject]; } [array addObject:loginVC]; [self.navigationController setViewControllers:array animated:YES]; } #if 0 UIAlertController *alert = [UIAlertController alertControllerWithTitle:number message:@"拨打电话" preferredStyle:UIAlertControllerStyleAlert]; UIAlertAction *ok = [UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDestructive handler:^(UIAlertAction * _Nonnull action) { }]; UIAlertAction *no = [UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) { }]; unsigned int count = 0; Ivar *ivars = class_copyIvarList([UIAlertController class], &count); for(int i = 0; i<count; i ++) { Ivar ivar = ivars[i]; NSString *ivarName = [NSString stringWithCString:ivar_getName(ivar) encoding:NSUTF8StringEncoding]; //标题颜色 if ([ivarName isEqualToString:@"_attributedTitle"]) { NSMutableAttributedString *attr = [[NSMutableAttributedString alloc]initWithString:alert.title attributes:@{NSForegroundColorAttributeName:RGB_HEXString(@"#323232"),NSFontAttributeName:Font(14)}]; [alert setValue:attr forKey:@"attributedTitle"]; continue; } //描述颜色 if ([ivarName isEqualToString:@"_attributedMessage"]) { NSMutableAttributedString *attr = [[NSMutableAttributedString alloc]initWithString:alert.message attributes:@{NSForegroundColorAttributeName:RGB_HEXString(@"#646464"),NSFontAttributeName:Font(14)}]; [alert setValue:attr forKey:@"attributedMessage"]; continue; } } free(ivars); ivars = class_copyIvarList([UIAlertAction class], &count); for (int i=0; i<count; i++) { Ivar ivar = ivars[i]; NSString *name = [NSString stringWithCString:ivar_getName(ivar) encoding:NSUTF8StringEncoding]; if ([name isEqualToString:@"_titleTextColor"]) { [no setValue:RGB_HEXString(@"#323232") forKey:@"titleTextColor"]; [ok setValue:RGB_HEXString(@"#c30d23") forKey:@"titleTextColor"]; } } free(ivars); [alert addAction:no]; [alert addAction:ok]; [self presentViewController:alert animated:YES completion:nil]; #endif @end
3.应用:
[self.viewModel requestOrderShopList:self.requestModel callback:^(IOAResponse *response) { dispatch_async(dispatch_get_main_queue(), ^{ [self stopProgress]; NSArray <IOAOrderGroup *> *group = response.responseObject; if (group) { if (group.count == 0 && self.requestModel.page_no.integerValue == 0) { self.requestModel.page_no = [NSString stringWithFormat:@"%ld", self.requestModel.page_no.integerValue+1]; } [self.dataArr removeAllObjects]; [self.dataArr addObjectsFromArray:response.responseObject]; [self.tableView reloadData]; if (self.dataArr.count == 0) { //空页面 [self showNoOrderPage:@"目前还没有待付款订单" withImage:@"order"]; return ; } } else { if ([response isNoNetwork] && self.dataArr.count == 0) { //无网络 [self showNoNetworkPage]; return; } if ([response isRequestServerError]) { //后台错误 [self showServerErrorPage]; return; } } }); }];
可以直接copy,有一个点要主要
[self showServerErrorPage]这些方法,我是继承了三四层,你可以改成类方法,直接用类调用,这些都是源代码,可以用,这个暂不考虑上传git,谢谢观看