我曾经开发过一个iphone应用程序,它显示了大量的输入,这些输入分为不一样的类别,在`UITableView`...若要更改其中一个输入的值,用户按下表视图中的对应行,并在出现的单独屏幕中更改该值。表视图为每一个类别有一个节,每一个节包含每一个输入的表格单元格(行)。编程
问题是输入的数量变得很是很是大,因此它没有给用户一个很是好的概述。从桌面滚动到底部甚至很乏味。数组
咱们决定用户应该可以经过简单地按下节的标题来折叠和展开表中的部分(类别)。咱们要求实现这一目标的代码应该是可重用的,而且要求对现有代码进行尽量少的更改。缓存
另外,若是你想一块儿进阶,不妨添加一下交流群[1012951431](),选择加入一块儿交流,一块儿学习。期待你的加入!(进群获取本文源码)多线程
下面的屏幕截图显示了表视图及其可折叠部分的外观:app
我认为实现上述目标的最佳方法是建立 UITableView 类,命名为 CollapsableTableView ...这确保了代码是可重用的。若是操做正确,则不须要对 UITableView- 他们会像一个普通的人同样处理桌子上的风景`UITableView`...惟一必要的更改是更改 UITableView 在西布文件到这个新的子类。,以确保客户端能够像普通用户同样使用表视图。UITableView ,咱们必须尝试容许彻底经过 UITableView 类,包括 UITableViewDelegate ,以及 UITableViewDataSource 协议。iphone
可折叠表视图必须以某种方式跟踪哪些区段被折叠(收缩)以及哪些部分被展开。可能最明显的方法是维护一组已展开的节的索引,或者一个布尔数组,其中每一个索引的值指示对应的节是否展开。可是,若是咱们假设表视图的客户端能够添加和删除节(在咱们的场景中是这样的),那么节的索引将不会保持固定,所以处理索引充其量也是很麻烦的。所以,咱们必须为节找到不一样的标识符。为此,咱们可使用章节的标题文本。固然,这假定节的标题文本惟一地标识该节,而且其标题文本保持不变,但考虑到必须坚持使用 UITableView 同窗们,这多是咱们能作的最好的了。这还假设客户端实现了 tableView:titleForHeaderInSection: 的选择器 UITableViewDelegate 全部表格单元格的协议。在咱们的项目中,状况就是这样。在使用代码节中,咱们将解释咱们的类是如何支持实现 tableView:viewForHeaderInSection: 选择器。ide
为了更容易地管理头视图,咱们建立了一个 UIViewController 类,命名为 CollapsableTableViewHeaderViewController ...对于这门课,有两个西布一号西布用于平面布局的表,而用于具备分组布局的表。此类包含视图中可操做的全部标签的`IB`出口。它存储一个布尔值,指示区段是否折叠。此视图控制器类还确保它的视图在用户点击它时通知咱们,以便 CollapsableTableView 才能采起必要的行动。布局
下面是.h.的档案 CollapsableTableViewHeaderViewController: 学习
#import <UIKit/UIKit.h> #import "TapDelegate.h" #import "CollapsableTableViewTapRecognizer.h" @interface CollapsableTableViewHeaderViewController : UIViewController { IBOutlet UILabel *collapsedIndicatorLabel,*titleLabel,*detailLabel; CollapsableTableViewTapRecognizer* tapRecognizer; BOOL viewWasSet; id<TapDelegate> tapDelegate; NSString* fullTitle; BOOL isCollapsed; } @property (nonatomic, retain) NSString* fullTitle; @property (nonatomic, readonly) UILabel* titleLabel; @property (nonatomic, retain) NSString* titleText; @property (nonatomic, readonly) UILabel* detailLabel; @property (nonatomic, retain) NSString* detailText; @property (nonatomic, assign) id<TapDelegate> tapDelegate; @property (nonatomic, assign) BOOL isCollapsed; @end
collapsedIndicatorLabel 是否显示“-”或“+”的小标签,取决于区段是否折叠。当 isCollapsed 的文本被更改。 collapsedIndicatorLabel 则相应地设置为“-”或“+”。 titleLabel 是包含标题和 detailLabel 显示标题右侧的可选详细文本。优化
下面是`TapDelegate`议定书:
#import <UIKit/UIKit.h> @protocol TapDelegate - (void) view:(UIView*) view tappedWithIdentifier:(NSString*) identifier; @end
这个 view:tappedWithIdentifier: 当头视图被点击时调用选择器 CollapsableTableView 实现 TapDelegate 协议,使其可以折叠或展开相应的报头以响应此协议。调用选择器时,将使用 view 参数的标头的标题字符串。 identifier 参数,以便`CollapsableTableView`能够进行查找,以肯定标题当前是否已折叠,以及其当前节索引是什么。
在该项目的第一个已发布的实现中,该选择器由 CollapsableTableViewHeaderViewController 对应的标题视图。由于在该版本中, CollapsableTableView 存储(并所以保留) CollapsableTableViewHeaderViewControllers 其全部章节。可是,为了提升实现的内存效率--特别是对于具备多个节的表 --CollapsableTableView 因此它再也不这样作了。所以,结果是 CollapsableTableViewHeaderViewController 在头视图出如今表中后不久,就会从内存中释放报头视图(报头)。UIView 只要它在表中可见,它仍然保留在内存中)。这意味着当头视图被点击时,可能没有。 CollapsableTableViewHeaderViewController 调用 TapDelegate 选择器。
在咱们寻找这个问题的解决方案以前,让咱们看看头视图的点击是如何在 CollapsableTableViewHeaderViewController.m.
- (void) setView:(UIView*) newView { if (viewWasSet) { [self.view removeGestureRecognizer:tapRecognizer]; [tapRecognizer release]; } [super setView:newView]; tapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(headerTapped)]; [self.view addGestureRecognizer:tapRecognizer]; viewWasSet = YES; } - (void) headerTapped { [tapDelegate viewTapped:self.view ofViewController:self]; }
因此咱们推翻了 setView: 方法 UIViewController 类以添加 UITapGestureRecognizer 到 UIView 分配给 CollapsableTableViewHeaderViewController ...这,这个 UITapGestureRecognizer 的方法被配置为调用 CollapsableTableViewHeaderViewController 每当头视图被点击时。在新代码中,此技术再也不起做用,由于 CollapsableTableViewHeaderViewController 将常常在用户点击头时被解除分配。
这个问题的惟一解决方案多是最明显的,就是配置 UITapGestureRecognizer 若要调用对象中的选择器,当用户单击标头视图时,该对象将不会被释放。该对象的一些选择以下:
第二个选择将不起做用,由于咱们没法控制 UIView 传递到 setView: 方法来自(也就是说,咱们不能使用子类)。 UIView 为了给它添加一个额外的方法,也许咱们能够将传入的内容包装起来。 UIView 类的子类中的 UIView 咱们本身的,但咱们不要去那里!)向 CollapsableTableView 是一个选项,尽管添加没有参数的方法是不行的,由于 CollapsableTableView 不知道哪一个标题已被点击。然而,在文件中 UITapGestureRecognizer ,咱们看到替代选择器类型是一个选择器,它接受 UITapGestureRecognizer 对象做为参数。可是,咱们必须把 UITapGestureRecognizer 以添加存储标题字符串的属性。因此若是咱们必须把 UITapGestureRecognizer,使用第三个选项并配置 UITapGestureRecognizer 调用其内部的选择器。这是在实现中采起的方法:咱们使用 UITapGestureRecognizer ,咱们称之为 CollapsableTableViewTapRecognizer ,定义以下:
#import <Foundation/Foundation.h> #import "TapDelegate.h" @interface CollapsableTableViewTapRecognizer : UITapGestureRecognizer { id<TapDelegate> tapDelegate; NSString* fullTitle; UIView* tappedView; } @property (nonatomic, assign) id<TapDelegate> tapDelegate; @property (nonatomic, retain) NSString* fullTitle; @property (nonatomic, assign) UIView* tappedView; - (id) initWithTitle:(NSString*) theFullTitle andTappedView:(UIView*) theTappedView andTapDelegate:(id<TapDelegate>) theTapDelegate; @end
在 initWithTitle:andTappedView:andTapDelegate: 方法,CollapsableTableViewTapRecognizer 对象配置为调用私有方法。headerTapped 当景物被点击的时候。
- (void) headerTapped { [tapDelegate view:tappedView tappedWithIdentifier:fullTitle]; }
让咱们回到 CollapsableTableView 如今。当它从客户端得到一个节的标题时,它须要可以进行一次查找,以查看标头是否折叠,以及标头的节索引是什么。为此,咱们保持两个独立的 NSMutableDictionary 对象:将标题标题映射为指示标头是否折叠的布尔值的对象,以及将标题标题映射为整数(给定标头节索引的整数)的对象。咱们还可使用一个字典在指定的索引上查找该节的标题(固然,每当客户端从表中添加或删除一个节时,就必须更新该字典)。
那么,如何 CollapsableTableView 其实是塌陷和扩张部分?那么,折叠的部分只会有0行,因此即便客户端将返回该节的正常行数, CollapsableTableView 将返回折叠节的行数为0,或由客户端返回的扩展节的行数。这代表 CollapsableTableView 须要拦截对 tableView:numberOfRowsInSection: 方法。它还必须返回 CollapsableTableViewHeaderViewController 对于每一个部分,所以它还必须拦截对 tableView:viewForHeaderInSection: 方法。因此为了 CollapsableTableView 为了可以响应这两个选择器,它必须实现 UITableViewDelegate 而 UITableViewDataSource 协议,并在运行时将其委托和数据源属性设置为.自己!可是,必须将对这些协议的选择器的许多调用转发给客户端,所以 CollapsableTableView 存储真实委托和数据源的引用,以便为这些状况提供参考。
- (void) setDelegate:(id <UITableViewDelegate>) newDelegate { [super setDelegate:self]; realDelegate = newDelegate; } - (void) setDataSource:(id <UITableViewDataSource>) newDataSource { [super setDataSource:self]; realDataSource = newDataSource; }
的接口文件CollapsableTableView:
#import <Foundation/Foundation.h> #import "TapDelegate.h" #define COLLAPSED_INDICATOR_LABEL_TAG 36 #define BUSY_INDICATOR_TAG 37 @interface CollapsableTableView : UITableView <UITableViewDelegate,UITableViewDataSource,TapDelegate> { id<UITableViewDelegate> realDelegate; id<UITableViewDataSource> realDataSource; id<CollapsableTableViewDelegate> collapsableTableViewDelegate; ... } @property (nonatomic,assign) id<CollapsableTableViewDelegate> collapsableTableViewDelegate; @property (nonatomic,retain) NSString* collapsedIndicator; @property (nonatomic,retain) NSString* expandedIndicator; @property (nonatomic,assign) BOOL showBusyIndicator; @property (nonatomic,assign) BOOL sectionsInitiallyCollapsed; @property (nonatomic,readonly) NSDictionary* headerTitleToIsCollapsedMap; - (void) setIsCollapsed:(BOOL) isCollapsed forHeaderWithTitle:(NSString*) headerTitle; - (void) setIsCollapsed:(BOOL) isCollapsed forHeaderWithTitle:(NSString*) headerTitle andView:(UIView*) headerView; - (void) setIsCollapsed:(BOOL) isCollapsed forHeaderWithTitle:(NSString*) headerTitle withRowAnimation:(UITableViewRowAnimation) rowAnimation; - (void) setIsCollapsed:(BOOL) isCollapsed forHeaderWithTitle:(NSString*) headerTitle andView:(UIView*) headerView withRowAnimation:(UITableViewRowAnimation) rowAnimation; @end
执行 CollapsableTableView 从讨论到这一点,基本上都是这样。下面的段落简要解释了该类的公共属性和方法的用途。
这个 collapsableTableViewDelegate 属性能够设置为实现 CollapsableTableViewDelegate 属性,以便每当某个区段折叠或展开时,以及当该对象完成折叠或展开时,都会通知该对象。
默认折叠和展开指示符(默认值分别为“+”和“-”)可使用 collapsedIndicator 和 expandedIndicator 财产。
这个 showBusyIndicator 属性的默认值为YES,若是设置,则会致使活动指示符视图( SPINTER )(位于标头视图中的子视图中具备由BUSY_INDICATOR_TAG)当折叠或展开页眉视图的部分花费的时间超过0.5秒时,要在标题视图上动画。
这个 sectionsInitiallyCollapsed 属性的默认值为NO,并控制新的部分是否会首先被折叠或不折叠。
这个 headerTitleToIsCollapsedMap 物业供应 NSDictionary 将标题的标题字符串映射到 NSNumber 对象,该对象包含指示标头是否折叠的布尔值。
这个 setIsCollapsed:forHeaderWithTitle: ...方法将用于以编程方式折叠或展开区段。若是客户端具备对 UIView 对于相应的标头,它能够调用包含 andView: 用那个`UIView`做为参数。若是调用了另外两个方法中的任何一个,则必须从新加载相应的部分(和头视图),而且动画有时会比... andView: 使用方法。
须要添加到 Xcode 项目以便使用 CollapsableTableView 类中的 CollapsableTableView 文件夹中的压缩文件(下载源代码).
另外,若是你想一块儿进阶,不妨添加一下交流群[1012951431](),选择加入一块儿交流,一块儿学习。期待你的加入!(进群获取本文源码)
CollapsableTableView 彻底能够像普通的 UITableView ,只要客户端实现 tableView:titleForHeaderInSection: 选择器(相对于 tableView:viewForHeaderInSection: )用于全部表单元格。惟一必要的更改是更改 UITableView 在西布到 CollapsableTableView ...要作到这一点,请打开西布文件,选择 UITableView ,打开身份检查器并键入“ CollapsableTableView “”类“字段旁边。
执行 CollapsableTableView 也容许使用 tableView:viewForHeaderInSection: ,但在这里,它没法访问标头的标题字符串,它一般用做标头的标识符。相反,它使用字符串“标记%i”,其中%i是tag返回的视图的属性(若是tag为0,但区段索引不是0,此数字默认为 CollapsableTableView )。这意味着,若是客户端返回某些单元格的视图(而不是头文本字符串),而且若是它能够添加和删除部分,则必须分配惟一的tag对应于每一个区段的视图的编号。
若是客户端返回某些单元格的视图,则这些视图能够包含一个标签,该标签指示是否折叠了标头。简单地设置tag属性设置为 COLLAPSED_INDICATOR_LABEL_TAG 中定义的 CollapsableTableView.h ...这个 CollapsableTableView 而后,每当区段折叠或展开时,该标签的文本将设置为“+”或“-”(除非 collapsedIndicator或expandedIndicator 属性已设置为不一样的字符串)。
的客户端。CollapsableTableView 可能不知道它不适用于常规的 UITableView 但若是它知道 UITableView 是 CollapsableTableView ,它能够将对象强制转换为后一种类型,并使用headerTitleToIsCollapsedMap属性以肯定哪些区段已折叠,而setIsCollapsed:forHeaderWithTitle: 方法以编程方式折叠或展开区段。
正如在实施部分,CollapsableTableView 还容许将细节文本显示在标题的右侧。若要使用此功能,请在 tableView:titleForHeaderInSection: ,返回表单的字符串“Header Text\Details Text”。
可折叠表视图如今缓存全部标头视图的高度。这实际上消除了在折叠或扩展区段时发生的过分延迟。
翻译地址:https://www.codeproject.com/Articles/240435/Collapsable-Table-View-for-iOS