IOS从android角度来实现理解IOS的UITableView.docx
《IOS从android角度来实现理解IOS的UITableView.docx》由会员分享,可在线阅读,更多相关《IOS从android角度来实现理解IOS的UITableView.docx(22页珍藏版)》请在冰豆网上搜索。
IOS从android角度来实现理解IOS的UITableView
本人从在学校开始到现在上班(13年毕业)一直做web和android方面的开发,最近才开学习及ios的开发,所以ios学习中有不当之处,请大家留言赐教啦
以前从来没有接触过Objective-C这门语言,不过我想面向对象编程应该大体思想都差不多
在ios中的UITableView学习中,开发过android的朋友应该马上会联想到ListView和GridView这两个控件,接下来以ListView为例子,跟UITableView做个对比,看看它们实现的方式有什么相同之处。
怎么样能让有android开发经验的朋友,马上掌握UITableView这个控件
先新建一个demo,取名TabViewTest(原谅我吧--,本来要取名TableViewTest,谁知脑抽新建项目的时候写错了,诶。
。
。
算了,将错就错吧--)
ios没有命名空间的概念,没有包概念(这也是为什么ios中的类都有前缀的原因,比如NS等),所以上面像“包”一样的文件夹都是我自己新建的“group”,只是为了看起来比较有分层的概念而已,打开finder,到项目文件里一看如下图,妈呀--,所有的类都挤在一个文件夹里面。
。
。
这是我觉得蛋疼的地方之一-。
-
再回来看看我们项目结构,我分的几个group,如果我把controller这个group的名字改成“activity”,android开发者肯定有种似曾相识的感觉了:
controller:
控制层group,相当于android中的activity
layout:
布局group,相当于android中res目录下的layout(xml布局文件)
model:
这个不用说就知道放了什么东西了,经典的Person这个测试用的数据结构
adapter:
为了还念android中的适配器,然后我就取了这么个group名字
好了,现在正式开始代码的编写
打开MainAppDelegate.m,它实现了UIApplicationDelegate协议,所以可以在该类中实现应用状态的回调函数
在application:
didFinishLaunchingWithOptions:
方法(应用程序启动时回调该方法)中来设置它的RootController(根控制器,不知道这样翻译专不专业--),我的实现是生成一个UINavigationController作为它的rootcontroller,然后把自己新建的一个NaviRootController(在这个Controller中放入一个UITableView,具体下面会讲到)作为UINavigationController的rootview,具体代码如下(这个不是我们本次的重点,所以一笔带过):
1-(BOOL)application:
(UIApplication*)applicationdidFinishLaunchingWithOptions:
(NSDictionary*)launchOptions
2{
3self.window=[[UIWindowalloc]initWithFrame:
[[UIScreenmainScreen]bounds]];
4
5//生成UINavigationController对象,并设置它的RootController
6UINavigationController*naviController=[[UINavigationControlleralloc]initWithRootViewController:
[[NaviRootControlleralloc]init]];
7//然后把NavigationController设置为window的RootViewController
8[self.windowsetRootViewController:
naviController];
9
10self.window.backgroundColor=[UIColorwhiteColor];
11[self.windowmakeKeyAndVisible];
12returnYES;
13}
然后,重点就是NaviRootController这个Controller了,
新建NaviRootController,继承UIViewController,在.h文件中:
声明一个NSMutableArray对象data作为tableView的数据源(相当于在android中经常用到的ArrayListdata。
NSMutableArray是数组,ArrayList在java中的底层实现本来用的就是数组,所以很好理解)
声明一个用于适配和绑定tableView数据的适配器TableViewAdapter*adapter(这个类是我自己写的,下面会讲到。
java中要实现ListView中的数据绑定适配,也要通过一个继承于BaseAdapter的适配器进行数据的适配吧,也很好理解)
声明一个UITableView对象(相当于android中,在activity中声明一个privateListViewlistView;)
具体代码如下:
1//
2//NaviRootController.h
3//TabViewTest
4//
5//CreatedbyWANGJIEon13-10-31.
6//Copyright(c)2013年WANGJIE.Allrightsreserved.
7//
8
9#import
10@classTableViewAdapter;
11@interfaceNaviRootController:
UIViewController
12{
13NSMutableArray*data;
14TableViewAdapter*adapter;
15}
16
17@property(weak,nonatomic)IBOutletUITableView*tableView;
18
19
20@propertyNSMutableArray*data;
21@property(strong,nonatomic)TableViewAdapter*adapter;
22@end
ViewCode
好了,声明部分到此为止,接下来看看实现部分
刚刚在MainAppDelegate中,生成了一个NaviRootController对象,然后把这个对象加入到了UINavigationController对象的rootViewController。
生成NaviRootController对象的时候,调用了init方法,我们应该在init方法里面去初始化布局,如下:
1-(id)init
2{
3self=[selfinitWithNibName:
@"navi_root"bundle:
nil];
4returnself;
5}
6
7-(id)initWithNibName:
(NSString*)nibNameOrNilbundle:
(NSBundle*)nibBundleOrNil
8{
9self=[superinitWithNibName:
nibNameOrNilbundle:
nibBundleOrNil];
10if(self){
11//获得当前导航栏对象
12UINavigationItem*item=[selfnavigationItem];
13//设置导航栏左按钮
14UIBarButtonItem*leftButton=[[UIBarButtonItemalloc]initWithTitle:
@"leftButton"style:
UIBarButtonItemStylePlaintarget:
selfaction:
@selector(buttonClickedAction:
)];
15[leftButtonsetTag:
0];
16[itemsetLeftBarButtonItem:
leftButtonanimated:
YES];
17//设置导航栏右按钮
18UIBarButtonItem*rightButton=[[UIBarButtonItemalloc]initWithTitle:
@"rightButton"style:
UIBarButtonItemStyleDonetarget:
selfaction:
@selector(buttonClickedAction:
)];
19[rightButtonsetTag:
1];
20[itemsetRightBarButtonItem:
rightButtonanimated:
YES];
21
22}
23returnself;
24}
ViewCode
我们在init方法中调用了initWithNibName:
bundle:
方法,传入了NibName这个字符串,这个是什么?
Nib是ios里面的布局文件,也就是相当于android中类似main.xml这种布局文件吧,现在传进去的时nibname,也就是布局文件的名称。
所以,没错,传入这个参数后,当前的controller(也就是NaviRootController就跟传入的这个布局文件绑定起来了,类似于android中activity中熟悉的setContentView(R.layout.main)一样!
)
然后我们顺便看看navi_root.xib这个布局文件:
整个界面就一个UITableView,对了,别忘了在File'sOwner中把customclass改成NaviRootController。
键盘按住control,用鼠标拖动左边栏的“TableView”到NaviRootController.h中,会自动生成UITableView声明,并自动绑定。
接下来回到NaviRootController的初始化方法中。
initWithNibName:
bundle:
方法中后面的UINavigationItem相关的代码是用来设置导航栏左边和右边的按钮,既然是个demo,所以也没什么特别的功能,就是点击下,跳出一个UIAlertView提示下,所以一笔带过。
此时界面布局和controller已经绑定起来了,现在的任务应该是初始化UITableView的数据,也就是上面的data属性,但是在哪里初始化比较好呢?
刚开始,我是直接在init方法中直接去初始化数据,但是失败了,不管在init方法中初始化多少次(data调用多少次addObject方法),data的值永远都是nil(相当于在android中,不管调用多少次list.add(...)方法,list中一条数据也没有加入!
),猜测是因为在init方法中的时候属性和控件都还没有被初始化。
最后我的解决办法就是在viewDidLoad方法中去加载数据。
viewDidLoad这个回调方法是会在控件加载完毕后调用,所以,我认为在这里去加载数据和做控件的初始化操作是比较合理的。
viewDidLoad方法实现如下:
1-(void)viewDidLoad
2{
3
4//Doanyadditionalsetupafterloadingtheview.
5[superviewDidLoad];
6
7if(!
adapter){
8[selfinitData];
9
10//生成适配器委托对象
11adapter=[[TableViewAdapteralloc]initWithSource:
dataController:
self];
12
13//设置适配器委托对象
14[[selftableView]setDelegate:
adapter];
15[[selftableView]setDataSource:
adapter];
16
17}
18}
ViewCode
如上,在viewDidLoad中,我先去初始化数据(demo中的实现其实就是循环了14次,往data中加了14个Person对象),然后生成一个适配器委托对象,传入data(数据源)和self(当前controller对象),相当于android中的adapter=newMyAdapter(list,this);有木有?
?
!
!
然后,setDelegate用来设置委托对象(相当于android中的listView.setAdapter(adapter)),setDataSource用来设置数据源。
这里,完整地列出NaviRootController的代码:
1//
2//NaviRootController.m
3//TabViewTest
4//
5//CreatedbyWANGJIEon13-10-31.
6//Copyright(c)2013年WANGJIE.Allrightsreserved.
7//
8
9#import"NaviRootController.h"
10#import"Person.h"
11#import"TableViewAdapter.h"
12
13@interfaceNaviRootController()
14
15@end
16
17@implementationNaviRootController
18@synthesizedata,adapter;
19
20-(id)init
21{
22self=[selfinitWithNibName:
@"navi_root"bundle:
nil];
23returnself;
24}
25
26-(id)initWithNibName:
(NSString*)nibNameOrNilbundle:
(NSBundle*)nibBundleOrNil
27{
28self=[superinitWithNibName:
nibNameOrNilbundle:
nibBundleOrNil];
29if(self){
30//Custominitialization
31//获得当前导航栏对象
32UINavigationItem*item=[selfnavigationItem];
33//设置导航栏左按钮
34UIBarButtonItem*leftButton=[[UIBarButtonItemalloc]initWithTitle:
@"leftButton"style:
UIBarButtonItemStylePlaintarget:
selfaction:
@selector(buttonClickedAction:
)];
35[leftButtonsetTag:
0];
36[itemsetLeftBarButtonItem:
leftButtonanimated:
YES];
37//设置导航栏右按钮
38UIBarButtonItem*rightButton=[[UIBarButtonItemalloc]initWithTitle:
@"rightButton"style:
UIBarButtonItemStyleDonetarget:
selfaction:
@selector(buttonClickedAction:
)];
39[rightButtonsetTag:
1];
40[itemsetRightBarButtonItem:
rightButtonanimated:
YES];
41
42
43
44
45}
46returnself;
47}
48
49/**
50*初始化列表数据
51*/
52-(void)initData
53{
54data=[[NSMutableArrayalloc]init];
55NSLog(@"%@",NSStringFromSelector(_cmd));
56Person*person;
57for(inti=0;i<14;i++){
58person=[[Personalloc]init];
59[personsetName:
[@"name"stringByAppendingString:
60[NSStringstringWithFormat:
@"%d",i]
61]
62];
63
64[personsetAge:
i+10];
65
66[personsetPic:
[UIImageimageNamed:
@"Hypno.png"]];
67
68[dataaddObject:
person];
69
70}
71}
72
73
74-(void)viewDidLoad
75{
76
77//Doanyadditionalsetupafterloadingtheview.
78[superviewDidLoad];
79
80if(!
adapter){
81[selfinitData];
82
83//生成适配器委托对象
84adapter=[[TableViewAdapteralloc]initWithSource:
dataController:
self];
85
86//设置适配器委托对象
87[[selftableView]setDelegate:
adapter];
88[[selftableView]setDataSource:
adapter];
89
90}
91
92
93
94}
95
96-(void)viewWillAppear:
(BOOL)animated
97{
98[superviewWillAppear:
YES];
99
100}
101
102-(void)didReceiveMemoryWarning
103{
104[superdidReceiveMemoryWarning];
105//Disposeofanyresourcesthatcanberecreated.
106}
107
108//按钮点击事件回调
109-(void)buttonClickedAction:
(id)sender
110{
111
112NSString*message;
113switch([sendertag]){
114case0:
115message=@"leftbuttonclicked!
";
116break;
117case1:
118message=@"rightbuttonclicked!
";
119break;
120default:
121message=@"unknowbuttonclicked!
";
122break;
123}
124
125UIAlertView*alert=[[UIAlertViewalloc]initWithTitle:
@"alertview"message:
messagedelegate:
selfcancelButtonTitle:
@"cancel"otherButtonTitles:
nil,nil];
126[alertshow];
127}
128
129
130
131
132@end
ViewCode
好了,UITableView的初始化准备工作到此就做完了,现在干嘛?
当然去编写TableViewAdapter这个类啊。
数据源有了(并且初始化完毕),用以显示的控件(UITableView)有了(并且初始化完毕),而且两个还已经绑定起来了。
但是还缺的就是一个角色,这个角色可以把数据源中的数据跟控件中某个相应的子控件适配起来。
比如数据源中有4个数据,A、B、C、D,控件有one、two、three、four这4个控件,这个角色的任务就是告诉one:
你要显示的数据是A;告诉two:
你要显示的数据是B;告诉three:
你要显示的数据是C;告诉four:
你要显示的数据是D!
这个角色就是适配器!
也就是下面要说的那个TableViewAdapter
新建TableViewAdapter,实现这两个协议(android中适配器不同的是要继承BaseAdapter类)。
声明一个数据源data,这个数据源是从NaviRootController中传过来的已经初始化好了的数据源,还有一个声明是NaviRootController对象传过来的self(需要什么,就让调用者传什么过来,这个应该都懂的-。
-),另外还有一个自己写的初始化方法(自己写初始化方法init打头的方法就行,不像java中的构造方法,方法名要跟类名相同,不过这些都是换汤不换药)
然后看看TableViewAdapter的实现类(.m)
实现了这两个协议后,你就能覆写里面的一些UITableView的回调方法了,比如:
tableView:
numberOfRowsInSection:
方法,返回数据源的数量就行了(类似android的adapter中自己要实现的getCount()方法!
)
tableView:
cellForRowAtIndexPath:
这个是方法是这里最关键的一个方法了,就是在这里进行数据的适配工作(类似android的adapter中自己要实现的getView()方法!
),这里返回的UITableViewCell就是类表中的一项(类似android中listview的一个item),这个一项的布局,已经在table_cell.xib文件中布局完毕,如下:
设置File'sOwner为TableViewAdapter,设置TableViewCell的identifier设置为“MyTableCell”,这个可以任意取名,但是要跟后面的方法实现中统一(跟哪里统一