大数据

iOS实践:打造一个可以快速索引的城市列表页

相信绝大部分LBS的APP里面,大家都能看到一个带索引的城市列表页面,用来让用户选择所在城市。

我们就一步一步的来实现这个页面,最终效果如下:

Paste_Image.png

最终我们会按照首字母汉语拼音对所有城市进行排序,可以通过右侧的首字母索引来快速定位到城市。

1. 从plist中获取城市字典

1.1 准备素材,下载文件

城市列表(带拼音首字母的),下载地址:

链接: https://pan.baidu.com/s/1nvPNYJJ 密码: cjpw

1.2 从plist中读取出所有的城市。

- (NSDictionary *)loadCityListData{

    return [NSDictionary dictionaryWithContentsOfURL:[[NSBundle mainBundle] URLForResource:@"cityList.plist" withExtension:nil]];
}

1.3 取出字典中所有的key

  • 字典中有一个属性allKeys,可以取出字典中所有的key。这是一个只读的属性。
  • 根据allKeys就能知道字典中有多少组,去对应的数据源设置返回数据。
@property (readonly, copy) NSArray *allKeys;

2. 对城市的首字母进行排序

  • 对所有字典key的数组中的内容进行排序
  • 对于排序,系统提供了两种办法可以进行排序。我们就不用再写什么冒泡儿、选择之类的算法了,直接来就可以用。

    2.1 方法一:使用方法进行排序:升序

    _firstLetterOfCitys = [_firstLetterOfCitys sortedArrayUsingSelector:@selector(compare:)];

2.2 使用block进行排序:可以选择排序的方法

    _firstLetterOfCitys = [_firstLetterOfCitys sortedArrayUsingComparator:^NSComparisonResult(id  _Nonnull obj1, id  _Nonnull obj2) {
//        升序排序
        return [obj1 compare:obj2];

//        将序排序
        return [obj2 compare:obj1];

//        乱序
        return arc4random_uniform(10) % 3 - 1;

    }];
  • 之所以是能够排序,是每次obj1和obj2比较都进行了一次排序。排序结果记录在了NSComparisonReuslt中。
  • NSComparisonReuslt是一个枚举。通过操作两数比较的结果,进行排序。如果两数比较的结果一会儿是-1,一会儿是0,一会儿是1,那排序出来的结果也就是乱的。

官方对此说明:

typedef NS_ENUM(NSInteger, NSComparisonResult) {NSOrderedAscending = -1L, NSOrderedSame, NSOrderedDescending};

3. 设置边栏索引

  • 边栏的索引显示的文字和实际跳转没有直接关系。
  • 边栏索引无论写什么,都是按照实际的key值进行跳转的。
- (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView{
    return _firstLetterOfCitys;
}

4. 关于约束的重要提示

  • 所有的类方法在执行初始化的时候都需要先去看看类里面初始化的方法首选项。

    以UITableView为例

NS_CLASS_AVAILABLE_IOS(2_0) @interface UITableView : UIScrollView 

- (instancetype)initWithFrame:(CGRect)frame style:(UITableViewStyle)style NS_DESIGNATED_INITIALIZER; // must specify style at creation. -initWithFrame: calls this with UITableViewStylePlain
- (nullable instancetype)initWithCoder:(NSCoder *)aDecoder NS_DESIGNATED_INITIALIZER;

官方说明的意思是说,当执行init的时候,系统要求执行了两个初始化方法。

问题来了:如果在初始化的时候,写成了[[UITableView alloc] init],在编译的时候实际上还是执行了两个初始化方法。第一个初始化方法中要求传入frame,此时因为没有传入参数,frame就会被默认为{0.0.0.0}。

为了解决这个问题,当view有了frame之后,就需要重新给tableView设置frame。是用以下方法进行设置:

- (void)layoutSubviews{
    [super layoutSubviews];
    _tableView.frame = self.bounds;
}

5. 完善:封装

因为城市列表今后我们很有可能会在其他项目里面使用,但我们又不确定以后再使用的时候是用StoryBoard调用还是代码调用。所以我们还要进一步处理一下。

//当从XIB或者UIStoryboard中创建UITableView的时候加载此方法
- (void)awakeFromNib{
    [super awakeFromNib];
    [self createCityListTableView];
}

//使用代码创建的时候加载此方法
- (instancetype)initWithFrame:(CGRect)frame{
    self = [super initWithFrame:frame];
    if (self) {
        [self createCityListTableView];
    }

    return self;
}

这样就算是封装好了,以后不管是SB还是代码方式,都可以直接的把刚才写好的城市列表进行引用了。