之前项目里有做选择联系人列表,于是仿照微信把功能模块抽出来写了个demo。
索引的处理
/**
联系人数组排序
@param array 原始联系人数组数据
@return 排序后的联系人数组
*/
+ (NSMutableArray *) getContactListDataBy:(NSMutableArray *)array{
NSMutableArray *ans = [[NSMutableArray alloc] init];
NSArray *serializeArray = [(NSArray *)array sortedArrayUsingComparator:^NSComparisonResult(id obj1, id obj2) {//排序
int i;
NSString *strA = [((ConatctModel *)obj1).name transformCharacter];// transformCharacter:获取姓氏首字母
NSString *strB = [((ConatctModel *)obj2).name transformCharacter];
for (i = 0; i < strA.length && i < strB.length; i ++) {
char a = [strA characterAtIndex:i];
char b = [strB characterAtIndex:i];
if (a > b) {
return (NSComparisonResult)NSOrderedDescending;//上升
}
else if (a < b) {
return (NSComparisonResult)NSOrderedAscending;//下降
}
}
if (strA.length > strB.length) {
return (NSComparisonResult)NSOrderedDescending;
}else if (strA.length < strB.length){
return (NSComparisonResult)NSOrderedAscending;
}else{
return (NSComparisonResult)NSOrderedSame;
}
}];
char lastC = '1';
NSMutableArray *data;
NSMutableArray *oth = [[NSMutableArray alloc] init];
for (ConatctModel *contact in serializeArray) {
char c = [[contact.name transformCharacter] characterAtIndex:0];
if (!isalpha(c)) {
[oth addObject:contact];
}
else if (c != lastC){
lastC = c;
if (data && data.count > 0) {
[ans addObject:data];
}
data = [[NSMutableArray alloc] init];
[data addObject:contact];
}
else {
[data addObject:contact];
}
}
if (data && data.count > 0) {
[ans addObject:data];
}
if (oth.count > 0) {
[ans addObject:oth];
}
return ans;
}
/**
获取分区数(姓氏首字母)
@param array 排序后的联系人数组
@return [A,B,C,D.....]
*/
+ (NSMutableArray *)getContactListSectionBy:(NSMutableArray *)array {
NSMutableArray *section = [[NSMutableArray alloc] init];
[section addObject:UITableViewIndexSearch]; // 索引栏最上方的搜索icon(可加可不加)
for (NSArray *item in array) {
ConatctModel *model = [item objectAtIndex:0];
char c = [[model.name transformCharacter] characterAtIndex:0];
if (!isalpha(c)) {
c = '#';
}
[section addObject:[NSString stringWithFormat:@"%c", toupper(c)]];
}
return section;
}
-
点击索引列表时显示tipView,cell背景变为绿色,联系人列表滚动到对应的section。
// indexTableView didSelectRow
self.isScrollToShow = NO;// 这个属性后面会讲到
self.selectIndex = indexPath.row;
[_indexTableView reloadData];
[_listTableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:indexPath.row-1] atScrollPosition:UITableViewScrollPositionTop animated:YES];
[self showTipViewWithIndex:indexPath];
/**
显示tipView
*/
- (void)showTipViewWithIndex:(NSIndexPath *)indexPath {
CGFloat y = CGRectGetMinY(_indexTableView.frame) + indexPath.row*20;
self.tipViewBtn.frame = CGRectMake(CGRectGetMinX(_indexTableView.frame)-70, y-12, 65, 50);
[self.view addSubview:self.tipViewBtn];
self.tipViewBtn.titleLabel.font = [UIFont systemFontOfSize:24];
NSString *title = _sectionArr[self.selectIndex];
[self.tipViewBtn setTitle:title forState:(UIControlStateNormal)];
[self performSelector:@selector(dismissTipViewBtn) withObject:nil afterDelay:0.5];
}
- (void)dismissTipViewBtn {
[self.tipViewBtn removeFromSuperview];
}
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
if (self.isScrollToShow) { // 判断self.isScrollToShow,为NO时不显示tipView
// 获取当前屏幕可见范围的indexPath
NSArray *visiblePaths = [_listTableView indexPathsForVisibleRows];
if (visiblePaths.count < 1) {
return;
}
NSIndexPath *indexPath0 = visiblePaths[0];
// 判断是否已滑到最底部
CGFloat height = scrollView.frame.size.height;
CGFloat contentOffsetY = scrollView.contentOffset.y;
CGFloat bottomOffset = scrollView.contentSize.height - contentOffsetY;
NSIndexPath *indexPath;
if (bottomOffset <= height || fabs(bottomOffset - height) < 1) {
//在最底部(显示最后一个索引字母)
NSInteger row = _sectionArr.count-1;
indexPath = [NSIndexPath indexPathForRow:row inSection:0];
self.selectIndex = indexPath.row;
}else {
indexPath = [NSIndexPath indexPathForRow:indexPath0.section inSection:0];
self.selectIndex = indexPath.row+1;
}
[_indexTableView reloadData];
}
}
由于[_listTableView scrollToRowAtIndexPath: atScrollPosition: animated:];
这个方法同时会触发- (void)scrollViewDidScroll:(UIScrollView *)scrollView
,这个时候索引列表的UI会出现bug。因此设置属性self.isScrollToShow
,默认值为YES,在点击索引时设为NO,不处理tipView显示,在滚动开始和结束时重置。
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView {
// 重置
if (!self.isScrollToShow) {
self.isScrollToShow = YES;
}
}
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
// 重置
if (!self.isScrollToShow) {
self.isScrollToShow = YES;
}
}
头部View选择联系人UI处理
-
头部封装一个包含collectionView和textField的view
@protocol SelectMemberWithSearchViewDelegate <NSObject>
// 点击collection cell取消选中
- (void)removeMemberFromSelectArray:(ConatctModel *)member
indexPath:(NSIndexPath *)indexPath;
@end
@interface SelectMemberWithSearchView : UIView
@property (nonatomic, weak) id<SelectMemberWithSearchViewDelegate> delegate;
@property (nonatomic, strong) UICollectionView *collectionView;
@property (nonatomic, strong) UITextField *textfield;
// 当选中人数发生改变时 更改collection view UI
- (void)updateSubviewsLayout:(NSMutableArray *)selelctArray;
@end
-
点击联系人列表cell,刷新选中状态并更新头部View的UI
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
[tableView deselectRowAtIndexPath:indexPath animated:YES];
if (tableView == _listTableView) {
ConatctModel *model = _rowArr[indexPath.section][indexPath.row];
SelectContactCell *cell = [tableView cellForRowAtIndexPath:indexPath];
cell.selectedBtn.selected = !cell.selectedBtn.selected;
if (cell.selectedBtn.selected == YES) {
[cell.selectedBtn setImage:[UIImage imageNamed:@"circle_green"] forState:(UIControlStateSelected)];
[self.selectArray addObject:model];
}else {
[cell.selectedBtn setImage:[UIImage imageNamed:@"circle_empty"] forState:(UIControlStateNormal)];
[self.selectArray removeObject:model];
}
[self updateRightBarButtonItem]; // 更改rightBarButtonItem
[self.searchView updateSubviewsLayout:self.selectArray]; //刷新头部view的UI
}
}
-
点击头部collectionView的cell,实现代理的方法,取消选中状态并更新UI
- (void)removeMemberFromSelectArray:(ConatctModel *)member indexPath:(NSIndexPath *)indexPath {
[_contactArray enumerateObjectsUsingBlock:^(ConatctModel * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
if ([obj.name isEqualToString:member.name]) {
[self.selectArray removeObject:obj];
[_listTableView reloadData];
[self updateRightBarButtonItem]; // 更改rightBarButtonItem
}
}];
}
关于搜索,这里偷个懒不想写了。思路都一样,判断cell的选中状态并更新列表,同时刷新头部View。后期有时间的话再更新