MwfTableViewController 0.0.6

MwfTableViewController 0.0.6

测试已测试
语言语言 Obj-CObjective C
许可证 MIT
发布上次发布2014年12月

无人认领 维护。



  • 梅文福

对 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;

在 MwfTableViewController 子类中使用 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

     }];
  }

重新加载表格视图

通过在 MwfTableViewControllertableData 属性中设置表格数据将触发 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: 是如何工作的呢?简单来说,设计采用了基于约定的编码。它根据行数据的名称调用创建和配置方法。例如:假设您有行数据类型 TweetComment,它将尝试为您调用以下方法

  • - (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 个属性,即 cellClassreuseIdentifieruserInfo

  • cellClass,按照约定,它将返回带有后缀 Cell 的项目类名称。例如 MwfTableItemMwfTableItemCellTweetTweetCell
  • reuseIdentifier,按照约定,它将返回 cellClass 名称。
  • userInfo,默认为 nil。您实际上可以给它设置任何内容(您可以用它来进一步配置单元格)。

使用此方法的优点

  • 可复用性,您可以构建一个项目目录和单元格,您可以在需要时重复使用。
  • 当与 MwfTableViewControllertableView:cellForObject:atIndexPath: (如前所述)一起使用时,您可以消除实现创建和配置方法的必要性。
  • 当与 MwfTableViewControllertableView: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的代理或数据源回调需要特别注意。您不应该直接调用tableDatasearchResultsTableData以避免在使用不正确的表数据时出错。相反,提供了一个方法- (MwfTableData *) tableDataForTableView:(UITableView *)tableView;供您使用,以获取特定tableView的正确表数据。这个方法的实现实际上非常简单

  - (MwfTableData *)tableDataForTableView:(UITableView *)tableView;
  {
    if (tableView == self.tableView) return _tableData;
    return _searchResultsTableData;
  }

记住这一点将为您节省大量的调试时间,有时这些调试问题可能非常难以解决。

许可

MwfTableViewController受MIT许可证的许可。特此授予任何获得此软件及其相关文档文件(以下简称“软件”)副本的人免费的许可,可以在不受限制的情况下处理软件,包括但不限于使用、复制、修改、合并、出版、分发、再许可和/或销售软件副本,并允许将软件提供给其他人使用,前提是遵守以下条件

上述版权声明和本许可声明应包含在软件的副本或实质性部分的副本中。

本软件按“原样”提供,不做任何保证,无论是明示的还是暗示的,包括但不限于适销性、针对特定目的的适用性和非侵权性保证。在任何情况下,作者或版权所有者均不对任何索赔、损害或其他责任承担任何责任,无论这些责任是基于合同、侵权或其他法律原因产生的,无论这些责任是否与软件或其使用或其他操作有关。