1. mvc架构模式与视图控制器的作用
通过UITableView的学习相信大家对于iOS的MVC已经有一个大致的了解,这里简单的分析一下iOS中MVC模式的设计方式。在iOS中多数数据源视图控件(View)都有一个dataSource属性用于和控制器(Controller)交互,而数据来源我们一般会以数据模型(Model)的形式进行定义,View不直接和模型交互,而是通过Controller间接读取数据。
就拿前面的联系人应用举例,UITableView作为视图(View)并不能直接访问模型Contact,它要显示联系人信息只能通过控制器(Controller)来提供数据源方法。同样的控制器本身就拥有视图控件,可以操作视图,也就是说视图和控制器之间可以互相访问。而模型既不能访问视图也不能访问控制器。具体依赖关系如下图:
参考KCApps1
Model模型
#import<Foundation/Foundation.h>
@interface KCApps : NSObject
#pragma mark 属性
@property(nonatomic,copy)NSString *appId;
@property(nonatomic,copy)NSString *name;
@property(nonatomic,copy)NSString *icon;
@property(nonatomic,copy)NSString *author;
@property(nonatomic,copy)NSString *pay;
@property(nonatomic,copy)NSString *star;
-(KCApps*)initWithDictionary:(NSDictionary *)dic;
+(KCApps*)appsWithDictionary:(NSDictionary *)dic;
//-(KCApps*)aaa;
-(KCApps*)initWithappId:(NSString *)appId name:(NSString *)name icon:(NSString *)iconauthor:(NSString *)author pay:(NSString *)pay star:(NSString *)star;
@end
#import "KCApps.h"
@implementation KCApps
@synthesize appId;
@synthesize name;
@synthesize icon;
@synthesize author;
@synthesize pay;
@synthesize star;
-(KCApps*)initWithDictionary:(NSDictionary *)dic
{
if (self = [super init]) {
self.appId = dic[@"appId"];
self.name = dic[@"name"];
self.icon = dic[@"icon"];
self.author = dic[@"author"];
self.pay = dic[@"pay"];
self.star = dic[@"star"];
}
return self;
}
//-(KCApps *)aaa
//{
//
// // self.appId = dic[@"appId"];
// // self.name = dic[@"name"];
// // self.icon = dic[@"icon"];
// // self.author = dic[@"author"];
// // self.pay = dic[@"dic"];
// // self.star = dic[@"star"];
//
// self.appId = @"0111";
// self.name = @"name";
// self.icon = @"icon";
// self.author = @"author";
// self.pay = @"dic";
// self.star = @"star";
//
// return self;
//}
+(KCApps*)appsWithDictionary:(NSDictionary *)dic
{
return [[KCApps alloc]initWithDictionary:dic];
}
-(KCApps *)initWithappId:(NSString*)kappId name:(NSString *)kname icon:(NSString *)kicon author:(NSString*)kauthor pay:(NSString *)kpay star:(NSString *)kstar
{
self.appId = kappId;
self.name = kname;
self.icon = kicon;
self.author = kauthor;
self.pay = kpay;
self.star = kstar;
return self;
}
@end
View视图
#import <UIKit/UIKit.h>
@class KCApps;
@interface KCAppsTableViewCell :UITableViewCell
#pragma mark Apps对象
@property(nonatomic,strong)KCApps *apps;
#pragmamark 单元格高度
@property(nonatomic,assign)CGFloat height;
@end
#import"KCAppsTableViewCell.h"
#import "KCApps.h"
#define kcSpace 5
#define kcIconWidth 50
#define kcIconHeight 50
#define kcPayWidth 80
#define kcStarWidth 80
#define kcStarHeight 20
@interface KCAppsTableViewCell(){
UIImageView *_iconImgView;
UILabel *_nameView;
UILabel *_authorView;
UILabel *_payView;
UIImageView *_starImgView;
}
@end
@implementation KCAppsTableViewCell
- (void)setSelected:(BOOL)selectedanimated:(BOOL)animated {
[super setSelected:selected animated:animated];
// Configure the view for the selected state
}
-(instancetype)initWithStyle:(UITableViewCellStyle)stylereuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
[self initSubView];
}
return self;
}
//初始化视图
-(void)initSubView
{
//图标
_iconImgView = [[UIImageView alloc]init];
[self addSubview:_iconImgView];
//APP名字
_nameView = [[UILabel alloc]init];
_nameView.textColor = [UIColorpurpleColor];
_nameView.font = [UIFontsystemFontOfSize:15];
[self addSubview:_nameView];
//作者
_authorView = [[UILabel alloc]init];
_authorView.textColor = [UIColorgreenColor];
_authorView.font = [UIFontsystemFontOfSize:13];
[self addSubview:_authorView];
//付费情况
_payView = [[UILabel alloc]init];
_payView.textColor = [UIColor blueColor];
_payView.font = [UIFontsystemFontOfSize:14];
[self addSubview:_payView];
//评价星级
_starImgView = [[UIImageView alloc]init];
[self addSubview:_starImgView];
}
//设置Apps
-(void)setApps:(KCApps*)apps
{
_iconImgView.frame = CGRectMake(kcSpace,kcSpace, kcIconWidth, kcIconHeight);
_iconImgView.image = [UIImageimageNamed:apps.icon];
CGFloat nameViewX =CGRectGetMaxX(_iconImgView.frame)+kcSpace;
CGFloat nameViewY = kcSpace;
CGFloat nameViewW =self.frame.size.width-4*kcSpace-kcIconWidth-kcPayWidth;
CGFloat nameViewH =CGRectGetMidY(_iconImgView.frame);
_nameView.frame = CGRectMake(nameViewX,nameViewY, nameViewW, nameViewH);
_nameView.text = apps.name;
CGFloat authorViewX =_nameView.frame.origin.x+kcSpace;
CGFloat authorViewY =CGRectGetMaxY(_nameView.frame);
CGFloat authorViewW = nameViewW;
CGFloat authorViewH = nameViewH;
_authorView.frame = CGRectMake(authorViewX,authorViewY, authorViewW, authorViewH);
_authorView.text = apps.author;
CGFloat payViewX =self.frame.size.width-kcSpace-kcPayWidth;
CGFloat payViewH = nameViewH;
_payView.frame = CGRectMake(payViewX,kcSpace, kcPayWidth, payViewH);
_payView.text = apps.pay;
CGFloat starViewX = payViewX;
CGFloat starViewY =CGRectGetMaxY(_payView.frame);
_starImgView.frame = CGRectMake(starViewX,starViewY, kcStarWidth, kcStarHeight);
_starImgView.image = [UIImageimageNamed:apps.star];
CGFloat cellheight =MAX(CGRectGetMaxY(_authorView.frame), CGRectGetMaxY(_starImgView.frame));
_height = cellheight;
NSLog(@"名字是:%@",apps.name);
NSLog(@"id是:%@",apps.appId);
}
@end
controller 控制器
#import"KCTopShowViewController.h"
#import "KCApps.h"
#import"KCAppsTableViewCell.h"
#define kImageW 70
#define kImageH 70
#define kSpace 10
#define kChange 90
#define kImageCount 5
#define klayoutSpace 7
#define ktitleBarYCGRectGetMaxY(_label.frame)+kSpace
#define ktitleW(self.view.frame.size.width - 2*klayoutSpace)/3
#define ktitleH 50
@interface KCTopShowViewController()<UIScrollViewDelegate,UITableViewDataSource,UITableViewDelegate>
{
UIScrollView *_scrollView;
UIImageView *_sideView1;
UIImageView *_sideView2;
UIImageView *_centerView1;
UIImageView *_centerView2;
UIImageView *_centerView3;
UILabel *_label;
NSMutableArray *_imagedata;
int _currentImageIndex;
int _imageCount;
UIButton *_btn1;
UIButton *_btn2;
UIButton *_btn3;
UITableView *_tableView;
NSMutableArray *_apps;
NSMutableArray *_appsCells;
}
@end
@implementationKCTopShowViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
[self addScrollView];
[self addImageViews];
[self addLabel];
[self setDefaultImage];
[self addTitleBar];
[self initTab];
[self initDataWithPlist:@"Apps1.plist"];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
/*
#pragma mark - Navigation
// In a storyboard-basedapplication, you will often want to do a little preparation before navigation
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
// Get the new view controller using [segue destinationViewController].
// Pass the selected object to the new view controller.
}
*/
#pragma mark 添加scrollView
-(void)addScrollView
{
//设置_scrollView所显示的内容大小,这里是3个图片和4个空隙
CGFloat layoutW = kSpace*4+kImageW*3;
CGFloat layoutX =(self.view.frame.size.width-layoutW)/2;
_scrollView = [[UIScrollViewalloc]initWithFrame:CGRectMake(layoutX, 20, layoutW, kImageH+kSpace)];
[self.view addSubview:_scrollView];
_scrollView.delegate = self;
_scrollView.tag = 100;
//设置contentSize
_scrollView.contentSize =CGSizeMake((kImageW*kImageCount+kSpace*(kImageCount+1)), kImageH+10);
//设置当前显示的是第一张图片
[_scrollViewsetContentOffset:CGPointMake(kSpace+kImageW, 0) animated:YES];
//设置分页
_scrollView.pagingEnabled = YES;
//去掉滚动条
_scrollView.showsHorizontalScrollIndicator= NO;
}
#pragmamark 添加图片控件
-(void)addImageViews
{
_sideView1 = [[UIImageViewalloc]initWithFrame:CGRectMake(kSpace, kSpace, kImageW, kImageH)];
//UIViewContentModeScaleToFill,让图片充满整个UIImageView
_sideView1.contentMode =UIViewContentModeScaleToFill;
[_scrollView addSubview:_sideView1];
_centerView1 = [[UIImageViewalloc]initWithFrame:CGRectMake(kSpace*2+kImageW, kSpace, kImageW, kImageH)];
_centerView1.contentMode =UIViewContentModeScaleToFill;
[_scrollView addSubview:_centerView1];
_centerView2 = [[UIImageViewalloc]initWithFrame:CGRectMake(kSpace*3+kImageW*2, kSpace, kImageW, kImageH)];
_centerView2.contentMode =UIViewContentModeScaleToFill;
[_scrollView addSubview:_centerView2];
_centerView3 = [[UIImageViewalloc]initWithFrame:CGRectMake(kSpace*4+kImageW*3, kSpace, kImageW, kImageH)];
_centerView3.contentMode =UIViewContentModeScaleToFill;
[_scrollView addSubview:_centerView3];
_sideView2 = [[UIImageViewalloc]initWithFrame:CGRectMake(kSpace*5+kImageW*4, kSpace, kImageW, kImageH)];
_sideView2.contentMode =UIViewContentModeScaleToFill;
[_scrollView addSubview:_sideView2];
}
#pragmamark 设置默认显示的图片
-(void)setDefaultImage
{
_sideView1.image = [UIImage imageNamed:[NSStringstringWithFormat:@"longmao%i.png",kImageCount-1]];
_centerView1.image = [UIImageimageNamed:[NSString stringWithFormat:@"longmao%i.png",0]];
_centerView2.image = [UIImageimageNamed:[NSString stringWithFormat:@"longmao%i.png",1]];
_centerView3.image = [UIImageimageNamed:[NSString stringWithFormat:@"longmao%i.png",2]];
_sideView2.image = [UIImage imageNamed:[NSStringstringWithFormat:@"longmao%i.png",3]];
//设置_currentImageIndex的初始值
_currentImageIndex = 0;
}
#pragmamark 添加label控件
-(void)addLabel
{
_label = [[UILabelalloc]initWithFrame:CGRectMake(0, kImageH+kSpace*3, self.view.frame.size.width,30)];
//文字居中显示
_label.textAlignment =NSTextAlignmentCenter;
_label.textColor = [UIColor greenColor];
[_label setFont:[UIFontsystemFontOfSize:15]];
_label.text = [NSStringstringWithFormat:@"longmao%i.png",0];
[self.view addSubview:_label];
}
#pragmamark 滚动停止事件
-(void)scrollViewDidEndDecelerating:(UIScrollView*)scrollView
{
if (scrollView.tag == 100) {
//重新加载图片
[self reloadImage];
//移到中间
[_scrollViewsetContentOffset:CGPointMake(kImageW+kSpace, 0) animated:NO];
//设置描述信息
_label.text = [NSStringstringWithFormat:@"longmao%i.png",_currentImageIndex];
NSLog(@"---%d",_currentImageIndex);
}
}
#pragmamark 重新加载图片
-(void)reloadImage
{
intsideView1Index,centerView1Index,centerView2Index,centerView3Index,sideView2Index;
//取得视图的移动距离
CGPoint offset = [_scrollViewcontentOffset];
if (offset.x > kChange) {
//向右滑动
NSLog(@">>>>>");
_currentImageIndex =(_currentImageIndex+1)%kImageCount;
}else if (offset.x < kChange){
//向左滑动
NSLog(@"<<<<<");
_currentImageIndex=(_currentImageIndex+kImageCount-1)%kImageCount;
}
//限制_currentImageIndex的变化范围
if (_currentImageIndex >= kImageCount) {
_currentImageIndex =_currentImageIndex%kImageCount;
}
//计算每个ImageView对应的Idex
sideView1Index =(_currentImageIndex+_imageCount-1)%kImageCount;
centerView1Index =(_currentImageIndex)%kImageCount;
centerView2Index =(_currentImageIndex+1)%kImageCount;
centerView3Index =(_currentImageIndex+2)%kImageCount;
sideView2Index =(_currentImageIndex+3)%kImageCount;
NSLog(@"1.--%d,2.--%d,3.--%d,4.--%d,5.--%d",sideView1Index,centerView1Index,centerView2Index,centerView3Index,sideView2Index);
//重新设置图片
_sideView1.image = [UIImageimageNamed:[NSStringstringWithFormat:@"longmao%i.png",sideView1Index]];
_centerView1.image = [UIImageimageNamed:[NSString stringWithFormat:@"longmao%i.png",centerView1Index]];
_centerView2.image = [UIImageimageNamed:[NSStringstringWithFormat:@"longmao%i.png",centerView2Index]];
_centerView3.image = [UIImageimageNamed:[NSStringstringWithFormat:@"longmao%i.png",centerView3Index]];
_sideView2.image = [UIImageimageNamed:[NSStringstringWithFormat:@"longmao%i.png",sideView2Index]];
}
#pragmamark 设置TitleBar
-(void)addTitleBar
{
_btn1 = [UIButtonbuttonWithType:UIButtonTypeCustom];
_btn1.frame = CGRectMake(0, ktitleBarY,ktitleW, ktitleH);
[_btn1 setTitle:@"btn1"forState:UIControlStateNormal];
[_btn1 setTitleColor:[UIColor blackColor]forState:UIControlStateNormal];
[_btn1 setShowsTouchWhenHighlighted:YES];
[_btn1 addTarget:selfaction:@selector(highLightButton:) forControlEvents:UIControlEventTouchUpInside];
//初始化时,第一个按钮是按下状态
_btn1.backgroundColor = [UIColorpurpleColor];
_btn1.tag = 1;
[self.view addSubview:_btn1];
_btn2 = [UIButtonbuttonWithType:UIButtonTypeCustom];
_btn2.frame =CGRectMake(ktitleW+klayoutSpace, ktitleBarY, ktitleW, ktitleH);
[_btn2 setTitle:@"btn2"forState:UIControlStateNormal];
[_btn2 setTitleColor:[UIColor blackColor]forState:UIControlStateNormal];
[_btn2 setShowsTouchWhenHighlighted:YES];
[_btn2 addTarget:selfaction:@selector(highLightButton:)forControlEvents:UIControlEventTouchUpInside];
_btn2.backgroundColor = [UIColor redColor];
_btn2.tag = 2;
[self.view addSubview:_btn2];
_btn3 = [UIButton buttonWithType:UIButtonTypeCustom];
_btn3.frame =CGRectMake(2*(ktitleW+klayoutSpace), ktitleBarY, ktitleW, ktitleH);
[_btn3 setTitle:@"btn3"forState:UIControlStateNormal];
[_btn3 setTitleColor:[UIColor blackColor]forState:UIControlStateNormal];
[_btn3 setShowsTouchWhenHighlighted:YES];
[_btn3 addTarget:selfaction:@selector(highLightButton:)forControlEvents:UIControlEventTouchUpInside];
_btn3.backgroundColor = [UIColor redColor];
_btn3.tag = 3;
[self.view addSubview:_btn3];
}
#pragmamark 添加按钮的动作
-(void)highLightButton:(UIButton*)button
{
switch (button.tag) {
case 1:
_btn1.backgroundColor = [UIColorpurpleColor];
_btn2.backgroundColor = [UIColorredColor];
_btn3.backgroundColor = [UIColorredColor];
[selfinitDataWithPlist:@"Apps1.plist"];
[_tableView reloadData];
break;
case 2:
_btn1.backgroundColor = [UIColorredColor];
_btn2.backgroundColor = [UIColorpurpleColor];
_btn3.backgroundColor = [UIColorredColor];
[selfinitDataWithPlist:@"Apps2.plist"];
[_tableView reloadData];
break;
case 3:
_btn1.backgroundColor = [UIColorredColor];
_btn2.backgroundColor = [UIColorredColor];
_btn3.backgroundColor = [UIColorpurpleColor];
[selfinitDataWithPlist:@"Apps3.plist"];
[_tableView reloadData];
break;
default:
break;
}
}
#pragmamark 初始化tableview
-(void)initTab
{
_tableView = [[UITableViewalloc]initWithFrame:CGRectMake(0, CGRectGetMaxY(_btn1.frame)+1,self.view.frame.size.width,self.view.frame.size.height-CGRectGetMaxY(_btn1.frame)-40-1)];
_tableView.delegate = self;
_tableView.dataSource = self;
[self.view addSubview:_tableView];
}
#pragmamark 加载数据
-(void)initDataWithPlist:(NSString*)plist
{
NSString *path = [[NSBundlemainBundle]pathForResource:plist ofType:nil];
//NSLog(@"path is %@",path);
NSArray *array = [NSArrayarrayWithContentsOfFile:path];
_apps = [[NSMutableArray alloc]init];
_appsCells = [[NSMutableArray alloc]init];
[array enumerateObjectsUsingBlock:^(id obj,NSUInteger idx, BOOL *stop) {
[_apps addObject:[KCAppsappsWithDictionary:obj]];
KCAppsTableViewCell *cell =[[KCAppsTableViewCell alloc]init];
NSLog(@"$$$$$$");
[_appsCells addObject:cell];
}];
}
#pragmamark 返回分组数
-(NSInteger)numberOfSectionsInTableView:(UITableView*)tableView
{
return 1;
}
#pragmamark 返回每组行数
-(NSInteger)tableView:(UITableView*)tableView numberOfRowsInSection:(NSInteger)section
{
NSLog(@"number is%lu",_apps.count);
return _apps.count;
}
#pragmamark返回每行的单元格
-(UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath*)indexPath
{
static NSString *cellIdentifier =@"UITableViewCell";
KCAppsTableViewCell *appTableViewCell =[tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (!appTableViewCell) {
appTableViewCell =[[KCAppsTableViewCell alloc]initWithStyle:UITableViewCellStyleDefaultreuseIdentifier:cellIdentifier];
}
KCApps *apps = _apps[indexPath.row];
appTableViewCell.apps = apps;
return appTableViewCell;
}
#pragmamark - 代理方法
#pragmamark 重新设置单元格高度
-(CGFloat)tableView:(UITableView*)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
KCAppsTableViewCell *cell = _appsCells[indexPath.row];
cell.apps = _apps[indexPath.row];
return cell.height;
}
#pragmamark 重写状态样式方法
-(UIStatusBarStyle)preferredStatusBarStyle{
return UIStatusBarStyleLightContent;
}
2. UIViewController的生命周期
当一个视图控制器被创建,并在屏幕上显示的时候。代码的执行顺序
1、 alloc 创建对象,分配空间
2、init(initWithNibName) 初始化对象,初始化数据
3、loadView 从nib载入视图,通常这一步不需要去干涉。除非你没有使用xib文件创建视图
4、viewDidLoad 载入完成,可以进行自定义数据以及动态创建其他控件
5、viewWillAppear 视图将出现在屏幕之前,马上这个视图就会被展现在屏幕上了
6、viewDidAppear 视图已在屏幕上渲染完成
当一个视图被移除屏幕并且销毁的时候的执行顺序,这个顺序差不多和上面的相反
1、viewWillDisappear 视图将被从屏幕上移除之前执行
2、viewDidDisappear 视图已经被从屏幕上移除,用户看不到这个视图了
3、dealloc 视图被销毁,此处需要对你在init和viewDidLoad中创建的对象进行释放
关于viewDidUnload :在发生内存警告的时候如果本视图不是当前屏幕上正在显示的视图的话, viewDidUnload将会被执行,本视图的所有子视图将被销毁,以释放内存,此时开发者需要手动对viewLoad、viewDidLoad中创建的对象释放内存。因为当这个视图再次显示在屏幕上的时候,viewLoad、viewDidLoad 再次被调用,以便再次构造视图。
当我们创建一个UIViewController类的对象时,通常系统会生成几个默认的方法,这些方法大多与视图的调用有关,但是在视图调用时,这些方法的调用顺序如何,需要整理下。
通常上述方法包括如下几种,这些方法都是UIViewController类的方法:
- (void)viewDidLoad;
- (void)viewDidUnload;
-(void)viewWillAppear:(BOOL)animated;
-(void)viewDidAppear:(BOOL)animated;
- (void)viewWillDisappear:(BOOL)animated;
-(void)viewDidDisappear:(BOOL)animated;
下面介绍下APP在运行时的调用顺序。
1)- (void)viewDidLoad;
一个APP在载入时会先通过调用loadView方法或者载入IB中创建的初始界面的方法,将视图载入到内存中。然后会调用viewDidLoad方法来进行进一步的设置。通常,我们对于各种初始数据的载入,初始设定等很多内容,都会在这个方法中实现,所以这个方法是一个很常用,很重要的方法。
但是要注意,这个方法只会在APP刚开始加载的时候调用一次,以后都不会再调用它了,所以只能用来做初始设置。
2) -(void)viewDidUnload;
在内存足够的情况下,软件的视图通常会一直保存在内存中,但是如果内存不够,一些没有正在显示的viewcontroller就会收到内存不够的警告,然后就会释放自己拥有的视图,以达到释放内存的目的。但是系统只会释放内存,并不会释放对象的所有权,所以通常我们需要在这里将不需要在内存中保留的对象释放所有权,也就是将其指针置为nil。
这个方法通常并不会在视图变换的时候被调用,而只会在系统退出或者收到内存警告的时候才会被调用。但是由于我们需要保证在收到内存警告的时候能够对其作出反应,所以这个方法通常我们都需要去实现。
另外,即使在设备上按了Home键之后,系统也不一定会调用这个方法,因为IOS4之后,系统允许将APP在后台挂起,并将其继续滞留在内存中,因此,viewcontroller并不会调用这个方法来清除内存。
3)- (void)viewWillAppear:(BOOL)animated;
系统在载入所有数据后,将会在屏幕上显示视图,这时会先调用这个方法。通常我们会利用这个方法,对即将显示的视图做进一步的设置。例如,我们可以利用这个方法来设置设备不同方向时该如何显示。
另外一方面,当APP有多个视图时,在视图间切换时,并不会再次载入viewDidLoad方法,所以如果在调入视图时,需要对数据做更新,就只能在这个方法内实现了。所以这个方法也非常常用。
4) -(void)viewDidAppear:(BOOL)animated;
有时候,由于一些特殊的原因,我们不能在viewWillApper方法里,对视图进行更新。那么可以重写这个方法,在这里对正在显示的视图进行进一步的设置。
5) -(void)viewWillDisappear:(BOOL)animated;
在视图变换时,当前视图在即将被移除、或者被覆盖时,会调用这个方法进行一些善后的处理和设置。
由于在IOS4之后,系统允许将APP在后台挂起,所以在按了Home键之后,系统并不会调用这个方法,因为就这个APP本身而言,APP显示的view,仍是挂起时候的view,所以并不会调用这个方法。
6) -(void)viewDidDisappear:(BOOL)animated;
我们可以重写这个方法,对已经消失,或者被覆盖,或者已经隐藏了的视图做一些其他操作。
上述方法的流程图可以简单用如下表示:
运行APP —> 载入视图 —> 调用viewDidLoad方法 —> 调用viewWillAppear方法 —> 调用viewDidAppear方法 —> 正常运行
| 载入新的View
释放对象所有权 <— 调用viewDidUnload <— 收到内存警告 <— 调用viewDidDisappear <— 调用viewWillDisappear <— APP需要调用另一个view
IOS程序启动执行顺序
http://www.yifeiyang.net/iphone-developer-advanced-3-iphone-application-startup-process/
IOS 开发 loadView 和 viewDidLoad 的区别
iPhone开发必不可少的要用到这两个方法。他们都可以用来在视图载入的时候,初始化一些内容。但是他们有什么区别呢?
viewDidLoad 此方法只有当view从nib文件初始化的时候才被调用。
loadView 此方法在控制器的view为nil的时候被调用。此方法用于以编程的方式创建view的时候用到。如:
- - ( void ) loadView {
- UIView *view = [ [ UIView alloc] initWithFrame:[ UIScreen
- mainScreen] .applicationFrame] ;
- [ view setBackgroundColor:_color] ;
- self.view = view;
- [ view release] ;
- }
你在控制器中实现了loadView方法,那么你可能会在应用运行的某个时候被内存管理控制调用。如果设备内存不足的时候, view 控制器会收到didReceiveMemoryWarning的消息。默认的实现是检查当前控制器的view是否在使用。如果它的view不在当前正在使用的view hierarchy里面,且你的控制器实现了loadView方法,那么这个view将被release, loadView方法将被再次调用来创建一个新的view。
--------------------------------------------------------------------------------------------------------------------------------------------
Don't read self.viewin -loadView. Only set it, don't get it.
The self.view propertyaccessor calls -loadViewif the view isn't currently loaded. There's your infinite recursion.
The usual way to buildthe view programmatically in -loadView, as demonstrated in Apple'spre-Interface-Builder examples, is more like this:
UIView * view = [[ UIView alloc ] init ...];
...
[ view addSubview : whatever ];
[ view addSubview : whatever2 ];
...
self . view = view ;
[ view release ];
And I don't blame youfor not using IB. I've stuck with this method for all of Instapaper and findmyself much more comfortable with it than dealing with IB's complexities,interface quirks, and unexpected behind-the-scenes
一、调用顺序:
1. + (id)alloc 分配内存;
2. - (id)init 方法(包括其他-(id)init...方法),只允许调用一次,并且要与 alloc方法 写在一起,在init方法中申请的内存,要在dealloc方法中释放(或者其他地方);
3. - (void)awakeFromNib使用Xib初始化后会调用此方法,一般不会重写此方法;
4. - (void)loadView 如果使用Xib创建ViewController,就不要重写该方法。一般不会修改此方法;
5. - (void)viewDidLoad 视图加载完成之后被调用,这个方法很重要,可以在此增加一些自己定义的控件,注意此时view的frame不一定是显示时候的frame,真实的frame会在 - (void)viewDidAppear: 后。
在iOS6.0+版本中在对象的整个生命周期中只会被调用一次,
这里要注意在iOS3.0~iOS5.X版本中可能会被重复调用,当ViewController收到内存警告后,会释放View,并调用viewDidUnload,之后会重新调用viewDidLoad,所以要支持iOS6.0以前版本的童鞋要注意这里的内存管理。
6. - (void)viewWillAppear:(BOOL)animated view 将要显示的时候,可以在此加载一些图片,和一些其他占内存的资源;
7. - (void)viewDidAppear:(BOOL)animated view 已经显示的时候;
8. - (void)viewWillDisappear:(BOOL)animated view 将要隐藏的时候,可以在此将一些占用内存比较大的资源先释放掉,在 viewWillAppear: 中重新加载。如:图片/声音/视频。如果View已经隐藏而又在内存中保留这些在显示前不会被调用的资源,那么App闪退的几率会增加,尤其是ViewController比较多的时候;
9. - (void)viewDidAppear:(BOOL)animated view 已经隐藏的时候;
10. - (void)dealloc,不要手动调用此方法,当引用计数值为0的时候,系统会自动调用此方法。
二、使用 NavigationController 去 Push 切换显示的View的时候,调用的顺序:
例如 从 A 控制器 Push 显示 B 控制器,
[(A *)self.navigationControllerpushViewController:B animated:YES]
1. 加载B控制器的View(如果没有的话);
2. 调用 A 的 -(void)viewWillDisappear:(BOOL)animated;
3. 调用 B 的 -(void)viewWillAppear:(BOOL)animated;
4. 调用 A 的 -(void)viewDidDisappear:(BOOL)animated;
5. 调用 B 的 -(void)viewDidAppear:(BOOL)animated;
总结来说,ViewController 的切换是先调用 隐藏的方法,再调用显示的方法;先调用Will,再调用Did。
三、重新布局View的子View
- (void)viewWillLayoutSubviews
- (void)viewDidLayoutSubviews
看字面意思就知道这两个方法是在View对他的子View进行布局的时候会被调用,包括View 显示/隐藏/屏幕旋转 的时候都会被调用。
如果设计的应用需要支持多方向可以在这里面进行一些UI的横竖屏适配。
打印附录:
2014-05-04 11:52:03.461 TestLifecycle[3141:60b] [ViewControllerviewDidLoad]
2014-05-04 11:52:03.463 TestLifecycle[3141:60b] [ViewControllerviewWillAppear:]
2014-05-04 11:52:03.464 TestLifecycle[3141:60b] [ViewControllerviewWillLayoutSubviews]
2014-05-04 11:52:03.465 TestLifecycle[3141:60b] [ViewControllerviewDidLayoutSubviews]
2014-05-04 11:52:03.516 TestLifecycle[3141:60b] [ViewControllerviewDidAppear:]
#Push一个新的ViewController : TestViewController
2014-05-04 11:52:07.219 TestLifecycle[3141:60b] [ViewControllerviewWillLayoutSubviews]
2014-05-04 11:52:07.220 TestLifecycle[3141:60b] [ViewControllerviewDidLayoutSubviews]
2014-05-04 11:52:07.298 TestLifecycle[3141:60b] [TestViewControllerviewDidLoad]
2014-05-04 11:52:07.299 TestLifecycle[3141:60b] [ViewControllerviewWillDisappear:]
2014-05-04 11:52:07.299 TestLifecycle[3141:60b] [TestViewControllerviewWillAppear:]
2014-05-04 11:52:07.303 TestLifecycle[3141:60b] [ViewControllerviewWillLayoutSubviews]
2014-05-04 11:52:07.303 TestLifecycle[3141:60b] [ViewControllerviewDidLayoutSubviews]
2014-05-04 11:52:07.304 TestLifecycle[3141:60b] [TestViewControllerviewWillLayoutSubviews]
2014-05-04 11:52:07.304 TestLifecycle[3141:60b] [TestViewControllerviewDidLayoutSubviews]
2014-05-04 11:52:07.806 TestLifecycle[3141:60b] [ViewControllerviewDidDisappear:]
2014-05-04 11:52:07.807 TestLifecycle[3141:60b] [TestViewControllerviewDidAppear:]
2014-05-04 11:52:07.807 TestLifecycle[3141:60b] [TestViewController viewWillLayoutSubviews]
2014-05-04 11:52:07.808 TestLifecycle[3141:60b] [TestViewControllerviewDidLayoutSubviews]
#在新的ViewController 中返回
2014-05-04 11:52:10.606 TestLifecycle[3141:60b] [TestViewControllerviewWillDisappear:]
2014-05-04 11:52:10.606 TestLifecycle[3141:60b] [ViewControllerviewWillAppear:]
2014-05-04 11:52:11.108 TestLifecycle[3141:60b] [TestViewControllerviewDidDisappear:]
2014-05-04 11:52:11.109 TestLifecycle[3141:60b] [ViewControllerviewDidAppear:]
3. 设备旋转的处理方法
参考: http://www.cnblogs.com/wengzilin/p/3258479.html
加速计是整个IOS屏幕旋转的基础,依赖加速计,设备才可以判断出当前的设备方向,IOS系统共定义了以下七种设备方向:
typedef NS_ENUM(NSInteger, UIDeviceOrientation) { UIDeviceOrientationUnknown, UIDeviceOrientationPortrait, // Deviceoriented vertically, home button on the bottom UIDeviceOrientationPortraitUpsideDown, // Device oriented vertically, home button on the top UIDeviceOrientationLandscapeLeft, // Device orientedhorizontally, home button on the right UIDeviceOrientationLandscapeRight, // Device orientedhorizontally, home button on the left UIDeviceOrientationFaceUp, // Device oriented flat, faceup UIDeviceOrientationFaceDown // Device oriented flat, face down };以及如下四种界面方向:
typedef NS_ENUM(NSInteger, UIInterfaceOrientation) { UIInterfaceOrientationPortrait = UIDeviceOrientationPortrait, UIInterfaceOrientationPortraitUpsideDown = UIDeviceOrientationPortraitUpsideDown, UIInterfaceOrientationLandscapeLeft = UIDeviceOrientationLandscapeRight, UIInterfaceOrientationLandscapeRight = UIDeviceOrientationLandscapeLeft };一、UIKit处理屏幕旋转的流程
当加速计检测到方向变化的时候,会发出 UIDeviceOrientationDidChangeNotification 通知,这样任何关心方向变化的view都可以通过注册该通知,在设备方向变化的时候做出相应的响应。
UIKit的相应屏幕旋转的流程如下:
1、设备旋转的时候,UIKit接收到旋转事件。
2、UIKit通过AppDelegate通知当前程序的window。
3、Window会知会它的rootViewController,判断该view controller所支持的旋转方向,完成旋转。
4、如果存在弹出的view controller的话,系统则会根据弹出的view controller,来判断是否要进行旋转。
二、UIViewController实现屏幕旋转
在响应设备旋转时,我们可以通过UIViewController的方法实现更细粒度的控制,当view controller接收到window传来的方向变化的时候,流程如下:
1、首先判断当前viewController是否支持旋转到目标方向,如果支持的话进入流程2,否则此次旋转流程直接结束。
2、调用 willRotateToInterfaceOrientation:duration:
方法,通知view controller将要旋转到目标方向。如果该viewController是一个container view controller的话,它会继续调用其content view controller的该方法。这个时候我们也可以暂时将一些view隐藏掉,等旋转结束以后在现实出来。
3、window调整显示的view controller的bounds,由于view controller的bounds发生变化,将会触发 viewWillLayoutSubviews
方法。
这个时候self.interfaceOrientation和statusBarOrientation方向还是原来的方向。
4、接着当前view controller的 willAnimateRotationToInterfaceOrientation:duration: 方法将会被调用。系统将会把该方法中执行的所有属性变化放到动animation block中。
5、执行方向旋转的动画。
6、最后调用 didRotateFromInterfaceOrientation: 方法,通知view controller旋转动画执行完毕。这个时候我们可以将第二部隐藏的view再显示出来。
整个响应过程如下图所示:
以上就是UIKit下一个完整的屏幕旋转流程,我们只需要按照提示做出相应的处理就可以完美的支持屏幕旋转。
三、注意事项和建议
1)注意事项
当我们的view controller隐藏的时候,设备方向也可能发生变化。例如viewController A弹出一个全屏的view controller B的时候,由于A完全不可见,所以就接收不到屏幕旋转消息。这个时候如果屏幕方向发生变化,再dismiss B的时候,A的方向就会不正确。我们可以通过在view controller A的viewWillAppear中更新方向来修正这个问题。
2)屏幕旋转时的一些建议
· 在旋转过程中,暂时界面操作的响应。
· 旋转前后,尽量保持当前显示的位置不变。
· 对于view层级比较复杂的时候,为了提高效率在旋转开始前使用截图替换当前的view层级,旋转结束后再将原view层级替换回来。
· 在旋转后最好强制reloadtableview,保证在方向变化以后,新的row能够充满全屏。例如对于有些照片展示界面,竖屏只显示一列,但是横屏的时候显示列表界面,这个时候一个界面就会显示更多的元素,此时reload内容就是很有必要的。
----------------------------------不旋转原因的分割线---------------
转自http://blog.sina.com.cn/s/blog_6de189920101266h.html
UIViewController没有随着设备一起旋转的原因
对于iPhone app,UIViewController类提供了基本的视图管理模式。当设备改变方向的时候view controller的视图会自动随之旋转的。如果视图和子视图的autoresizing属性设置是对的,这时候视图又没有随着设备一起旋转,可能是以下的原因:
1.viewcontroller没有完成代理方法
-(BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation;
也要实现了shouldAutorotateToInterfaceOrientation方法,同时shouldAutorotateToInterfaceOrientation方法要返回YES来支持所有的旋转方向
2.view controller的UIView属性嵌入在UIWindow中,并非是一个附加的view controller
你可能会发现shouldAutorotateToInterfaceOrientation方法只会在view controller启动的时候被调用,不会因为设置的旋转而在被调用。这是因为view controller束缚着他们所管理的视图,view controller是用来处理时间的响应链的一部分。view controller是从UIResponder继承而来,同时他被插入到他所管理的视图和他的superview之间。因此,通常的做法是在你的app中有一个主view controller来作为响应链的一部分。通常来说会添加一个主view controller,例如UINavigationController, UITabBarController或者UIViewController到UIWindow上。
例如
[myWindowaddSubview:primaryViewController.view];
如果你添加了另外一个view controller的UIView属性到UIWindow(anotherController和主view controller在同一个等级上)
[myWindowaddSubview:anotherController.view];
anotherController将不会接受旋转事件,只有第一个view controller(primaryViewController)会接受旋转事件。
3.你添加了view controller的UIView属性到UIWindow作为subview,但是过早的release它。
UIWindow会retain视图,而不是view controller。你不能过早的release他。在UIApplicationDelegate子类中定义他为retain属性。
4.在UITabBarController或者UINavigationController中的子view controller没有对同一方向的支持。
为了确保所有的子view controller旋转正确,你的每一个view controller,每一个tab或者额navigation都要完成shouldAutorotateToInterfaceOrientation,而且必须支持对于同一方向的旋转,也就是说对于同一方向的位置要返回为YES。
5.重写-(id)init:或者 -(id)initWithNibName:(NSString *)nibName bundle:(NSBundle*)nibBundle 方法的时候没有调用super。
对于对象的初始化,在你的view controller的init或者initWithNibName方法中必须要调用super。