仿微信選擇聯(lián)系人列表

之前項(xiàng)目里有做選擇聯(lián)系人列表洁仗,于是仿照微信把功能模塊抽出來(lái)寫(xiě)了個(gè)demo茄猫。

索引的處理

索引.gif
  • 聯(lián)系人列表根據(jù)首字母排序并獲取索引列表
/**
 聯(lián)系人數(shù)組排序
 
 @param array 原始聯(lián)系人數(shù)組數(shù)據(jù)
 @return 排序后的聯(lián)系人數(shù)組
 */
+ (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;
}

/**
 獲取分區(qū)數(shù)(姓氏首字母)

 @param array 排序后的聯(lián)系人數(shù)組
 @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;
}
  • 點(diǎn)擊索引列表時(shí)顯示tipView瘩绒,cell背景變?yōu)榫G色,聯(lián)系人列表滾動(dòng)到對(duì)應(yīng)的section贬堵。
// indexTableView didSelectRow
    self.isScrollToShow = NO;// 這個(gè)屬性后面會(huì)講到
    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];
}
  • 聯(lián)系人列表滾動(dòng)時(shí)恃轩, 索引列表要滾動(dòng)到對(duì)應(yīng)的字母
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {

    if (self.isScrollToShow) { // 判斷self.isScrollToShow,為NO時(shí)不顯示tipView
        // 獲取當(dāng)前屏幕可見(jiàn)范圍的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) {
            //在最底部(顯示最后一個(gè)索引字母)
            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:];這個(gè)方法同時(shí)會(huì)觸發(fā)- (void)scrollViewDidScroll:(UIScrollView *)scrollView黎做,這個(gè)時(shí)候索引列表的UI會(huì)出現(xiàn)bug叉跛。因此設(shè)置屬性self.isScrollToShow,默認(rèn)值為YES引几,在點(diǎn)擊索引時(shí)設(shè)為NO昧互,不處理tipView顯示,在滾動(dòng)開(kāi)始和結(jié)束時(shí)重置伟桅。
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView {
    // 重置
    if (!self.isScrollToShow) {
        self.isScrollToShow = YES;
    }
}
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
    // 重置
    if (!self.isScrollToShow) {
        self.isScrollToShow = YES;
    }
}

頭部View選擇聯(lián)系人UI處理

頭部view選擇.gif
  • 頭部封裝一個(gè)包含collectionView和textField的view
@protocol SelectMemberWithSearchViewDelegate <NSObject>
// 點(diǎn)擊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;

// 當(dāng)選中人數(shù)發(fā)生改變時(shí) 更改collection view UI
- (void)updateSubviewsLayout:(NSMutableArray *)selelctArray;

@end
  • 點(diǎn)擊聯(lián)系人列表cell敞掘,刷新選中狀態(tài)并更新頭部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
    }
}
  • 點(diǎn)擊頭部collectionView的cell,實(shí)現(xiàn)代理的方法楣铁,取消選中狀態(tài)并更新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
        }
    }];
}

關(guān)于搜索玖雁,這里偷個(gè)懶不想寫(xiě)了。思路都一樣盖腕,判斷cell的選中狀態(tài)并更新列表赫冬,同時(shí)刷新頭部View浓镜。后期有時(shí)間的話(huà)再更新

????I'm the demo。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末劲厌,一起剝皮案震驚了整個(gè)濱河市膛薛,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌补鼻,老刑警劉巖哄啄,帶你破解...
    沈念sama閱讀 222,807評(píng)論 6 518
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異风范,居然都是意外死亡咨跌,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,284評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門(mén)硼婿,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)锌半,“玉大人,你說(shuō)我怎么就攤上這事寇漫】常” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 169,589評(píng)論 0 363
  • 文/不壞的土叔 我叫張陵州胳,是天一觀(guān)的道長(zhǎng)冗澈。 經(jīng)常有香客問(wèn)我,道長(zhǎng)陋葡,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 60,188評(píng)論 1 300
  • 正文 為了忘掉前任彻采,我火速辦了婚禮腐缤,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘肛响。我一直安慰自己岭粤,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,185評(píng)論 6 398
  • 文/花漫 我一把揭開(kāi)白布特笋。 她就那樣靜靜地躺著剃浇,像睡著了一般。 火紅的嫁衣襯著肌膚如雪猎物。 梳的紋絲不亂的頭發(fā)上虎囚,一...
    開(kāi)封第一講書(shū)人閱讀 52,785評(píng)論 1 314
  • 那天,我揣著相機(jī)與錄音蔫磨,去河邊找鬼淘讥。 笑死,一個(gè)胖子當(dāng)著我的面吹牛堤如,可吹牛的內(nèi)容都是我干的蒲列。 我是一名探鬼主播窒朋,決...
    沈念sama閱讀 41,220評(píng)論 3 423
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼蝗岖!你這毒婦竟也來(lái)了侥猩?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 40,167評(píng)論 0 277
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤抵赢,失蹤者是張志新(化名)和其女友劉穎欺劳,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體瓣俯,經(jīng)...
    沈念sama閱讀 46,698評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡杰标,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,767評(píng)論 3 343
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了彩匕。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片腔剂。...
    茶點(diǎn)故事閱讀 40,912評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖驼仪,靈堂內(nèi)的尸體忽然破棺而出掸犬,到底是詐尸還是另有隱情,我是刑警寧澤绪爸,帶...
    沈念sama閱讀 36,572評(píng)論 5 351
  • 正文 年R本政府宣布湾碎,位于F島的核電站,受9級(jí)特大地震影響奠货,放射性物質(zhì)發(fā)生泄漏介褥。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,254評(píng)論 3 336
  • 文/蒙蒙 一递惋、第九天 我趴在偏房一處隱蔽的房頂上張望柔滔。 院中可真熱鬧,春花似錦萍虽、人聲如沸睛廊。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,746評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)超全。三九已至,卻和暖如春邓馒,著一層夾襖步出監(jiān)牢的瞬間嘶朱,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,859評(píng)論 1 274
  • 我被黑心中介騙來(lái)泰國(guó)打工光酣, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留见咒,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 49,359評(píng)論 3 379
  • 正文 我出身青樓挂疆,卻偏偏與公主長(zhǎng)得像改览,于是被迫代替她去往敵國(guó)和親下翎。 傳聞我的和親對(duì)象是個(gè)殘疾皇子宝当,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,922評(píng)論 2 361

推薦閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,334評(píng)論 25 707
  • 發(fā)現(xiàn) 關(guān)注 消息 iOS 第三方庫(kù)视事、插件、知名博客總結(jié) 作者大灰狼的小綿羊哥哥關(guān)注 2017.06.26 09:4...
    肇東周閱讀 12,124評(píng)論 4 61
  • 佛陀已經(jīng)把熄滅怒火的工具傳授給我們,那就是:念念分明地呼吸與走路订晌、擁抱憤怒虏辫、深層地觀(guān)照認(rèn)識(shí)的本質(zhì),以及深入地看到對(duì)...
    春寒1215閱讀 360評(píng)論 0 0
  • 吃飽喝足锈拨,沿海邊沙灘踏浪漫行至香水灣砌庄,見(jiàn)園林寂寂、門(mén)館幽幽奕枢,除了幾個(gè)員工娄昆,別無(wú)游客,安靜寧謐缝彬。綠化景觀(guān)極佳萌焰,細(xì)草蒼...
    雪夜書(shū)蟲(chóng)李老師閱讀 364評(píng)論 2 4
  • 我的孩子還差一個(gè)多月就兩歲了,對(duì)谷浅,你沒(méi)聽(tīng)錯(cuò)扒俯,我要向一個(gè)一歲多的寶寶學(xué)習(xí)。 1. 剛出生的寶寶除了會(huì)吃奶一疯,他還會(huì)哭陵珍,...
    笨學(xué)徒閱讀 531評(píng)論 0 1