IOS 整理tableview的代码

来源:互联网 发布:春秋战国士阶层 知乎 编辑:程序博客网 时间:2024/05/01 12:20


在IOS应用里tableview是非常灵活的。因此,许多代码都直接的或间接的与tableview有关,包括提供数据,更新tableview,控制它的行为和选择相应的反应。在本文中,我们将展示一种技术使它代码简洁并且结构良好。


UIViewController

苹果提供UITableviewController作为tableview专用的view controller。UITableviewController实现了一些非常实用的功能,它可以帮助你避免一再的写相同的模板代码。在另一方面,UITableViewController被限制只能精确的管理一个完全充满屏幕的Tableview,在许多时候,这正是你所需要的,如果它不是,有许多方法可以解决它,正如我们接下来所说的。


UITableviewController的特性

UITableviewController第一时间就帮你加载要展示在tableview上的数据。更具体的说,它帮你切换tableview的编辑模式,对键盘的的消息做出反应。为了使这些功能工作,当你调用任何view的事件方法同时调用基类的方法是很重要的,你可以在你的子类里重写这些方法。

UITableviewController和标准的视图控制器相比有一个唯一的特点,它支持苹果实现的下拉刷新。现在,文档里说唯一的使用UIRefreshControl的方法是UITableviewController。当然也有其他的方法使它工作,但是下次苹果更新之后,它很可能就不能正常工作了。

Apple定义了所有的关于tableview标准的接口。如果你的应用遵从这些标准,这是一个很好的主意防止重复写模板代码。


UITableviewController的限制

UITableviewController中view的特性总是被设置到tableview。如果您以后决定在屏幕上除tableview以外放一些其它的东西,那就要靠你的运气了,如果你不想依靠黑客。


如果您已经用代码或xib中定义好你的界面,它可以轻易的转换成标准的视图控制器。如果你使用的是storyboard,那么这个过程包含几个步骤。用storyboard,你不能把UITableviewController转换成标准的视图控件除非你重新做一遍。这就意味着你必须把所有的内容拷贝到新的view controller,把每个都重新写一遍


最终,你需要重新添加在这个过度中失去的功能。大多数是在viewWillAppear或viewDidApper中的单行语句。

之前你虽然走过这条路,这里有一个不错的选择。


Child View Controllers

不用完全的摆脱视图控制器,你也可以把一个子试图控制器添加到另一个上面。这样UITableviewController只需要管理tableview,他的父视图控制器管理你需要添加的其他元素。

- (void)addPhotoDetailsTableView

{

    DetailsViewController *details = [[DetailsViewController alloc] init];

    details.photo = self.photo;

    details.delegate = self;

    [self addChildViewController:details];

    CGRect frame = self.view.bounds;

    frame.origin.y = 110;

    details.view.frame = frame;

    [self.view addSubview:details.view];    

    [details didMoveToParentViewController:self];

}

如果你使用这种方法你必须创建一个子视图控制器和父视图控制器之间的联系。例如,用户选择了tableview中的一个cell,它的父视图控制器需要知道这些以便做出响应。基于不同的使用情况,通常最简单的方法是为这个试图控制器做一个委托,因此你可以在他的父视图控制器里这样做:

@protocolDetailsViewControllerDelegate

- (void)didSelectPhotoAttributeWithKey:(NSString *)key;

@end


@interfacePhotoViewController () <DetailsViewControllerDelegate>

@end


@implementationPhotoViewController

// ...

- (void)didSelectPhotoAttributeWithKey:(NSString *)key

{

    DetailViewController *controller = [[DetailViewController alloc] init];

    controller.key = key;

    [self.navigationController pushViewController:controller animated:YES];

}

@end


分离关注点

当我们处理tableview时有多种不同的任务涉及到数据模型、视图控制器、视图。为了防止视图控制器变成做所有任务的地方,我们试图隔离不同的任务到不同的地方。这帮助提高了可读性,可维护性和单独测试的能力。

这项技术在这儿描述伸展,在上一篇文章中展示概念。请参考这篇文章如何处理数据和模型的逻辑。在tableview的背景下,我们将具体的关注如何分离试图控制器和试图之间的关注点。

模型和cell之间的桥接模式

一些时候我们必须把我们想要展示的数据交给view层。由于我们仍然要保持模型和视图之间的明确分离,我们经常卸载这个tableview的数据源:

- (UITableViewCell *)tableView:(UITableView *)tableView 

         cellForRowAtIndexPath:(NSIndexPath *)indexPath

{

    PhotoCell *cell = [tableView dequeueReusableCellWithIdentifier:@"PhotoCell"];

    Photo *photo = [self itemAtIndexPath:indexPath];

    cell.photoTitleLabel.text = photo.name;

    NSString* date = [self.dateFormatter stringFromDate:photo.creationDate];

    cell.photoDateLabel.text = date;

}

这种类型的代码使得数据源和cell之间变得混乱,我们最好把它分解到cell的类别中:

@implementationPhotoCell (ConfigureForPhoto)


- (void)configureForPhoto:(Photo *)photo

{

    self.photoTitleLabel.text = photo.name;

    NSString* date = [self.dateFormatter stringFromDate:photo.creationDate];

    self.photoDateLabel.text = date;

}


@end

在这个地方,我们的数据源方法变得非常的简单:

- (UITableViewCell *)tableView:(UITableView *)tableView

         cellForRowAtIndexPath:(NSIndexPath *)indexPath

{

    PhotoCell *cell = [tableView dequeueReusableCellWithIdentifier:PhotoCellIdentifier];

    [cell configureForPhoto:[self itemAtIndexPath:indexPath]];

    return cell;

}

在我们的示例代码中,tableview的数据源被分解到我们自己的类中,他获取一个cell的配置block,因此,这个block就变得如此的简单:

TableViewCellConfigureBlock block = ^(PhotoCell *cell, Photo *photo) {

    [cell configureForPhoto:photo];

};


使cell变得可以复用

如果我们有多重的数据类型我们可以用一种cell类型去呈现它,我们可以更进一步的获取cell的可重用性。首先,我们为这个cell定一个协议,为了用它去展示必须遵从这个协议。然后我们只需要简单的配置cell的类别方法。这个简单的步骤使得数据和适应不同的数据类型去耦。

处理cell

如果我们想要做的事情超越tableview的标准或者选择行为,我们需要执行两个代理方法,我们想要改变点击cell的方法。

- (void)tableView:(UITableView *)tableView

        didHighlightRowAtIndexPath:(NSIndexPath *)indexPath

{

    PhotoCell *cell = [tableView cellForRowAtIndexPath:indexPath];

    cell.photoTitleLabel.shadowColor = [UIColor darkGrayColor];

    cell.photoTitleLabel.shadowOffset = CGSizeMake(3,3);

}


- (void)tableView:(UITableView *)tableView

        didUnhighlightRowAtIndexPath:(NSIndexPath *)indexPath

{

    PhotoCell *cell = [tableView cellForRowAtIndexPath:indexPath];

    cell.photoTitleLabel.shadowColor =nil;

}

然而,这里两个委托方法的实现又依赖cell具体是如何实现的,如果我们想换一种方式去把它从cell总分离出来,我们必须有适当的委托代码。这个view具体的执行基于代理。另一方面,我们可以把它的逻辑移动cell本身。

@implementationPhotoCell

// ...

- (void)setHighlighted:(BOOL)highlighted animated:(BOOL)animated

{

    [super setHighlighted:highlighted animated:animated];

    if (highlighted) {

        self.photoTitleLabel.shadowColor = [UIColor darkGrayColor];

        self.photoTitleLabel.shadowOffset = CGSizeMake(3,3);

    } else {

        self.photoTitleLabel.shadowColor =nil;

    }

}

@end

一般来说,我们努力的把具体的实现从试图控制器中分离出来。一个代理必须知道视图的不同状态,但它不需要知道如何修改视图树或子视图的设置,这一切逻辑应该在视图中封装,这样可以对外提供一个简单的API;


处理不同的cell类型


如果在一个tableview中有不同的cell类型,数据源可以很快的失控。在我们的示例项目中,在详情列表中我们有两种不同的cell类型:一种cell展示星级,一种一般的cell展示键值对。为了让代码分别处理不同的cell类型,数据源方法只是简单的分派处处理每个cell类型。

- (UITableViewCell *)tableView:(UITableView *)tableView  

         cellForRowAtIndexPath:(NSIndexPath *)indexPath

{

    NSString *key =self.keys[(NSUInteger) indexPath.row];

    id value = [self.photo valueForKey:key];

    UITableViewCell *cell;

    if ([key isEqual:PhotoRatingKey]) {

        cell = [self cellForRating:value indexPath:indexPath];

    } else {

        cell = [self detailCellForKey:key value:value];

    }

    return cell;

}


- (RatingCell *)cellForRating:(NSNumber *)rating

                    indexPath:(NSIndexPath *)indexPath

{

    // ...

}


- (UITableViewCell *)detailCellForKey:(NSString *)key

                                value:(id)value

{

    // ...

}


编辑Table View

Table View提供简单的编辑特性,允许重排和删除cell。在这些情况下,Table view数据源接收到不同的代理方法。因此,我们经常看到逻辑域和一些代理方法去执行真正的改变数据。




































0 0