对 UITableViewController 的扩展,旨在提供在大多数场景中可重用的一组额外功能。
配置加载样式,目前支持头部和脚部。
- (void)viewDidLoad
{
[super viewDidLoad];
theTableViewController.loadingStyle = MwfTableViewLoadingStyleFooter; // default is MwfTableViewLoadingStyleHeader
}
以程序方式触发加载动画
[theTableViewController setLoading:YES animated:YES];
以程序方式停止加载动画
[theTableViewController setLoading:YES animated:NO];
如果需要,您可以重写一些方法以为您自己的应用程序提供自定义的外观和感觉。
- (UIView *)createLoadingView
{
// ... construct your custom loading view
return yourAwesomeCustomLoadingView;
}
- (void)willShowLoadingView:(UIView *)loadingView
{
// cast to your implementation
YourAwesomeLoadingView * view = (YourAwesomeLoadingView *)loadingView;
// ... do something about it, e.g. start animating the activity indicator view
}
- (void)didHideLoadingView:(UIView *)loadingView
{
// cast to your implementation
YourAwesomeLoadingView * view = (YourAwesomeLoadingView *)loadingView;
// ... do something about it, e.g. stop animating the activity indicator view
}
提供了一个名为 MwfTableData
的类来管理您的表格后端存储。与使用 NSArray
相比,此类提供了与表格视图交互所需的所有便捷功能。
创建表格数据
+ (MwfTableData *) createTableData;
+ (MwfTableData *) createTableDataWithSections;
访问数据
- (NSUInteger) numberOfSections;
- (NSUInteger) numberOfRows: (NSUInteger)section;
- (NSUInteger) numberOfRows;
- (id) objectForSectionAtIndex: (NSUInteger)section;
- (id) objectForRowAtIndexPath: (NSIndexPath *)ip;
- (NSIndexPath *) indexPathForRow: (id)object;
- (NSUInteger) indexForSection: (id)sectionObject;
插入数据
- (NSUInteger)addSection: (id)sectionObject;
- (NSUInteger)insertSection: (id)sectionObject atIndex: (NSUInteger)sectionIndex;
- (NSIndexPath *)addRow: (id)object InSection: (NSUInteger)sectionIndex;
- (NSIndexPath *)insertRow: (id)object atIndexPath: (NSIndexPath *)indexPath;
- (NSIndexPath *)addRow: (id)object;
- (NSIndexPath *)insertRow: (id)object atIndex: (NSUInteger)index;
删除数据
- (NSIndexPath *)removeRowAtIndexPath: (NSIndexPath *)indexPath;
- (NSUInteger)removeSectionAtIndex: (NSUInteger)sectionIndex;
更新数据
- (NSUInteger)updateSection: (id)object atIndex: (NSUInteger)section;
- (NSIndexPath *)updateRow: (id)object atIndexPath: (NSIndexPath *)indexPath;
MwfTableData
重写 createAndInitTableData
在此方法中,您可以创建 MwfTableData
实例,使用其中一个创建方法(带或不带部分),然后用一些数据初始化。
- (MwfTableData *)createAndInitTableData;
{
MwfTableData * tableData = [MwfTableData createTableData];
[tableData addRow:@"Row 1"];
[tableData addRow:@"Row 2"];
[tableData addRow:@"Row 3"];
[tableData addRow:@"Load More"];
return tableData;
}
执行批量更新
MwfTableViewController
提供了一个名为 performUpdates:(void(^)(MwfTableData *))updatesBlock
的方法,您可以通过调用它来对您的表格视图执行批量更新(添加、删除、更新部分/行)。此方法将更新后端存储以及更新表格视图(使用 UITableViewRowAnimationAutomatic
用于行动画)。此方法使您免于跟踪已更改的索引集和路径,让您专注于更新表格的应用程序逻辑。
- (void)loadMoreData {
[self performUpdates:^(MwfTableData * data) {
// ... call the insert/delete/update method of MwfTableData
}];
}
重新加载表格视图
通过在 MwfTableViewController
的 tableData
属性中设置表格数据将触发 tableView 的 reloadData
。
- (void)reloadEntireTable { // a method in your MwfTableViewController subclass
MwfTableData * tableData = [MwfTableData createTableDataWithSections];
[tableData addSection:@"Section 1"];
[tableData addRow:@"Row 1" inSection:0];
[tableData addRow:@"Row 2" inSection:0];
self.tableData = tableData;
}
此功能旨在简化对 tableView:cellForRowAtIndexPath:
方法的实现。如果您使用了多种表格视图单元格类型的表格,您可能会得到一个庞大且复杂的 tableView:cellForRowAtIndexPath:
方法实现。
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)ip;
{
UITableViewCell * cell = nil;
id dataObject = ...; // get data object from backing store
NSString * reuseIdentifier = @"DefaultCell"; // default
Class cellClass = [UITableViewCell class]; // default
if ([dataObject isKindOf:[DataType1 class]]) {
reuseIdentifier = @"DataType1Cell";
cellClass = [DataType1Cell class];
}
else if ([dataObject isKindOf:[DataType2 class]]) {
reuseIdentifier = @"DataType2Cell";
cellClass = [DataType2Cell class];
}
... // and so on
cell = [tableView dequeueReusableCellWithIdentifier:reuseIdentifier];
if (!cell) {
cell = [[cellClass alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:reuseIdentifier];
}
// configure cell
if ([dataObject isKindOf:[DataType1 class]]) {
DataType1Cell * theCell = (DataType1Cell *)cell;
// configure using dataObject
}
else if ([dataObject isKindOf:[DataType2 class]]) {
DataType2Cell * theCell = (DataType2Cell *)cell;
// configure using dataObject
}
return cell;
}
上面的例子展示了当要显示的单元格类型增加时,情况可能多么糟糕。维护它可能成为一个噩梦。
因此,MwfTableViewController
实现了一个方法 tableView:cellForObject:atIndexPath:
,旨在改善可能存在的庞大、混乱的 tableView:cellForRowAtIndexPath:
方法。默认情况下,MwfTableViewController
通过调用 MwfTableData
和该方法来实现 tableView:cellForRowAtIndexPath:
。
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath;
{
id rowItem = [self.tableData objectForRowAtIndexPath:indexPath];
UITableViewCell * cell = [self tableView:tableView cellForObject:rowItem];
// to prevent app crashing when returning nil
if (!cell) {
cell = [self.tableView dequeueReusableCellWithIdentifier:@"NilCell"];
if (!cell) cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"NilCell"];
}
return cell;
}
如果您不喜欢默认的实现(因为您没有使用 MwfTableData
),您可以在您的 MwfTableViewController
子类中重写此方法以满足您的需求,同时仍然利用 tableView:cellForObject:atIndexPath:
方法。
tableView:cellForObject:atIndexPath:
是如何工作的呢?简单来说,设计采用了基于约定的编码。它根据行数据的名称调用创建和配置方法。例如:假设您有行数据类型 Tweet
和 Comment
,它将尝试为您调用以下方法
- (UITableViewCell *)tableView:(UITableView *)tableView cellForTweetAtIndexPath:(NSIndexPath *)ip
// 假设它返回 TweetCell 实例- (UITableViewCell *)tableView:(UITableView *)tableView cellForCommenAtIndexPatht:(NSIndexPath *)ip
// 假设它返回 CommentCell 实例- (UITableViewCell *)tableView:(UITableView *)tableView configCell:(TweetCell *)cell forTweet:(Tweet *)tweet
- (UITableViewCell *)tableView:(UITableView *)tableView configCell:(CommentCell *)cell forTweet:(Comment *)comment
如果您未为某个特定行类型实现创建和/或配置方法,但实现了其超类的方法,它们将被调用。例如,您有一个类 Shape 和 Box,其中 Box 是 Shape 的子类。在您的代码中,您实现了用于 Shape 的创建和配置方法,但没有为 Box 实现
- (UITableViewCell*)tableView:(UITableView*)tableView cellForShapeAtIndexPath:(NSIndexPath*)ip;
{
UITableViewCell * cell = ...; // dequeue and create the cell
return cell
}
- (void)tableView:(UITableView*)tableView configCell:(UITableViewCell*)cell forShape:(Shape*)shape;
{
// ... configure the cell
}
当 MwfTableViewController
遇到类型为 Box 的行时,它将调用 Shape 的方法(因为 Shape 是 Box 的超类)。
注意:如果您想进一步简化代码,甚至不编写这些方法... 继续阅读!
MwfTableItem
是一个简单的类,用于表示行单元格的模型。它有 3 个属性,即 cellClass
、reuseIdentifier
和 userInfo
。
cellClass
,按照约定,它将返回带有后缀 Cell
的项目类名称。例如 MwfTableItem
和 MwfTableItemCell
,Tweet
和 TweetCell
。reuseIdentifier
,按照约定,它将返回 cellClass
名称。userInfo
,默认为 nil
。您实际上可以给它设置任何内容(您可以用它来进一步配置单元格)。使用此方法的优点
MwfTableViewController
的 tableView:cellForObject:atIndexPath:
(如前所述)一起使用时,您可以消除实现创建和配置方法的必要性。MwfTableViewController
的 tableView:cellForObject:atIndexPath:
和 userInfo
设置一起使用时,您可以实现 tableView:configCell:for<TableItemClassName>UserInfo:
方法,使用 userInfo
中的数据来配置单元格。示例
// from demo project
@interface MwfDemoLoadingItem : MwfTableItem
@property (nonatomic,retain) NSString * loadingText;
@end
@interface MwfDemoLoadingItemCell : MwfTableItemCell
@property (nonatomic,retain) UIActivityIndicatorView * activityIndicatorView;
@end
// the implementation
@implementation MwfDemoLoadingItem
@synthesize loadingText = _loadingText;
@end
@implementation MwfDemoLoadingItemCell
@synthesize activityIndicatorView = _activityIndicatorView;
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier;
{
self = [super initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:reuseIdentifier];
if (self) {
_activityIndicatorView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
self.accessoryView = _activityIndicatorView;
}
return self;
}
- (void)setItem:(MwfDemoLoadingItem *)item;
{
[super setItem:item];
if (item.loadingText) self.textLabel.text = item.loadingText;
else self.textLabel.text = @"Loading...";
[_activityIndicatorView startAnimating];
}
@end
当使用 tableView:cellForObject:atIndexPath:
和 userInfo
设置时的示例。
// from demo project
// The following method will be automatically invoked if userInfo is set.
- (void)tableView:(UITableView *)tableView configCell:(MwfDemoLoadingItemCell *)cell forMwfDemoLoadingItemUserInfo:(NSString *)userInfo; // I know the userInfo is always NSString
{
cell.detailTextLabel.text = userInfo;
}
提供了3个宏定义来帮助简化上述回调方法的编写(减少按键次数和错误)。您可以选择以mwf_
为前缀或者更简洁的$
符号。如果更喜欢使用$
符号,只需定义CK_SHORTHAND
。
#define CK_SHORTHAND
#import "MwfTableViewController.h"
示例
// -(UITableViewCell*)tableView:(UITableView*)tableView cellForTweetAtIndexPath:(NSIndexPath*)indexPath
// can be written using macro as following
-$cellFor(Tweet)
{
// ... implementation
return cell;
}
// -(void)tableView:(UITableView*)tableView configCell:(TweetCell *)cell forTweet:(Tweet *)item
// can be written using macro as following
-$configCell(Tweet,TweetCell)
{
// ... implementation
}
// -(void)tableView:(UITableView*)tableView configCell:(AwesomeItemCell*)cell forAwesomeItemUserInfo:(NSString *)userInfo
// can be written using macro as following
-$configCellWithUserInfo(AwesomeItem,AwesomeItemCell,NSString)
{
// ... implementation
}
搜索是与表格视图一起使用的非常常见的一个功能。MwfTableViewController
提供了一些基本功能,以便您能够轻松地在表格视图中实现搜索。
启用表格视图控制器的搜索功能
要启用搜索,只需将wantSearch
设置为YES。此外,实现createAndInitSearchResultsTableData
以自定义搜索结果的初始数据。
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil;
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
self.wantSearch = YES;
}
return self;
}
- (MwfTableData *)createAndInitSearchResultsTableData {
MwfTableData * td = ...; // create and perhaps initialize with some rows
return td;
}
处理搜索查询
当用户在搜索栏中输入或者在搜索选项中进行选择时,将调用- (MwfTableData *)createSearchResultsTableDataForSearchText:(NSString *)searchText scope:(NSString *)scope;
。您应该实现此方法以填充一个包含搜索结果的MwfTableData
实例。默认情况下,此方法的调用被延迟了0.4秒
。您可以通过设置searchDelayInSeconds
属性来更改此值。
如果你希望在后台执行搜索,你应该从该方法返回nil
并将搜索任务派送到后台。一旦搜索结果可用,只需将searchResultsTableData
设置为显示它们。
额外内容
与tableData
类似,searchResultsTableData
也有一个等效的更新方法:performUpdatesForSearchResults:(void (^)(MwfTableData *))updateBlock
。
注意
当wantSearch
设置为YES
时,响应UITableViewController
的代理或数据源回调需要特别注意。您不应该直接调用tableData
或searchResultsTableData
以避免在使用不正确的表数据时出错。相反,提供了一个方法- (MwfTableData *) tableDataForTableView:(UITableView *)tableView;
供您使用,以获取特定tableView的正确表数据。这个方法的实现实际上非常简单
- (MwfTableData *)tableDataForTableView:(UITableView *)tableView;
{
if (tableView == self.tableView) return _tableData;
return _searchResultsTableData;
}
记住这一点将为您节省大量的调试时间,有时这些调试问题可能非常难以解决。
MwfTableViewController受MIT许可证的许可。特此授予任何获得此软件及其相关文档文件(以下简称“软件”)副本的人免费的许可,可以在不受限制的情况下处理软件,包括但不限于使用、复制、修改、合并、出版、分发、再许可和/或销售软件副本,并允许将软件提供给其他人使用,前提是遵守以下条件
上述版权声明和本许可声明应包含在软件的副本或实质性部分的副本中。
本软件按“原样”提供,不做任何保证,无论是明示的还是暗示的,包括但不限于适销性、针对特定目的的适用性和非侵权性保证。在任何情况下,作者或版权所有者均不对任何索赔、损害或其他责任承担任何责任,无论这些责任是基于合同、侵权或其他法律原因产生的,无论这些责任是否与软件或其使用或其他操作有关。