Lighter View Controllers

来源:互联网 发布:sql where 优先级 编辑:程序博客网 时间:2024/06/04 23:31

翻译了文章觉得这个网站真的挺好,就查了一下。竟然找到中文版的了。我去,白翻译了这么多。

推荐:http://www.objccn.io/


以我四级的英语水平 + 百度翻译给大家翻译篇文章,或许同事说的对,看国外的资料很重要。

原文地址:http://www.objc.io/issue-1/lighter-view-controllers.html

题目就叫《更轻量级的视图控制器》。

View controllers are often the biggest files in iOS projects, and they often contain way more code than necessary. Almost always, view controllers are the least reusable part of the code. We will look at techniques to slim down your view controllers, make code reusable, and move code to more appropriate places.

The example project for this issue is on GitHub.

视图控制器往往在iOS项目中使用最多的文件,它们通常包含更重要的代码。几乎总是,视图控制器代码可复用性是最小的。我们将看看可以让你的视图控制器代码更少的技术使代码可重用和移动到更合适的地方

这个问题是例子在GitHub上。

Separate Out Data Source and Other Protocols

One of the most powerful techniques to slim down your view controller is to take theUITableViewDataSource part of your code, and move it to its own class. If you do this more than once, you will start to see patterns and create reusable classes for this.

For example, in our example project, there is a class PhotosViewController which had the following methods:

将数据源和其他协议分割开来

缩减你的视图控制器的最强大的技术之一是把你的代码的uitableviewdatasource部分的代码移动到它自己的类。如果你这么做一次你将开始看到模式和这个创建可重用的

例如在我们的例子中有一个类photosviewcontroller遵循了下列方法

# pragma mark Pragma - (Photo*)photoAtIndexPath:(NSIndexPath*)indexPath {    return photos[(NSUInteger)indexPath.row];}- (NSInteger)tableView:(UITableView*)tableView  numberOfRowsInSection:(NSInteger)section {    return photos.count;}- (UITableViewCell*)tableView:(UITableView*)tableView         cellForRowAtIndexPath:(NSIndexPath*)indexPath {    PhotoCell* cell = [tableView dequeueReusableCellWithIdentifier:PhotoCellIdentifier                                                       forIndexPath:indexPath];    Photo* photo = [self photoAtIndexPath:indexPath];    cell.label.text = photo.name;    return cell;}

A lot of this code has to do with arrays, and some of it is specific to the photos that the view controller manages. So let’s try to move the array-related code into itsown class. We use a block for configuring the cell, but it might as well be a delegate, depending on your use-case and taste.

很多这样的代码控制数组,它的视图控制器管理一些特定的照片。让我们试着移动数组相关的代码到自己的类。我们使用block块去生成cell它也可以一个delegate根据您的使用习惯来选择

@implementation ArrayDataSource- (id)itemAtIndexPath:(NSIndexPath*)indexPath {    return items[(NSUInteger)indexPath.row];}- (NSInteger)tableView:(UITableView*)tableView  numberOfRowsInSection:(NSInteger)section {    return items.count;}- (UITableViewCell*)tableView:(UITableView*)tableView         cellForRowAtIndexPath:(NSIndexPath*)indexPath {    id cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier                                              forIndexPath:indexPath];    id item = [self itemAtIndexPath:indexPath];    configureCellBlock(cell,item);    return cell;}@end

The three methods that were in your view controller can go, and instead you can create an instance of this object and set it as the table view’s data source.

三个方法可以在你的视图控制器里实现,你可以创建对象的一个实例,并将它设置为表格视图数据源

void (^configureCell)(PhotoCell*, Photo*) = ^(PhotoCell* cell, Photo* photo) {   cell.label.text = photo.name;};photosArrayDataSource = [[ArrayDataSource alloc] initWithItems:photos                                                cellIdentifier:PhotoCellIdentifier                                            configureCellBlock:configureCell];self.tableView.dataSource = photosArrayDataSource;

Now you don’t have to worry about mapping an index path to a position in the array, and every time you want to display an array in a table view you can reuse this code. You can also implement additional methods such astableView:commitEditingStyle:forRowAtIndexPath: and share that code among all your table view controllers.

The nice thing is that we can test this class separately, and never have to worry about writing it again. The same principle applies if you use something else other than arrays.

In one of the applications we were working on this year, we made heavy use of Core Data. We created a similar class, but instead of being backed by an array, it is backed by a fetched results controller. It implements all the logic for animating the updates, doing section headers, and deletion. You can then create an instance of this object and feed it a fetch request and a block for configuring the cell, and the rest will be taken care of.

Furthermore, this approach extends to other protocols as well. One obvious candidate isUICollectionViewDataSource. This gives you tremendous flexibility; if, at some point during the development, you decide to have aUICollectionView instead of a UITableView, you hardly have to change anything in your view controller. You could even make your data source support both protocols.

现在你不必担心索引路径映射到数组中的一个位置,每次你想显示一个数组在表格视图可以重用代码。你也可以实现其他方法如tableView:commitEditingStyle:forRowAtIndexPath:和你的表视图控制器之间的代码共享

令人高兴的是,我们可以测试这个类的分开,而不必担心写一遍。如果你使用的东西,其他数组相同的原则也可以适用

一个我们今年开发的应用,我们使用 Core Data我们创建了一个类似的类,而不是有一个数组,它是由一个可以发送结果控制器。它实现了所有更新逻辑做动画,章节标题,和删除。您可以创建对象的一个实例,给它的要求和一个block块配置cell,和关心些其他的。

此外,这种方法延伸到其他协议一样。一个明显的例子是uicollectionviewdatasource。这会给你巨大的灵活性;如果,在一些事物在发展,你决定要一个uicollectionview代替一个tableVIew,你很难在你的视图控制器里改变任何东西。你甚至可以使你的数据支持共同的协议

Move Domain Logic into the Model

Here is an example of code in view controller (from another project) that is supposed to find a list of active priorities for a user:

移动领域的逻辑模型

这里是视图控制器代码的一个例子从另一个项目功能是找一个活跃用户优先名单

- (void)loadPriorities {  NSDate* now = [NSDate date];  NSString* formatString = @"startDate <= %@ AND endDate >= %@";  NSPredicate* predicate = [NSPredicate predicateWithFormat:formatString, now, now];  NSSet* priorities = [self.user.priorities filteredSetUsingPredicate:predicate];  self.priorities = [priorities allObjects];}

However, it is much cleaner to move this code to a category on the User class. Then it looks like this inView Controller.m:

然而,它是非常干净将此代码移至到用户类别。然后,看起来像这个视图控制器。M

- (void)loadPriorities {  self.priorities = [self.user currentPriorities];}

and in User+Extensions.m:

- (NSArray*)currentPriorities {  NSDate* now = [NSDate date];  NSString* formatString = @"startDate <= %@ AND endDate >= %@";  NSPredicate* predicate = [NSPredicate predicateWithFormat:formatString, now, now];  return [[self.priorities filteredSetUsingPredicate:predicate] allObjects];}

Some code cannot be easily moved into a model object but is still clearly associated with model code, and for this, we can use aStore:

一些代码不能很容易地移动到一个模型对象,仍清晰的模型代码相关联的因此,我们可以使用一个存储

Creating the Store Class

In the first version of our example application, we had some code to load data from a file and parse it. This code was in the view controller:

在我们的示例应用程序的第一个版本,我们有一些代码从文件中读取数据并将其解析。此代码是在视图控制器

- (void)readArchive {    NSBundle* bundle = [NSBundle bundleForClass:[self class]];    NSURL *archiveURL = [bundle URLForResource:@"photodata"                                 withExtension:@"bin"];    NSAssert(archiveURL != nil, @"Unable to find archive in bundle.");    NSData *data = [NSData dataWithContentsOfURL:archiveURL                                         options:0                                           error:NULL];    NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:data];    _users = [unarchiver decodeObjectOfClass:[NSArray class] forKey:@"users"];    _photos = [unarchiver decodeObjectOfClass:[NSArray class] forKey:@"photos"];    [unarchiver finishDecoding];}

The view controller should not have to know about this. We created a Store object that does just this. By separating it out, we can reuse that code, test it separately and keep our view controller small. The store can take care of data loading, caching, and setting up the database stack. This store is also often called a service layer or arepository.

视图控制器不应该知道这个。我们创建了一个存储对象,就是这样。通过分离出来,我们可以重复使用这些代码,检测分离性和保持我们的视图控制器代码少。存储可以关心数据加载,高速缓存,和设置数据库堆栈。这种存储也常常被称为一个服务层或

Move Web Service Logic to the Model Layer

This is very similar to the topic above: don’t do web service logic in your view controller. Instead, encapsulate this in a different class. Your view controller can then call methods on this class with a callback handler (for example, a completion block). The nice thing is that you can do all your caching and error handling in this class too.

这是以上题目非常相似:不要在你的视图控制器逻辑Web服务。相反,封装在一个不同的类。你的视图控制器可以与一个回调函数在这个类的方法调用(例如,一个完成块)。令人高兴的是,你可以做所有的缓存和误差在这类处理

Move View Code into the View Layer

Building complicated view hierarchies shouldn’t be done in view controllers. Either use interface builder, or encapsulate views into their ownUIView subclasses. For example, if you build your own date picker control, it makes more sense to put this into aDatePickerView class than creating the whole thing in the view controller. Again, this increases reusability and simplicity.

If you like Interface Builder, then you can also do this in Interface Builder. Some people assume you can only use this for view controllers, but you can also load separate nib files with your custom views. In our example app, we created aPhotoCell.xib that contains the layout for a photo cell:

移动视图代码视图层

建立复杂的视图层次结构不应该在视图控制器。使用界面生成器,或封装的观点纳入自己的UIView子类。例如,如果你创建你自己的日期选择控件,把它放置更多的场景。再次这增加了可重用性和简单性

如果你喜欢界面生成器,那么你也可以在界面生成器做这个。有些人认为你只能使用此视图控制器,但你也可以负载nib文件与您的自定义视图分离。在我们的示例应用程序我们创建了一个photocell.xib包含一个photo cell的布局


As you can see, we created properties on the view (we don’t use the File’s Owner object in this xib) and connect them to specific subviews. This technique is also very handy for other custom views.

正如你所看到的,我们在视图中创建属性(我们也该XIB没有文件的所有者对象使用)和连接到特定的视图。这项技术也非常方便的自定义视图

Communication

One of the other things that happen a lot in view controllers is communication with other view controllers, the model, and the views. While this is exactly what a controller should do, it is also something we’d like to achieve with as minimal code as possible.

There are a lot of well-explained techniques for communication between your view controllers and your model objects (such as KVO and fetched results controllers), however, communication between view controllers is often a bit less clear.

We often have the problem where one view controller has some state and communicates with multiple other view controllers. Often, it then makes sense to put this state into a separate object and pass it around the view controllers, which then all observe and modify that state. The advantage is that it’s all in one place, and we don’t end up entangled in nested delegate callbacks. This is a complex subject, and we might dedicate a whole issue to this in the future.

讨论

一个经常发生这种事在视图控制器的其他事情是与其他视图控制器,模型,和视图。这正是一个控制器应该做的也是我们想实现尽可能最小的代码

有很多很好的解释技术之间的沟通,的视图控制器模型对象(如KVO结果控制器)在视图控制器之间通信往往是有点不太清楚

我们经常有问题的其中一个视图控制器具有一定的状态和与其他多个视图控制器进行通信。通常,然后就把这个状态变成一个单独的对象和周围的视图控制器通过它,然后所有的观察和修改的状态。它的优点是,它是在一个地方,我们不会出现在嵌套的委托回调纠缠。这是一个复杂的对象我们可能会在未来出现大问题。

Conclusion

We’ve seen some techniques for creating smaller view controllers. We don’t strive to apply these techniques wherever possible, as we have only one goal: to write maintainable code. By knowing these patterns, we have better chances of taking unwieldy view controllers and making them clearer.

结论

我们已经看到一些技术创建更小的视图控制器。我们不努力将这些技术应用在可能的情况下,我们只有一个目标:编写可维护的代码。通过了解这些模式我们有更好的机会笨拙的视图控制器使他们更清楚

————————————————————————————     END   ————————————————————————————
量好大啊,再也不想写翻译文章了。不过外国人的思想还不错。特别文章前部分!



0 0