一.問題
對於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,謝謝觀看