有没有觉得UITableView自带的右侧索引很难用,我一直觉得WindowsPhone中的列表索引非常好用。

所以呢,我们来实现类似Windows Phone中的列表索引(这就是信仰)。

最终实现效果图:

1.完整的首字母索引                                                     2.Header名称索引

         

 

想法:这个控件是该继承UITableView还是UIView,抑或其他的呢?

想要写的这个控件,只是在UITableView的基础上增加-点击HeaderView事件-来弹出索引。

弹出索引的大小为控件的大小,并直接添加到父视图中。

所以觉得直接继承UITableView会更加方便,而继承UIView虽然说在写法上更简单些,但是总觉得不太好。

碰巧看到一个实现UITableView动画的例子,于是参照着实现了HeaderView的点击事件。

https://github.com/applidium/ADLivelyTableView

原理是通过增加一个中间代理,只在控件中实现了willDisplayCell协议来控制滑动时的动画,

如果在VC中使用控件时也实现了该协议,则让控件中的代理发送该消息给VC。这样的话相当

于UITableView的实现部分都不变,动画都交给控件来实现。

-------------------------------------我是分割线----------------------------------------------

1.继承UITableView   -   增加中间代理

普通流程:UIViewController<UITableViewDelegate>    --->   UITableView

tableView.delegate = self;

控件流程:继承UITableView :  YFMetroListBox : UITableView

     增加私有属性:       id<UITableViewDelegate> _selfDelegate;

     UIViewController<UITableViewDelegate>    --->   YFMetroListBox<UITableViewDelegate>         --->         UITableView

  tableView.delegate = self;                                  重写YFMetroListBox代理见下面代码

重写YFMetroListBox设置代理的方法:(这个是继承自UITableView的属性)

-(void)setDelegate:(id<UITableViewDelegate>)delegate{//这里的形参即VC对象
_selfDelegate = delegate;  //让本类(YFMetroListBox)对VC进行监听
[super setDelegate:self];  //让父类(UITableView)对子类的监听
}
//重写方法: YFMetroListBox或者父类是否实现了协议
- (BOOL)respondsToSelector:(SEL)aSelector {
return [super respondsToSelector:aSelector] || [_selfDelegate respondsToSelector:aSelector];
}
//转发消息
- (void)forwardInvocation:(NSInvocation *)anInvocation
{
  if ([_selfDelegate respondsToSelector:[anInvocation selector]]) {
    [anInvocation invokeWithTarget:_selfDelegate];
  } else {
    [super forwardInvocation:anInvocation];
  }
}

2.YFMetroListBox实现willDisplayHeaderView协议,增加添加HeaderView的点击手势

弯路1 :刚开始想使用viewForHeaderInSection添加自定义View,之后添加手势,

但是这样的话就得在此HeaderView内容,因此直接在初始化的时候传入了NSArray(感觉不好,但没其他方案)。

除了上述协议,还有titleForHeaderInSection也是能够直接设置默认的标题内容的,那么如何获取headerView?

再者,上面两个协议都是UITableViewDataSource中的,要在控件中这样做会更加的混乱了

解决 :不管VC中如何设置标题,在HeaderView将要显示的时候,添加手势就行了

#pragma mark -m UITableViewDelegate
- (void)tableView:(UITableView *)tableView willDisplayHeaderView:(UIView *)view forSection:(NSInteger)section{
  //如果VC也实现了该协议,则发送此消息
  if([_selfDelegate respondsToSelector:@selector(tableView:willDisplayHeaderView:forSection:)]){
[_selfDelegate tableView:tableView willDisplayHeaderView:view forSection:section];
}
  //增加手势  
UITapGestureRecognizer *tapRegesture = [[UITapGestureRecognizer alloc] initWithTarget:self
     action:@selector(tapHeaderViewInSection)];
view.gestureRecognizers = @[tapRegesture];
}

3. 在YFMetroListBox中获取标题内容数组

弯路2 : 存在headerViewForSection:方法获取某一section的headerView。由此想到,

既然在VC中设置好了头标题,那么我肯定可以在内部动态获取全部标题。(错误的想法)

-(NSArray *)getHeaderViewTextArray{
NSMutableArray *array = [NSMutableArray array];
for (int i = ; i < [self numberOfSections]; i++) {//循环section数
NSArray *subviews = [[self headerViewForSection:i] subviews];//取得UIlabel的内容
for (id subview in subviews) {
if([subview isKindOfClass:[UILabel class]]){
UILabel *label = (UILabel *)subview;
[array addObject:label.text];
break;
}
}
}
return array;
}

但是通过这个方法获取的的始终只有屏幕中的可见部分的头标题,而不是全部的头标题。

原因:因为使用了重用机制,只有显示的部分被创建了,所以想要获取全部的内容

是不可能的。这类问题 应该从数据源触发。

 解决 :像UITableViewDataSource协议中sectionIndexTitlesForTableView一样通过协议来实现。

自定义一个协议,在VC中实现该协议,给YFMetroListBox头标题数组数据。

@protocol YFMetroListBoxDelegate <NSObject>
@required
- (NSArray<NSString *> *)sectionIndexTitlesForYFMetroListBox:(YFMetroListBox *)metroListBox;
//返回的标题头数组有两种格式 1.自定义标题头 2.("#ABCD...Z@"),#表示数字 @暂时表示除了数字 字母 英文 以外的字符。
@end

具体的将一组包含数字、中文、英文、符号的数据分组排序成#ABC...Z✿"格式我们在下一篇随笔给出。

[已经写好了,2016.05.30]【IOS】将一组包含中文的数据按照#ABC...Z✿分组

4. 画出点击后界面

界面有两种,通过metroListBoxType枚举属性来进行设置

分别对应最上面的两幅图。这点没什么说的,通过上一点获取标题头数组。来展示。

如果为YFMetroListBoxTypeAllAlphabet,则将#.A.B.C...Z.@全部画出,给标题头数组存在的添加点击事件。

点击后通过 [self scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:section]

atScrollPosition:UITableViewScrollPositionTop animated:NO]进行跳转。

5.重写  插入、删除、重新载入数据等方法

到目前为止  基本上就做好了。只不过在添加更改数据源之后进行reloadData、

或者对section进行insertSections、deleteSections、moveSection等操作,索引列表是不会改变的。

因此要重写他们,手动的调用更新索引界面的方法。

//Data
-(void)reloadData{
[self updateZoomOutView];
[super reloadData];
}
-(void)reloadSectionIndexTitles{
[self updateZoomOutView];
[super reloadSectionIndexTitles];
}
// only call insert/delete/reload calls or change the editing state inside an update block. otherwise things like row count, etc. may be invalid.
- (void)insertSections:(NSIndexSet *)sections withRowAnimation:(UITableViewRowAnimation)animation{
[self updateZoomOutView];
[super insertSections:sections withRowAnimation:animation];
}
- (void)deleteSections:(NSIndexSet *)sections withRowAnimation:(UITableViewRowAnimation)animation{
[self updateZoomOutView];
[super deleteSections:sections withRowAnimation:animation];
}
- (void)reloadSections:(NSIndexSet *)sections withRowAnimation:(UITableViewRowAnimation)animation{
[self updateZoomOutView];
[super reloadSections:sections withRowAnimation:animation];
}
- (void)moveSection:(NSInteger)section toSection:(NSInteger)newSection{
[self updateZoomOutView];
[super moveSection:section toSection:newSection];
}

这样就大功告成了。

GitHub地址: YFMetroListBox

【IOS】模仿windowsphone列表索引控件YFMetroListBox的更多相关文章

  1. 做个简单的Android列表字母索引控件

    相信大家在许多App中都见到过带字母索引的界面,比如我最近看到的这个开源控件: WaveSideBar 很酷是不是?!!!如果加在例如联系人列表界面上,大大提升了用户体验. 那么这个索引控件要怎么做呢 ...

  2. 【IOS 开发】基本 UI 控件详解 (UISegmentedControl | UIImageView | UIProgressView | UISlider | UIAlertView )

    转载请注明出处 : http://blog.csdn.net/shulianghan/article/details/50163725 一. 分段控件 (UISegmentedControl) 控件展 ...

  3. MFC编程入门之二十四(常用控件:列表框控件ListBox)

    前面两节讲了比较常用的按钮控件,并通过按钮控件实例说明了具体用法.本文要讲的是列表框控件(ListBox)及其使用实例. 列表框控件简介 列表框给出了一个选项清单,允许用户从中进行单项或多项选择,被选 ...

  4. LabVIEW(十六):多列列表框控件

    1.多列列表框控件:前面板右键>列表.表格和树>多列列表框2.默认情况下只显示列首,可设置显示行首:前面板选中多列列表框右键>显示>行首3.LabVIEW中提供42种自带的图标 ...

  5. VS2010/MFC编程入门之二十九(常用控件:列表视图控件List Control 下)

    上一节是关于列表视图控件List Control的上半部分,简单介绍了列表视图控件,其通知消息的处理和有关结构体的定义.本节继续讲解下半部分,包括列表视图控件的创建.CListCtrl类的主要成员函数 ...

  6. VS2010/MFC编程入门之二十八(常用控件:列表视图控件List Control 上)

    前面一节中,鸡啄米讲了图片控件Picture Control,本节为大家详解列表视图控件List Control的使用.      列表视图控件简介 列表视图控件List Control同样比较常见, ...

  7. VS2010/MFC编程入门之二十四(常用控件:列表框控件ListBox)

    前面两节讲了比较常用的按钮控件,并通过按钮控件实例说明了具体用法.本文要讲的是列表框控件(ListBox)及其使用实例. 列表框控件简介 列表框给出了一个选项清单,允许用户从中进行单项或多项选择,被选 ...

  8. win32 sdk列表视图控件(ListCtrl或ListView)资料整理

    列表视图控件是一种非常常用的控件,在需要以报表形式显示数据时,列表控件通常是最好的选择,许多专用的数据报表控件,也是在它的基础上派生而来.与树视图类似,列表控件可以由多个子项目组成,可以设置为Icon ...

  9. MFC编程入门之二十八(常用控件:列表视图控件List Control上)

    前面一节中,讲了图片控件Picture Control,本节为大家详解列表视图控件List Control的使用. 列表视图控件简介 列表视图控件List Control同样比较常见,它能够把任何字符 ...

随机推荐

  1. 如何快速清除ZBrush画布中多余图像

    ZBrush是一款数字雕刻与绘画软件,它以强大的功能和直观的工作流程彻底改变了整个三维行业.它的简洁化.智能化和人性化的设计无不让众多用户所折服.刚接触它的用户可能会因为找不到相关命令或不熟悉而觉得它 ...

  2. BZOJ 1901: Zju2112 Dynamic Rankings[带修改的主席树]【学习笔记】

    1901: Zju2112 Dynamic Rankings Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 7143  Solved: 2968[Su ...

  3. WinForm TreeView递归加载

    这个其实通俗一点讲就是的树状分支图 首先利用递归添加数据 数据放入 treeView1.Nodes.Add() 中 public Form3() { InitializeComponent(); Tr ...

  4. Could not load type 'System.Web.Mvc.ViewPage<dynamic>' in asp.net mvc2 after publishing the website

    在WebConfig里 找到 <pages></pages> <pages pageParserFilterType="System.Web.Mvc.ViewT ...

  5. [LeetCode] One Edit Distance 一个编辑距离

    Given two strings S and T, determine if they are both one edit distance apart. 这道题是之前那道Edit Distance ...

  6. C#进阶系列——WebApi 身份认证解决方案:Basic基础认证

    前言:最近,讨论到数据库安全的问题,于是就引出了WebApi服务没有加任何验证的问题.也就是说,任何人只要知道了接口的url,都能够模拟http请求去访问我们的服务接口,从而去增删改查数据库,这后果想 ...

  7. 彻底搞懂编码 GBK 和 UTF8

    常用编码格式一览 首先来看一下常用的编码有哪些,截图自Notepad++.其中ANSI在中国大陆即为GBK(以前是GB2312),最常用的是 GBK 和 UTF8无BOM 编码格式.后面三个都是有BO ...

  8. 关于#define for if(false);else for

    今日在看一个第三方代码时看到了#define for if(false);else for 这样的一种定义,不明白这样用法的目的,于是查了一下. 这是一个兼容vc6.0的用法,csdn上有这个问题的回 ...

  9. C与指针(结构体指针,函数指针,数组指针,指针数组)定义与使用

    类型 普通指针 指针数组(非指针类型) 数组指针 结构体指针 函数指针 二重指针 定义方式 int *p; int *p[5]; int (*p)[5]; int a[3][5]; struct{.. ...

  10. sql server生成递归日期

    WITH Date AS ( SELECT CAST('2008-08-01' AS DATETIME) da UNION ALL FROM Date WHERE da < '2008-08-2 ...