美文网首页
iOS实现类似通讯录的字母滚动功能

iOS实现类似通讯录的字母滚动功能

作者: 小立哥 | 来源:发表于2021-08-19 11:20 被阅读0次

    实现效果

    111.png

    实现思路

    初始化字母表数组

    NSArray *charArr = @[@"A",@"B",@"C",@"D",@"E",@"F",@"G",@"H",@"I",@"J",@"K",@"L",@"M",@"N",@"O",@"P",@"Q",@"R",@"S",@"T",@"U",@"V",@"W",@"X",@"Y",@"Z",@"#"];
    _letterDataArray = [[NSMutableArray alloc] init];
        [charArr enumerateObjectsUsingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
            LetterWordItemModel *item = [[LetterWordItemModel alloc] init];
            item.content = obj;
            [_letterDataArray addObject:item];
        }];
    

    把字符串转成LetterWordItemModel,增加一个是否选中的属性,方便于后续控制字母表的显示样式。

    @interface LetterWordItemModel : NSObject
    @property (nonatomic,strong) NSString *content;
    @property (nonatomic,assign) BOOL isCheck;//是否被选中
    @end
    

    初始化内容的数据源数组

    @property (nonatomic,strong) NSArray<NSArray<LetterTableItemModel *> *> *dataArray;//内容数组
    
    @interface LetterTableItemModel : NSObject
    @property (nonatomic,strong) NSString *content;
    @property (nonatomic,strong) NSString *firstChar;
    @end
    

    tableView是一个分组的列表,需要的数据源为数组内层嵌套一个数组的形式。
    LetterTableItemModel其中这个类至少需要如上两个属性。
    content用于显示内容,firstChar用于存储字符串的第一个字的第一个拼音字母,根据此字段来为数组分组。
    可以根据自身需求灵活给该类增加字段,比如电话号码等等属性。

    NSMutableString *pinyin = [[NSMutableString alloc] initWithString:obj.content];
    CFStringTransform((__bridge CFMutableStringRef)pinyin, NULL, kCFStringTransformMandarinLatin, NO);
    CFStringTransform((__bridge CFMutableStringRef)pinyin, NULL, kCFStringTransformStripDiacritics, NO);
    obj.firstChar = [[pinyin substringToIndex:1] uppercaseString];
    

    用CFStringTransform方法来获取字符串的第一个拼音字母,并且统一转换成大写。

    初始化tableView。

    _tableView = [[UITableView alloc] initWithFrame:CGRectMake(0, 0, CGRectGetWidth(self.frame), CGRectGetHeight(self.frame))];//确定内容列表的位置和大小
    [self addSubview:_tableView];
    _tableView.dataSource = self; _tableView.delegate = self;
    [_tableView registerNib:[UINib nibWithNibName:@"ContentTableViewCell" bundle:nil] forCellReuseIdentifier:contentCellIdentifier];
    

    初始化右边竖向字母tableView。

    _letterTableView = [[UITableView alloc] initWithFrame:CGRectMake([[UIScreen mainScreen] bounds].size.width - letterTableViewWidth, [[UIScreen mainScreen] bounds].size.height/2 - letterTableViewCellHeight * _letterDataArray.count/2, letterTableViewWidth, letterTableViewCellHeight * _letterDataArray.count)];//确定字母表的位置和大小
    [self addSubview:_letterTableView]; 
    _letterTableView.dataSource = self;
    _letterTableView.delegate = self;
    [_letterTableView registerNib:[UINib nibWithNibName:@"LetterTableViewCell" bundle:nil] forCellReuseIdentifier:letterCellIdentifier];
    _letterTableView.separatorStyle = UITableViewCellSeparatorStyleNone;
    _letterTableView.backgroundColor = [UIColor clearColor];
     _letterTableView.bounces = NO;
    

    给字母右边的字母tableView添加一个滑动手势。

    //给字母表添加拖动手势
    UIPanGestureRecognizer *letterPan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(letterPan:)];
    [_letterTableView addGestureRecognizer:letterPan];
    

    分别实现两个tableView的DataSource。

    - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
        if (tableView == _tableView) {
            return _dataArray.count;
        }else {
            return 1;
        }
        
    }
    
    - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
        if (tableView == _tableView) {
            return ((NSArray *)_dataArray[section]).count;
        }else {
            return _letterDataArray.count;
        }
        
    }
    
    - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
        if (tableView == _tableView) {
            ContentTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:contentCellIdentifier];
            cell.contentLabel.text = _dataArray[indexPath.section][indexPath.row].content;
            return cell;
        }else {
            LetterTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:letterCellIdentifier];
            cell.letterWordLabel.text = _letterDataArray[indexPath.row].content;
            //配置字母表字母选中样式
            if (_letterDataArray[indexPath.row].isCheck) {
                cell.letterWordLabel.textColor = [UIColor whiteColor];
                cell.letterWordLabel.backgroundColor = [UIColor colorWithRed:0x56/255.0f green:0x70/255.0f blue:0xfe/255.0f alpha:1.0f];
            }else {
                cell.letterWordLabel.textColor = [UIColor colorWithRed:0xa2/255.0f green:0xa7/255.0f blue:0xc7/255.0f alpha:1.0f];
                cell.letterWordLabel.backgroundColor = [UIColor clearColor];
            }
            return cell;
        }
    }
    

    分别实现两个tableView的delegate。
    包括配置cell高度,sectionheaderView的高度和样式。
    两个tableView的点击事件。

    #pragma mark - UITableViewDelegate
    //配置两个tableView的cell高度
    - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
        if (tableView == _tableView) {
            return contentTableViewCellHeight;
        }else {
            return letterTableViewCellHeight;
        }
    }
    
    //配置sectionHeaderView的高度,字母表不需要sectionHeaderView
    - (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section {
        if (tableView == _tableView) {
            return contentTableViewSectionHeaderHeight;
        }else {
            return 0.000001;
        }
    }
    
    //配置sectionHeader的样式
    - (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
        if (tableView == _tableView) {
            UIView *view = [[UIView alloc] initWithFrame:CGRectMake(0, 0, [[UIScreen mainScreen] bounds].size.width, contentTableViewSectionHeaderHeight)];
            view.backgroundColor = [UIColor colorWithRed:0xf2/255.0f green:0xf2/255.0f blue:0xf2/255.0f alpha:1.0f];
            
            UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(20, 0, [[UIScreen mainScreen] bounds].size.width, contentTableViewSectionHeaderHeight)];
            label.font = [UIFont systemFontOfSize:14];
            label.textColor = [UIColor colorWithRed:0xa2/255.0f green:0xa7/255.0f blue:0xc7/255.0f alpha:1.0f];
            [view addSubview:label];
            if (_dataArray.count > section) {
                if (((NSArray *)_dataArray[section]).count > 0) {
                    LetterTableItemModel *item = _dataArray[section][0];
                    label.text = item.firstChar;
                }
            }
            
            return view;
        }else {
            return [UIView new];
        }
    }
    
    //分别实现两个tableView的点击事件
    - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
        if (tableView == _tableView) {
            //点击cell时调用,传入当前cell的model
            [self.delegate clickItem:_dataArray[indexPath.section][indexPath.row]];
        }else {
            //更新字母表选中状态
            [_letterDataArray enumerateObjectsUsingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
                LetterWordItemModel *item = obj;
                if (idx == indexPath.row) {
                    item.isCheck = YES;
                }else {
                    item.isCheck = NO;
                }
            }];
            [_letterTableView reloadData];
            //滚动到选中字母的位置
            [_dataArray enumerateObjectsUsingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
                NSMutableArray *arr = obj;
                if (arr.count > 0) {
                    LetterTableItemModel *item = arr[0];
                    //如果选中的字母,列表中没有 则不动
                    if ([item.firstChar isEqualToString:[((LetterWordItemModel *)_letterDataArray[indexPath.row]).content uppercaseString]]) {
                        [_tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:idx] atScrollPosition:UITableViewScrollPositionTop animated:NO];
                        *stop = YES;
                    }
                    
                }
            }];
        }
    }
    

    实现tableView的滑动协议,来实现滚动内容时,字母表能够及时的改变选中状态。

    //监听列表滑动事件
    - (void)scrollViewDidScroll:(UIScrollView *)scrollView {
        if (scrollView == _tableView) {
            //获取当前显示的section是哪个
            NSArray <UITableViewCell *> *cellArray = [self.tableView visibleCells];
            NSInteger nowSection = -1;
            if (cellArray) {
                UITableViewCell *cell = [cellArray firstObject];
                NSIndexPath *indexPath = [self.tableView indexPathForCell:cell];
                nowSection = indexPath.section;
            }
            //同时更改字母表选中状态
            if (_dataArray.count > 0) {
                if (((NSMutableArray *)_dataArray[nowSection]).count > 0) {
                    LetterTableItemModel *item = _dataArray[nowSection][0];
                    [_letterDataArray enumerateObjectsUsingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
                        LetterWordItemModel *o = obj;
                        if ([item.firstChar isEqualToString:o.content]) {
                            o.isCheck = YES;
                        }else {
                            o.isCheck = NO;
                        }
                    }];
                    [_letterTableView reloadData];
                }
            }
    
        }
    }
    

    初始化一个滑动字母表的提示View,可根据需求自定义一个。

    _letterTipsView = [[LetterTipsView alloc] init];
        [self addSubview:_letterTipsView];
    

    实现字母表滑动手势的方法。

    //滑动手势触发的方法
    - (void)letterPan:(UIPanGestureRecognizer *)pan {
        NSInteger state = pan.state;
        if (pan.state == UIGestureRecognizerStateBegan) {
            
        }else if (pan.state == UIGestureRecognizerStateChanged) {
            //滑动中时,获取当前手指所在位置
            CGPoint p = [pan locationInView:_letterTableView];
            NSInteger index = (NSInteger)p.y / letterTableViewCellHeight;
            //确保滑动的位置不超过最后一行
            if (index > _letterDataArray.count - 1) {
                index = _letterDataArray.count - 1;
            }
            //确保滑动的位置不小于第一行
            if (index < 0) {
                index = 0;
            }
            //根据手指位置,更改字母表选中状态
            [_letterDataArray enumerateObjectsUsingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
                LetterWordItemModel *item = obj;
                if (idx == index) {
                    item.isCheck = YES;
                }else {
                    item.isCheck = NO;
                }
            }];
            [_letterTableView reloadData];
            //滑动列表到指定位置
            [_dataArray enumerateObjectsUsingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
                NSMutableArray *arr = obj;
                if (arr.count > 0) {
                    LetterTableItemModel *item = arr[0];
                    //如果选中的字母没有对应的内容 则不动
                    if ([item.firstChar isEqualToString:((LetterWordItemModel *)_letterDataArray[index]).content]) {
                        [_tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:idx] atScrollPosition:UITableViewScrollPositionTop animated:NO];
                        *stop = YES;
                    }
                    
                }
            }];
            //在滑动时,显示提示当前选中的是哪个字母的提示view
            _letterTipsView.contentLabel.text = ((LetterWordItemModel *)_letterDataArray[index]).content;
            //确定提示view的显示位置
            _letterTipsView.frame = CGRectMake(CGRectGetMinX(_letterTableView.frame)-62, CGRectGetMinY(_letterTableView.frame) + index * letterTableViewCellHeight - 26 + letterTableViewCellHeight/2, 62, 52);
            _letterTipsView.contentLabel.frame = CGRectMake(0, 0, CGRectGetWidth(_letterTipsView.frame), CGRectGetHeight(_letterTipsView.frame));
        }else {
            //手指离开时,隐藏提示View
            _letterTipsView.frame = CGRectMake(0, 0, 0, 0);
        }
    }
    

    调用方式

     _letterView = [[LetterTableView alloc] initWithFrame:CGRectMake(0, 88, CGRectGetWidth([UIScreen mainScreen].bounds), CGRectGetHeight([UIScreen mainScreen].bounds) - 88) dataArray:dataArray delegate:self];
    [self.view addSubview:_letterView];
    

    设置好frame,添加到页面上,即可完成调用。
    实现LetterTableViewDelegate协议。来监听点击cell的回调。

    - (void)clickItem:(LetterTableItemModel *)item {
        NSLog(@"%@",item.content);
    }
    

    具体实现demo可以参考https://github.com/zjl0624/LetterTableView

    注意事项

    传入的tableView的dataSource数据源数组必须为LetterTableItemModel或者他的子类。并且必须是Array嵌套一个Array的方式,控件才能正确配置cell和section。

    相关文章

      网友评论

          本文标题:iOS实现类似通讯录的字母滚动功能

          本文链接:https://www.haomeiwen.com/subject/mlmobltx.html