CollectionView添加索引

2018-09-21  本文已影响0人  铁头娃_e245

在collectionView列表添加索引条, 因不像tableView的协议自带设置索引功能, 所以collectionView的索引需要自定义, 虽然麻烦点, 但也有更高的扩展性以应对更复(奇)杂(怪)的需求

我们先来看一下完成效果: ①右侧索引条 ②点击/滑动索引条屏幕中心的提示框


索引.gif

需求分解: 先将需求分解成三步

(1)绘制索引条
(2)索引条与collectionView联动效果
(3)制作提示框

这里只介绍几个用到的技术点:

(1)绘制索引条:

使用CAShapeLayerUIBezierPath实现不在view的drawRect方法中就画出想要的图形, 下图是核心绘制代码

UIBezierPath *bezierPath = [UIBezierPath bezierPath];
        [bezierPath moveToPoint:CGPointZero];
        [bezierPath addLineToPoint:CGPointMake(0, self.frame.size.height)];
        
        /*-----绘制文字部分------*/
        _letterHeight = 16;
        CGFloat fontSize = 12;
        [self.titleIndexes enumerateObjectsUsingBlock:^(NSString *obj, NSUInteger idx, BOOL *stop) {
            CGFloat originY = idx * _letterHeight;
            CATextLayer *ctl = [self textLayerWithSize:fontSize
                                                string:obj
                                              andFrame:CGRectMake(0, originY, self.frame.size.width, _letterHeight)];

            [self.layer addSublayer:ctl];
            
            [bezierPath moveToPoint:CGPointMake(0, originY)];
            [bezierPath addLineToPoint:CGPointMake(ctl.frame.size.width, originY)];
        }];
//绘制字体
- (CATextLayer*)textLayerWithSize:(CGFloat)size string:(NSString*)string andFrame:(CGRect)frame{
    CATextLayer *tl = [CATextLayer layer];
    [tl setFont:@"ArialMT"];
    [tl setFontSize:size];
    [tl setFrame:frame];
    [tl setAlignmentMode:kCAAlignmentCenter];
    [tl setContentsScale:[[UIScreen mainScreen] scale]];
    [tl setForegroundColor:RGB(168, 168, 168, 1).CGColor];
    [tl setString:string];
    return tl;
}

(2)索引条与collectionView联动效果:

重写响应事件的方法

#pragma mark- response事件
//手指触碰屏幕,触摸开始
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
    [super touchesBegan:touches withEvent:event];
    [self sendEventToDelegate:event];
    [self.collectionDelegate collectionViewIndexTouchesBegan:self];
}

//手指在屏幕上移动
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{
    [super touchesMoved:touches withEvent:event];
    [self sendEventToDelegate:event];
}

//手指离开屏幕,触摸结束
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
    [self.collectionDelegate collectionViewIndexTouchesEnd:self];
}
//根据触摸事件的触摸点来算出点击的是第几个section
- (void)sendEventToDelegate:(UIEvent*)event{
    UITouch *touch = [[event allTouches] anyObject]; //获取触摸对象
    CGPoint point = [touch locationInView:self];   //获取当前触摸点位置
    
    NSInteger indx = ((NSInteger) floorf(point.y) / _letterHeight);  //触摸位置对应的行数
    
    if (indx< 0 || indx > self.titleIndexes.count - 1) {
        return;
    }
    
    [self.collectionDelegate collectionViewIndex:self didselectionAtIndex:indx withTitle:self.titleIndexes[indx]];
    
}

(3)中文转成拼音以进行排序操作

/**
 将中文字符串转换为拼音格式(不带声调)
 @return 返回不带声调拼音字符串
 */
- (NSString *)transformToPinyin:(NSString *)str
{
    // 空值判断
    if (str == nil || str == NULL) {
        return @"";
    }
    // 将字符串转为NSMutableString类型
    NSMutableString *string = [str mutableCopy];
    // 将字符串转换为拼音音调格式
    CFStringTransform((__bridge CFMutableStringRef)string, NULL, kCFStringTransformMandarinLatin, NO);
    // 去掉音调符号
    CFStringTransform((__bridge CFMutableStringRef)string, NULL, kCFStringTransformStripDiacritics, NO);
    // 返回不带声调拼音字符串
    return string;
}

(4)对数据进行删选(把接口提供的数据转化为我们需要的数据结构)

接口提供数据:

self.data = [NSMutableArray arrayWithArray:@[@"安卓", @"宝丽来",@"霸王别姬",@"菜鸟",@"facebook",@"菲尔可",@"飞利浦",@"谷歌",@"海尔",@"海信",@"华为",@"iPhone",@"iPad",@"Mac book",@"松下"]];

因接口数据是按字母排序过得中文名称, 过滤代码如下, 时间复杂度是O(n)

NSMutableArray *sections = [NSMutableArray array];    //创建字母数组   @[@"A", @"D", @"F", @"M", @"N", @"Z"];
    rows = [NSMutableArray array];  //创建索引数组   @[@[@"adam", @"alfred"],@[@"bo"]];
    int indexsInt = -1;    //更新索引数组元素的数值
    NSString *tempStr = @"temp";    //交换字母,用来判断该字母是否存在的数值
    for (NSString *nameStr in self.data) {
        NSString *pinyin = [self transformToPinyin:nameStr];    //将中文字符串转换为拼音格式
        if ([pinyin isEqualToString:@"zhang hong"]) {   //排错   长虹转换为拼音错误
            pinyin = @"chang hong";
        }
        NSString *testUpFirst = [pinyin capitalizedString];      //首字母大写
        NSString *firstLetterStirng = [testUpFirst substringToIndex:1];    //取出字符串第一位字母
        
        if (firstLetterStirng == tempStr) {   //判断该字母是否为上一位存在的
            [rows[indexsInt] addObject:firstLetterStirng];   //加入对应数组中
        }else{
            [sections addObject:firstLetterStirng];   //字母数组添加新字母
            NSMutableArray *array = [NSMutableArray array];   //生成新字母数组
            [array addObject:firstLetterStirng];
            [rows addObject:array];     //索引数组添加新字母数组
            indexsInt = indexsInt + 1;    //索引数组count+1
            tempStr = firstLetterStirng;    //上一位字母更换为新字母
        }
    }

引申内容

(5)数组遍历(enumerateObjectsUsingBlock)

我们常用的循环方式:
for循环 方便针对下标的处理, 适用面最广
forin 效率更高, 但无法针对下标处理, 反向遍历不方便
enumerateObjectsUsingBlock 底层通过GCD来处理并发执行事宜, 多线程来并发实现,并不保证按照顺序执行, 但效率最高

[self.titleIndexes enumerateObjectsUsingBlock:^(NSString *obj, NSUInteger idx, BOOL *stop) {
      //obj代表内容, idx代表循环在第几个元素(row), stop用来停止循环
      CGFloat originY = idx *12;
}];

效率(遍历速度): enumerateObjectsUsingBlock > forin > for 尽量选择更高效的遍历方式

(6)数组排序 (sortedArrayUsingComparator)

// 数组的排序
    // 定义一个数组数组
    NSArray *array = @[@(3),@(5),@(4),@(2),@(1)];
    // 对数组进行排序(升序)
    NSArray *resultAscending = [array sortedArrayUsingComparator:^NSComparisonResult(id  _Nonnull obj1, id  _Nonnull obj2) {
        NSLog(@"%@ ~ %@", obj1, obj2);
        return [obj1 compare:obj2];
    }];
    NSLog(@"对数组进行排序(正序):%@", resultAscending);
    // 对数组进行排序(降序)
    NSArray *resultDescending = [array sortedArrayUsingComparator:^NSComparisonResult(id  _Nonnull obj1, id  _Nonnull obj2) {
        NSLog(@"%@ ~ %@", obj1, obj2);
        return [obj2 compare:obj1];
    }];
    NSLog(@"对数组进行排序(降序):%@", resultDescending);
    // 对数组进行排序(乱序)
    NSArray *resultBreak = [resultAscending sortedArrayUsingComparator:^NSComparisonResult(id  _Nonnull obj1, id  _Nonnull obj2) {
        NSLog(@"%@ ~ %@", obj1, obj2);
        if (arc4random_uniform(2) == 0) { // arc4random_uniform会随机返回一个0到上界之间(不含上界)的整数。以2为上界会得到0或1,像投硬币一样
            return [obj2 compare:obj1];// descending
        } else {
            return [obj1 compare:obj2];// ascending
        }
    }];
    NSLog(@"对数组进行排序(乱序):%@", resultBreak);

参考文献:
UICollectionView 加字母索引
iOS触摸事件
iOS数组遍历 (enumerateObjectsUsingBlock)
iOS数组排序 (sortedArrayUsingComparator)

Demo下载地址:
GitHub下载

上一篇下一篇

猜你喜欢

热点阅读