斯坦福大学iOS公开课笔记(11)-UITableView和iPad应用开发

这节课主要介绍了UITableView的使用,还讲到了针对iPad进行开发时需要适配的特性。最后还是老样子通过一个Demo来加深对知识点的理解。

UITabelView

UITableView是iOS开发中最重要的控件之一,几乎所有的页面中都会有他的身影,自然他的功能也非常强大。

UITableView是一个列表,它是一维的表格。

UITabelView Style

UITableView有两种风格的表格,一种是常规类型,另外一种是分组类型。

logo

每种类型的UITabelView都有header、footer、section、cell这四部分构成。

logo
logo

Delegate(代理) & DataSource(数据源)

UITabelView说到底也是一个View,要想完整的实现还需要Controller和Model部分的配合,所以在UITableView中有两个特殊的协议,delegate和datasource。

DataSource

系统为UITabelView提供了一些数据源方法来确定需要展示的数据。

表格中section的数量,不是必须实现的,不实现的情况下默认为1。

1
- (NSInteger)numberOfSectionsInTabelView:(UITabelView *)sender;

表格中每一个section中的行数,必须实现。

1
- (NSInteger)tableView:(UITableView *)sender numberOfRowInSection:(NSInteger)section;

每一行中显示的内容,必须实现。

1
- (UITableView *)tableView:(UITableView *)sender cellForRowAtIndexPath:(NSIndexPath *)indexPath;

Delegate

点击某一行时触发,不是必须实现。

1
- (void)tableView:(UITableView *)sender didSelectRowAtIndexPath:(NSIndexPath *)path;

除了以上介绍的之外,UITabelView还有很多代理方法,这里就不一个一个介绍了。

刷新UITabelView

刷新UITableView可以直接使用方法:

1
- (void)reloadData;

当然如果你想要对某一个或者少数几个数据进行刷新的时候也可以使用:

1
- (void)reloadRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animationStyle;

iPad开发 Universal Applications

如何让一个应用同时适配于iPhone和iPad?答案就是使用 Universal Application。他会有两个Storyboard用来适配iPad以及iPhone。

这里我们着重的分析一下iPad的部分。iPad上我们有几种不同的控制器来控制我们的应用。一种是SplitView,另外一种是Popver。

判断设备是否为iPad

判断是否为iPad。

1
BOOL iPad = ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad);

SplitView

SpiltView是将页面拆分成两个MVC,一个比较窄的在左边,一个比较大的在右边。比如设置页面。

logo

其中左边的部分叫做 Master View Controller,右边的部分叫做 Detail View Controller。master中的动作通常会决定detail中的内容显示。

注意SplitView位于整个界面的顶部,所以千万不要把SplitView放到导航栏或tabbar中。

返回当前ViewController所在的SplitViewController,如果没有会返回nil。

1
@property (strong) UISPlitViewController *splitViewController;

获取SplitViewController中的master和detail。有一个数组,下标为0的是master,下标为1的是detail。

1
@property (copy) NSArray *viewControllers;

UISplitViewControllerDelegate,它用来获取竖屏模式。要在awakeFromNib时就要设定。负责控制合适显示master和detail。

1
2
3
4
5
6
- (BOOL)splitViewController:(UISplitViewController *)sender shouldHideViewController:(UIVIewController *)master inOrientation:(UIInterfaceOrientation)orientation
{
return NO; //永远不隐藏maser
return UIInterfaceOrientationIsPostrait(orientation); //只有竖屏的时候隐藏master
}

Popver

PopoverController不是一个UIViewController,而是一个NSObject。他的作用是控制另一个视图控制器,弹出到屏幕上。

logo

1
@property (nonatomic, strong) UIViewController *contentViewController;

通过设定segue控制弹出PopoverViewController。这里segue的类型是UIStroyboardPopoverSegue

1
2
3
4
5
6
7
- (void)prepareForSegue: (UIStoryboardSegue *)segue sender: (id)sender
{
if([segue isKindOfClass:[UIStroyboardPopoverSegue class]])
{
UIPopoverController *popoverController = ((UIStroyboardPopoverSegue *)segue.)popoverController;
}
}

只要我们点击其他部分,就可以让PopoverView消失。

方法是

1
- (void)dismissPopoverAnimated:(BOOL)animated;

Demo

这个Demo主要是展示了从Flickr的Api获取图片数据,并且展示在UITableView上。这里调用Flickr的Api会有一点麻烦,之后我会把写好的代码传到git仓库中,如果想要使用的话直接下载下来将Flickr Fetcher文件夹拖入自己的工程并修改一下API Key 就可以了。

下边实现一下我们的这个Demo。

首先在storyboard中创建一个UITabelViewController,然后在写一个方法用来获取Flickr中的数据。

1
2
3
4
5
6
7
8
9
10
11
12
13
- (void)fetchPhotots
{
NSURL *url = [FlickrFetcher URLforRecentGeoreferencedPhotos];
NSData *jsonResult = [NSData dataWithContentsOfURL:url];
NSDictionary *propertyListResults = [NSJSONSerialization JSONObjectWithData:jsonResult
options:0
error:NULL];
NSArray *photos = [propertyListResults valueForKeyPath:FLICKR_RESULTS_PHOTOS];
self.photos = photos;
}

这里还说了一下nil和NULL的区别。

1
2
//nil 是指针指向的内容为空。
//null 是指 指针什么都没有指向。

但是按照上边的做法,下载照片内容的时候会堵塞线程,所以我们要另外开辟一个线程来进行下载数据的操作。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
- (void)fetchPhotots
{
NSURL *url = [FlickrFetcher URLforRecentGeoreferencedPhotos];
dispatch_queue_t fetchQ = dispatch_queue_create("flickr fecher", NULL);
dispatch_async(fetchQ, ^{
NSData *jsonResult = [NSData dataWithContentsOfURL:url];
NSDictionary *propertyListResults = [NSJSONSerialization JSONObjectWithData:jsonResult
options:0
error:NULL];
//nil 是指针指向的内容为空。
//null 是指 指针什么都没有指向。
NSArray *photos = [propertyListResults valueForKeyPath:FLICKR_RESULTS_PHOTOS];
dispatch_async(dispatch_get_main_queue(), ^{
self.photos = photos;
});
});
}

OK,接下来我们来设置我们的界面,通过设置UITableView的数据源,就可以将Flickr中的内容展示到屏幕上。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [self.photos count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellId = @"Flickr Photo Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellId forIndexPath:indexPath];
NSDictionary *photo = self.photos[indexPath.row];
cell.textLabel.text = [photo valueForKeyPath:FLICKR_PHOTO_TITLE];
cell.detailTextLabel.text = [photo valueForKeyPath:FLICKR_PHOTO_DESCRIPTION];
return cell;
}

完成了列表的数据部分,接下来我们只需要把图片下载下来就可以了。这里使用到上一次课中的图片显示器Imaginarium,当我们点击cell的时候跳转到上节课中的显示器并显示出来,就完成了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#pragma mark - Navigation
- (void)prepareImageViewController:(ImageViewController *)ivc toDisplayPhoto:(NSDictionary *)photo
{
ivc.imageURL = [FlickrFetcher URLforPhoto:photo format:FlickrPhotoFormatLarge];
ivc.title = [photo valueForKeyPath:FLICKR_PHOTO_TITLE];
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if([sender isKindOfClass:[UITableViewCell class]])
{
NSIndexPath *indexPath = [self.tableView indexPathForCell:sender];
if(indexPath)
{
if([segue.identifier isEqualToString:@"Display Photo"])
{
if([segue.destinationViewController isKindOfClass:[ImageViewController class]])
{
[self prepareImageViewController:segue.destinationViewController toDisplayPhoto:self.photos[indexPath.row]];
}
}
}
}
}
logo