iOS开发高级分享 - iOS的可折叠表视图

导言

我曾经开发过一个iphone应用程序,它显示了大量的输入,这些输入分为不一样的类别,在`UITableView`...若要更改其中一个输入的值,用户按下表视图中的对应行,并在出现的单独屏幕中更改该值。表视图为每一个类别有一个节,每一个节包含每一个输入的表格单元格(行)。编程

问题是输入的数量变得很是很是大,因此它没有给用户一个很是好的概述。从桌面滚动到底部甚至很乏味。数组

咱们决定用户应该可以经过简单地按下节的标题来折叠和展开表中的部分(类别)。咱们要求实现这一目标的代码应该是可重用的,而且要求对现有代码进行尽量少的更改。缓存

另外,若是你想一块儿进阶,不妨添加一下交流群[1012951431](),选择加入一块儿交流,一块儿学习。期待你的加入!(进群获取本文源码多线程

下面的屏幕截图显示了表视图及其可折叠部分的外观:app

最高达到IOS 6                                       iOS 7+

         

实施

我认为实现上述目标的最佳方法是建立 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 类以添加 UITapGestureRecognizerUIView 分配给 CollapsableTableViewHeaderViewController ...这,这个 UITapGestureRecognizer 的方法被配置为调用 CollapsableTableViewHeaderViewController 每当头视图被点击时。在新代码中,此技术再也不起做用,由于 CollapsableTableViewHeaderViewController 将常常在用户点击头时被解除分配。

这个问题的惟一解决方案多是最明显的,就是配置 UITapGestureRecognizer 若要调用对象中的选择器,当用户单击标头视图时,该对象将不会被释放。该对象的一些选择以下:

  •  CollapsableTableView
  •  UIView
  •  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 为了可以响应这两个选择器,它必须实现 UITableViewDelegateUITableViewDataSource 协议,并在运行时将其委托和数据源属性设置为.自己!可是,必须将对这些协议的选择器的许多调用转发给客户端,所以 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 属性,以便每当某个区段折叠或展开时,以及当该对象完成折叠或展开时,都会通知该对象。

默认折叠和展开指示符(默认值分别为“+”和“-”)可使用 collapsedIndicatorexpandedIndicator 财产。
这个 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 但若是它知道 UITableViewCollapsableTableView ,它能够将对象强制转换为后一种类型,并使用headerTitleToIsCollapsedMap属性以肯定哪些区段已折叠,而setIsCollapsed:forHeaderWithTitle: 方法以编程方式折叠或展开区段。

正如在实施部分,CollapsableTableView 还容许将细节文本显示在标题的右侧。若要使用此功能,请在 tableView:titleForHeaderInSection: ,返回表单的字符串“Header Text\Details Text”。

 历史

  •  2011/08/13
    •  初始版本
  •  2011/10/29
    •  在上一个版本中,在IOS 5中,标题的高度都是0。这个tableView:heightForHeaderInSection:选择器如今找到适当的标题视图,并直接询问它的高度,这解决了问题。
    •  增长了对多行标头的支持。若是numberOfLines属性设置为0,而且标头的文本不适合一行,标签将按须要将文本拆分红多行,标头视图控制器将设置标签和标头视图的高度,以便全部行都适合(这在setTitleText:选择器CollapsableTableViewHeaderViewController.m)。能够经过设置标签的numberOfLines属性设置为最大行数。
    •  1 st.osama指出setIsCollapsed:forHeaderWithTitle:在显式从新加载相应标头的整个部分以后,选择器才有效果。这个已经修好了。
    •  当最后一节被折叠并展开时,表视图最多向下滚动到该节的第五行,这样用户就能够看到出现了一些行。
  •  2011/11/05
    •  修正了iOS 4中出现的错误,致使纯文本表格视图的标题消失。
  •  2011/11/27
    •  CollapsableTableView被更改,使其再也不存储全部的CollapsableTableViewHeaderViewController各部分的对象。此更改的目的是提升实现的内存效率,特别是对于有许多节的表。这是一个至关戏剧性的变化,但文章的相关部分已经更新。
    •  这个getHeaderTitleToIsCollapsedMap方法CollapsableTableView被只读属性替换。headerTitleToIsCollapsedMap.
    •  自定义头视图如今能够包含一个具备魔力的标签。tag值为36(按常量定义)COLLAPSED_INDICATOR_LABEL_TAG在……里面CollapsableTableView.h)当相应的标题折叠或展开时,其文本将被更新为“+”或“-”。
  •  2012/01/26
    •  根据阿拉斯加22的请求,除了initWithCoder:方法,CollapsableTableView如今也重写了init, initWithFrame:,和initWithFrame:style:方法来执行必要的初始化。这是由于CollapsableTableView也能够以编程方式构造,而不是由西布.
  •  2012/02/12
    •  正如magikcm在注释中所建议的那样,我已经实现了对展开节时行插入的优化。我是经过实施“不诚实的代理数据源”策略来作到这一点的。这个职位.
  •  2012/08/10
    •  之前,当表视图须要知道标头视图的高度时,可折叠表视图实际上将建立该标头,以肯定并返回其高度。当表视图须要从新计算其总高度时,这会形成很大的延迟,由于在此过程当中会查询全部标头的高度。例如,当扩展或折叠一个区段时,就会发生这种状况。这一延误在许多款次中尤其明显。

可折叠表视图如今缓存全部标头视图的高度。这实际上消除了在折叠或扩展区段时发生的过分延迟。

    • 美学改进:扩展部分的标题视图的折叠/扩展指示符如今显示较长的破折号字符,而不是常规的短破折号字符。
  •  2012/11/14
    • 当区段为空时,未显示其折叠/展开指示符。
    • 已折叠和展开的指示符如今能够经过collapsedIndicator和expandedIndicator.的性质CollapsableTableView.
  •  2012/12/23
    •  若是客户端同时为标题提供视图和标题(经过实现tableView:viewForHeaderInSection:和tableView:titleForHeaderInSection:),则当为该节返回的视图未被选中时,视图是首选的。nil.页脚的处理方式相似。这种行为与UITableView已实现。
    •  增长了对节页脚的支持。
    •  修正了当最后一个部分不包含任何行而且被点击时发生的崩溃。
    •  当区段折叠或展开所需时间超过0.5秒时,活动指示符将出如今标头视图中。(呜呜!使用多线程进行用户界面编程是很棘手的!)
    •  能够经过设置showBusyIndicator属性(默认为打开)。若要在自定义标头视图中启用此行为,请添加UIActivityIndicatorView值为BUSY_INDICATOR_TAG(定义为CollapsableTableView.h如37).
    •  A CollapsableTableViewDelegate能够分配给CollapsableTableView,这样,每当某个区段开始折叠或展开时,或当它完成折叠或展开时,均可以通知委托。
    •  新财产sectionsInitiallyCollapsed的CollapsableTableView若是新区段最初是否折叠,则控制。默认值是NO.
  •  2013/01/18
    •  修正了在scrollToRowAtIndexPath:…,或selectRowAtIndexPath:…,或deselectRowAtIndexPath:…在折叠部分中的一行被调用。
    •  修正了上面提到的方法,以便在使用动画=调用它们时YES,效果将是即时的(同步的)。
    •  修正了在折叠和扩展部分时偶尔发生的崩溃。
    •  实现了删除-行优化。因为这一点,具备大量行的折叠部分如今应该更快了。
    •  CollapsableTableView已与存储板中的静态单元格兼容。
  •  2013/02/10
    •  修正了双击大截面的头部时发生的崩溃.
  • 2013/09/08
    •  修正了使用自定义标头视图时在IOS 6.1旋转时发生的崩溃。
    •  启用即时编程行-选择或在区段展开上滚动行。
  •  2013/09/23
    • 修正了在将行添加到空部分后未显示折叠/展开指示符的错误。
  •  2013/10/26
    • 在iOS 7或更高版本上运行时,可折叠表视图如今使用单独的XIB文件做为表各节的头和页脚,以适应IOS 7的新外观。

翻译地址:https://www.codeproject.com/Articles/240435/Collapsable-Table-View-for-iOS

相关文章
相关标签/搜索