通常我们在项目中会建一个BaseViewController或者CommonViewController作为基类,在里面实现全局性的基础功能,以后所有的ViewController均继承该基类。
既然要在此CommonViewController里写“全局性的基本功能”,那想想都有哪些东西呢?我列出了下面这些:
1.自定义界面:为了使项目具有较好的灵活性及兼容性,我并没有使用系统的导航栏和view,而是进行了自定义。这样不仅更灵活,而且结构更清楚简洁。
2.导航栏的一些通用点击事件:比如点击左边按钮返回上个界面,点击右边按钮扫一扫,点击导航栏scrollerView滑动到最顶部等。CommonViewController中写全局性的点击事件处理,各子类若有不同处理,只需重写事件处理方法即可。
3.一些功能方法(修改导航栏的样式,比如背景色,按钮的title、图标等):每个界面的这些东西不同,需要提供方法定制。
4.对显示“加载动画”、“加载失败”、“数据为空”几种视图的封装:
5.对发送网络请求,并显示加载动画的合并封装:一般我们请求了网络接口后就要使其显示加载动画。方便起见,我们将两者合并封装。这样,我们只要发送了接口请求,便自动显示加载动画。
** 6.全局滑动手势:**如果项目要求每个界面需要自定义侧滑返回,这是全局性的东西,所以得写。
7.可以选择是否监听键盘通知:暴露给外部一个isListensKeyboard
属性,意为是否监听键盘通知,默认是NO。并重写该属性的setter方法,在其中判断若为YES,则需要监听键盘通知,就设置该self为处理键盘通知的代理;若为NO,即该VC不需要监听键盘,那我们便不设代理。
8.设置状态栏颜色:有多种方式,plist文件配置和代码修改,这里指通过代码修改。
** 9.其他多种全局性的基础功能.....**
talk is cheap,so...
** CommonViewController.h **
#import <UIKit/UIKit.h>
#import "RedPointView.h"
#import "TipView.h"
@interface CommonViewController : UIViewController
@property (nonatomic, strong)UIView *contentView;
@property (nonatomic, strong)UIView *titleBarView; // 导航栏视图
@property (nonatomic, strong)RedPointView *leftBtnView; // 导航栏上左按钮
@property (nonatomic, strong)RedPointView *rightBtnView; // 导航栏上右按钮
@property (nonatomic, strong)UILabel *titleBarLab; // 导航栏中间标题Label
@property (nonatomic, strong)UIView *titleBarBottomLine; // 导航栏底部分隔线
@property (nonatomic, strong)TipView *tipView; // loading视图
@property (nonatomic, assign)BOOL isListensKeyboard; //是否监听键盘改变,默认是NO
@property (nonatomic,assign)BOOL isBanGesturesSliding; // 是否禁止手势滑动
// 修改导航栏左右按钮为文本形式
- (void)changeTitleBarButtonText:(NSString *)text buttonType:(NSInteger)btnType;
// 修改导航栏左右按钮为图标形式
- (void)changeTitleBarButtonImage:(NSString *)imgName buttonType:(NSInteger)btnType;
// 显示、出现tipView
- (void)showTipsView:(TipType)tipType;
// 隐藏、消失tipView
- (void)hideTipsView;
// 将请求网络数据和显示加载动画合并,使其一请求网络同时便开始loading动画。
- (void)startNetworkRequestAction:(SEL)action tipType:(TipType)tipType;
// 带参数object
- (void)startNetworkRequestAction:(SEL)action tipType:(TipType)tipType withObject:(id)object;
@end
** CommonViewController.m**
#import "CommonViewController.h"
#import "AppDelegate.h"
@interface CommonViewController ()<YWKeyboardDelegate>
@end
@implementation CommonViewController
#pragma mark ---- life cycyle
- (instancetype)init
{
self = [super init];
if(self)
{
if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 7.0)
{
self.edgesForExtendedLayout = UIRectEdgeNone;
self.extendedLayoutIncludesOpaqueBars = NO;
self.automaticallyAdjustsScrollViewInsets = NO;
}
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
// init views
[self loadCommonTitleView];
[self loadCommonContentView];
// 添加自定义的全局的侧滑返回
[self fullScreenSlide];
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
// 使状态栏和导航栏隐藏(必须写在viewWillAppear:方法里,否则无效)
self.navigationController.navigationBarHidden = YES;
}
#pragma mark ---- init View
- (void)loadCommonTitleView
{
// _titleBarView
_titleBarView = [UIView create];
_titleBarView.backgroundColor = [UIColor grayColor];
[self.view addSubview:_titleBarView];
LAY(_titleBarView.left, self.view.left, 1, 0);
LAY(_titleBarView.right, self.view.right, 1, 0);
LAY(_titleBarView.top, self.view.top, 1, 0);
LAYC(_titleBarView.height, 64.f);
// _titleBarBottomLine
_titleBarBottomLine = [UIView create];
_titleBarBottomLine.backgroundColor = [UIColor redColor];
[_titleBarView addSubview:_titleBarBottomLine];
LAY(_titleBarBottomLine.left, _titleBarView.left, 1, 0);
LAY(_titleBarBottomLine.right, _titleBarView.right, 1, 0);
LAY(_titleBarBottomLine.bottom, _titleBarView.bottom, 1, 0);
LAYC(_titleBarBottomLine.height, 1.f);
// _titleBarLab
_titleBarLab = [UILabel create];
_titleBarLab.font = [UIFont boldSystemFontOfSize:17];
_titleBarLab.textColor = [UIColor whiteColor];
_titleBarLab.backgroundColor = [UIColor redColor];
_titleBarLab.textAlignment = NSTextAlignmentCenter;
[_titleBarView addSubview:_titleBarLab];
LAY(_titleBarLab.top, _titleBarView.top, 1, 20.f);
LAY(_titleBarLab.centerX, _titleBarView.centerX, 1, 0);
LAYC(_titleBarLab.width, 200.f);
LAYC(_titleBarLab.height, 44.f);
// _leftBtn
_leftBtnView = [[RedPointView alloc] initWithFrame:CGRectMake(10.f, 20.f, 44.f, 44.f)];
_leftBtnView.backgroundColor = [UIColor clearColor];
[_titleBarView addSubview:_leftBtnView];
[_leftBtnView refreshImageName:@"pubCommon_back" target:self action:@selector(leftBtnViewClick:)];
LAY(_leftBtnView.left, _titleBarView.left, 1, 15.f);
LAY(_leftBtnView.centerY, _titleBarLab.centerY, 1, 0);
// _rightBtn
_rightBtnView = [[RedPointView alloc] initWithFrame:CGRectMake(self.view.frame.size.width-10.f-44.f, 20.f, 44.f, 44.f)];
_rightBtnView.backgroundColor = [UIColor clearColor];
[_rightBtnView refreshImageName:@"pubCommon_more" target:self action:@selector(rightBtnViewClick:)];
[_titleBarView addSubview:_rightBtnView];
LAY(_rightBtnView.right, _titleBarView.right, 1, -15.f);
LAY(_rightBtnView.centerY, _titleBarLab.centerY, 1, 0);
}
- (void)loadCommonContentView
{
_contentView = [[UIView alloc] initWithFrame:CGRectMake(0, 64.f, self.view.frame.size.width, self.view.frame.size.height-64.f)];
_contentView.backgroundColor = [UIColor greenColor];
[self.view addSubview:_contentView];
}
#pragma mark ---- event response
- (void)leftBtnViewClick:(UITapGestureRecognizer *)gesture
{
NSLog(@"-----leftBtnViewClick----");
[self.navigationController popToRootViewControllerAnimated:YES];
}
- (void)rightBtnViewClick:(UITapGestureRecognizer *)gesture
{
NSLog(@"-----rightBtnViewClick----");
}
#pragma mark ---- 提供给外部的功能方法
#pragma mark - 修改导航栏控件样式
/*
注意:一般来说,当我们想改变一个视图的某属性时,首先想到的方案应该是在视图的层级关系中找出该视图,然后改变其属性就行。
但是此情况却有所不同。当为文本时视图为UILabel,当为图片时视图为UIImageView。
因此在方法内部首先要清除原有视图,然后根据是显示文本或图片,而创建添加UILabel或者UIImageView。
*/
// 修改导航栏左右按钮为文本形式
- (void)changeTitleBarButtonText:(NSString *)text buttonType:(NSInteger)btnType
{
UIView *bgView = (btnType==-1 ? _leftBtnView:_rightBtnView);
UIImageView *imgView = [bgView viewWithTag:11];
[imgView removeFromSuperview];
imgView = nil;
if(text){
UILabel *lab = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, bgView.frame.size.width, bgView.frame.size.height)];
lab.text = text;
lab.textColor = [UIColor whiteColor];
lab.font = [UIFont boldSystemFontOfSize:15];
lab.backgroundColor = [UIColor clearColor];
lab.tag = 11;
lab.userInteractionEnabled = YES;
lab.contentMode = UIViewContentModeCenter;
lab.textAlignment = NSTextAlignmentCenter;
[bgView addSubview:lab];
}
}
// 修改导航栏左右按钮为图标形式
- (void)changeTitleBarButtonImage:(NSString *)imgName buttonType:(NSInteger)btnType
{
UIView *bgView = (btnType==-1 ? _leftBtnView:_rightBtnView);
UIImageView *imgView = [bgView viewWithTag:11];
[imgView removeFromSuperview];
imgView = nil;
if(imgName){
UIImageView *imgView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, bgView.frame.size.width, bgView.frame.size.height)];
imgView.image = [UIImage imageNamed:imgName];
imgView.backgroundColor = [UIColor clearColor];
imgView.tag = 11;
imgView.userInteractionEnabled = YES;
imgView.contentMode = UIViewContentModeCenter;
[bgView addSubview:imgView];
}
}
#pragma mark - 加载动画的显示、隐藏
// 显示、出现tipView
- (void)showTipsView:(TipType)tipType
{
if(!_tipView){
_tipView = [[TipView alloc] init];
}
[_tipView addTipViewTarget:self tipType:TipType_loadingFull];
}
// 隐藏、消失tipView
- (void)hideTipsView
{
if(_tipView)
{
[_tipView removeTipView];
}
}
#pragma mark - 发送网络请求同时显示加载动画
- (void)startNetworkRequestAction:(SEL)action tipType:(TipType)tipType
{
[self startNetworkRequestAction:action tipType:tipType withObject:nil];
}
- (void)startNetworkRequestAction:(SEL)action tipType:(TipType)tipType withObject:(id)object
{
[self showTipsView:tipType]; // 显示loading动画。
if(!action){
return;
}
[self performSelector:action withObject:object afterDelay:0];
}
////网络请求失败 点击重试
//-(void)failureClickedRetry
//{
//
//}
#pragma mark ---- 内部功能方法
- (void)fullScreenSlide
{
// 代码略。
}
// 修改状态栏颜色
- (UIStatusBarStyle)preferredStatusBarStyle
{
return UIStatusBarStyleLightContent;
}
#pragma mark ---- setter、getter
- (void)setIsListensKeyboard:(BOOL)isListensKeyboard
{
if(isListensKeyboard){
AppDelegate *appDelegate = (AppDelegate *)[UIApplication sharedApplication].delegate;
appDelegate.ywKeyboardDelegate = self;
}
}
@end
下面是加载动画视图的代码:
** TipView.h **
#import <UIKit/UIKit.h>
typedef NS_ENUM(NSInteger, TipType)
{
TipType_LoadingCenter = 0, // 只在屏幕中心的加载动画
TipType_loadingFull, // 覆盖整个屏幕的加载动画
};
@interface TipView : UIView
@property (nonatomic, assign)CGRect tipFrame;
@property (nonatomic, assign)CGSize tipImageSize;
@property (nonatomic, strong)UIColor *tipBgColor;
// 添加、创建tipView
- (void)addTipViewTarget:(id)target tipType:(TipType)type;
// 移除tipView
- (void)removeTipView;
@end
** TipView.m **
#import "TipView.h"
#import "CommonViewController.h"
@interface TipView ()
{
UIView *_bgView;
}
@end
@implementation TipView
#pragma mark ---- 公共方法:提供给外部的功能方法
// 添加bgView到window上。
- (void)addTipViewTarget:(id)target tipType:(TipType)type
{
if(_bgView){
[_bgView removeFromSuperview];
_bgView = nil;
}
// 确定frame
if([target isKindOfClass:[CommonViewController class]]){
CommonViewController *vc = (CommonViewController *)target;
_tipFrame = vc.contentView.frame;
}
_bgView = [[UIView alloc] initWithFrame:_tipFrame];
if(_tipBgColor){
_bgView.backgroundColor = _tipBgColor;
}else{
_bgView.backgroundColor = [UIColor whiteColor]; // 默认背景是白色
}
switch (type)
{
case TipType_loadingFull:
{
_bgView = [self createLoadingFullView]; // 创建bgView,即加载动画的核心视图
UIWindow *window = [UIApplication sharedApplication].keyWindow;
if (!window)
{
window = [[UIApplication sharedApplication].windows lastObject];
}
[window addSubview:_bgView];
}
break;
case TipType_LoadingCenter:
{
// 没写,略。
}
break;
}
}
// 移除tipView
- (void)removeTipView
{
if(_bgView)
{
[_bgView removeFromSuperview];
_bgView = nil;
}
}
#pragma mark ---- 内部功能方法
// 创建加载动画核心视图
- (UIView *)createLoadingFullView
{
UIImage *image = [UIImage imageNamed:@"Network_loading1"];
if(_tipImageSize.width == 0){
_tipImageSize = CGSizeMake(image.size.width/2.f, image.size.height/2.f);
}
UIImageView *animationView = [[UIImageView alloc] initWithFrame:CGRectMake((_bgView.frame.size.width-_tipImageSize.width)/2.f, (_bgView.frame.size.height-_tipImageSize.height)/2.f, _tipImageSize.width, _tipImageSize.height)];
animationView.image = image;
animationView.backgroundColor = [UIColor whiteColor];
animationView.animationImages = [NSArray arrayWithObjects:
image,
[UIImage imageNamed:@"Network_loading2.png"],
[UIImage imageNamed:@"Network_loading3.png"],
[UIImage imageNamed:@"Network_loading4.png"],
[UIImage imageNamed:@"Network_loading5.png"],
[UIImage imageNamed:@"Network_loading6.png"],
[UIImage imageNamed:@"Network_loading7.png"],
[UIImage imageNamed:@"Network_loading8.png"],
[UIImage imageNamed:@"Network_loading9.png"],
[UIImage imageNamed:@"Network_loading10.png"],
[UIImage imageNamed:@"Network_loading11.png"],
[UIImage imageNamed:@"Network_loading12.png"],
[UIImage imageNamed:@"Network_loading13.png"],
[UIImage imageNamed:@"Network_loading14.png"],
[UIImage imageNamed:@"Network_loading15.png"],
[UIImage imageNamed:@"Network_loading16.png"],
[UIImage imageNamed:@"Network_loading17.png"],
[UIImage imageNamed:@"Network_loading18.png"],
nil];
[animationView setAnimationDuration:1.0f];
[animationView setAnimationRepeatCount:-1];
[animationView startAnimating];
[_bgView addSubview:animationView];
return _bgView;
}
@end
最后是HomeViewController中的代码:
** HomeViewController.h **
#import "CommonViewController.h"
@interface HomeViewController : CommonViewController
@end
** HomeViewController.m **
#import "HomeViewController.h"
#import "AppDelegate.h"
@interface HomeViewController ()
@end
@implementation HomeViewController
- (void)viewDidLoad {
[super viewDidLoad];
[self loadTitleView];
[self loadContentView];
self.isListensKeyboard = YES; // 已经重写了isListensKeyboard的setter方法,会在setter方法里帮你设置键盘通知处理的代理,只需实现其代理方法即可。
// [self showTipsView:TipType_loadingFull];
[self startNetworkRequestAction:@selector(requestStudentList) tipType:TipType_loadingFull]; //发送网络请求,同时出现加载动画。
// 模拟网络回调完成,3秒后隐藏加载动画。
dispatch_time_t time=dispatch_time(DISPATCH_TIME_NOW, 3ull *NSEC_PER_SEC);
dispatch_after(time, dispatch_get_main_queue(), ^{
//执行操作
NSLog(@"----假装后台已做回应,数据已经请求到。所以隐藏加载视图----");
[self hideTipsView];
});
}
#pragma mark ---- init views
- (void)loadTitleView
{
self.titleBarView.backgroundColor = [UIColor blackColor];
self.titleBarLab.text = @"老王的Demo";
[self changeTitleBarButtonText:@"其他" buttonType:1];
[self changeTitleBarButtonImage:@"PubCommon_more" buttonType:-1];
}
- (void)loadContentView
{
UITextField *textfield = [[UITextField alloc] initWithFrame:CGRectMake(20, 400, self.contentView.frame.size.width-40.f, 40.f)];
textfield.borderStyle = UITextBorderStyleRoundedRect;
textfield.placeholder = @"请输入姓名";
[self.contentView addSubview:textfield];
}
#pragma mark ---- event response
// 重写基类中的点击事件,进行点击事件处理定制。
- (void)leftBtnViewClick:(UITapGestureRecognizer *)gesture
{
UIAlertView *alertV = [[UIAlertView alloc] initWithTitle:nil message:@"老王子点击了导航栏左按钮" delegate:self cancelButtonTitle:@"取消" otherButtonTitles:nil];
[alertV show];
}
- (void)rightBtnViewClick:(UITapGestureRecognizer *)gesture
{
UIAlertView *alertV = [[UIAlertView alloc] initWithTitle:nil message:@"老王子点击了导航栏右按钮" delegate:self cancelButtonTitle:@"取消" otherButtonTitles:nil];
[alertV show];
}
#pragma mark ---- network request
- (void)requestStudentList
{
NSLog(@"----这里写请求学生列表接口的方法----");
}
#pragma mark ---- ywkeyboardDelegate
- (void)ywKeyboardChangeStatus:(KeyBoardChangeType)changeType beginFrame:(CGRect)beginFrame endFrame:(CGRect)endFrame duration:(CGFloat)duration keyboardHeight:(CGFloat)kbHeight userInfo:(NSDictionary *)info
{
if(changeType == KeyBoardWillShow){
[UIView animateWithDuration:duration animations:^{
self.contentView.transform = CGAffineTransformMakeTranslation(0, -kbHeight);
}];
}
else if(changeType == KeyBoardWillHide){
[UIView animateWithDuration:duration animations:^{
self.contentView.transform = CGAffineTransformIdentity;
}];
}
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
[self.contentView endEditing:YES];
}
@end
看看效果:
需要说明的是上面代码里提到的两处代码在本文中没有给出。但在我的其他笔记中已经写过了。
一处是写在AppDelegate里有关键盘通知的:
《UITextField一箩筐——输入长度限制、自定义placeholder、键盘遮挡问题》
另一处是有关自定义侧滑返回的代码:
《iOS使其支持侧滑返回》