相信絕大部分LBS的APP里面,大家都能看到一個(gè)帶索引的城市列表頁面,用來讓用戶選擇所在城市。
我們就一步一步的來實(shí)現(xiàn)這個(gè)頁面,最終效果如下:

Paste_Image.png
最終我們會(huì)按照首字母漢語拼音對所有城市進(jìn)行排序,可以通過右側(cè)的首字母索引來快速定位到城市。
1. 從plist中獲取城市字典
1.1 準(zhǔn)備素材,下載文件
城市列表(帶拼音首字母的),下載地址:
鏈接: 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
- 字典中有一個(gè)屬性allKeys,可以取出字典中所有的key。這是一個(gè)只讀的屬性。
- 根據(jù)allKeys就能知道字典中有多少組,去對應(yīng)的數(shù)據(jù)源設(shè)置返回?cái)?shù)據(jù)。
@property (readonly, copy) NSArray<KeyType> *allKeys;
2. 對城市的首字母進(jìn)行排序
- 對所有字典key的數(shù)組中的內(nèi)容進(jìn)行排序
- 對于排序,系統(tǒng)提供了兩種辦法可以進(jìn)行排序。我們就不用再寫什么冒泡兒、選擇之類的算法了,直接來就可以用。
2.1 方法一:使用方法進(jìn)行排序:升序
_firstLetterOfCitys = [_firstLetterOfCitys sortedArrayUsingSelector:@selector(compare:)];
2.2 使用block進(jìn)行排序:可以選擇排序的方法
_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比較都進(jìn)行了一次排序。排序結(jié)果記錄在了NSComparisonReuslt中。
- NSComparisonReuslt是一個(gè)枚舉。通過操作兩數(shù)比較的結(jié)果,進(jìn)行排序。如果兩數(shù)比較的結(jié)果一會(huì)兒是-1,一會(huì)兒是0,一會(huì)兒是1,那排序出來的結(jié)果也就是亂的。
官方對此說明:
typedef NS_ENUM(NSInteger, NSComparisonResult) {NSOrderedAscending = -1L, NSOrderedSame, NSOrderedDescending};
3. 設(shè)置邊欄索引
- 邊欄的索引顯示的文字和實(shí)際跳轉(zhuǎn)沒有直接關(guān)系。
- 邊欄索引無論寫什么,都是按照實(shí)際的key值進(jìn)行跳轉(zhuǎn)的。
- (NSArray<NSString *> *)sectionIndexTitlesForTableView:(UITableView *)tableView{
return _firstLetterOfCitys;
}
4. 關(guān)于約束的重要提示
- 所有的類方法在執(zhí)行初始化的時(shí)候都需要先去看看類里面初始化的方法首選項(xiàng)。
以UITableView為例
NS_CLASS_AVAILABLE_IOS(2_0) @interface UITableView : UIScrollView <NSCoding>
- (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;
官方說明的意思是說,當(dāng)執(zhí)行init的時(shí)候,系統(tǒng)要求執(zhí)行了兩個(gè)初始化方法。
問題來了:如果在初始化的時(shí)候,寫成了[[UITableView alloc] init],在編譯的時(shí)候?qū)嶋H上還是執(zhí)行了兩個(gè)初始化方法。第一個(gè)初始化方法中要求傳入frame,此時(shí)因?yàn)闆]有傳入?yún)?shù),frame就會(huì)被默認(rèn)為{0.0.0.0}。
為了解決這個(gè)問題,當(dāng)view有了frame之后,就需要重新給tableView設(shè)置frame。是用以下方法進(jìn)行設(shè)置:
- (void)layoutSubviews{
[super layoutSubviews];
_tableView.frame = self.bounds;
}
5. 完善:封裝
因?yàn)槌鞘辛斜斫窈笪覀兒苡锌赡軙?huì)在其他項(xiàng)目里面使用,但我們又不確定以后再使用的時(shí)候是用StoryBoard調(diào)用還是代碼調(diào)用。所以我們還要進(jìn)一步處理一下。
//當(dāng)從XIB或者UIStoryboard中創(chuàng)建UITableView的時(shí)候加載此方法
- (void)awakeFromNib{
[super awakeFromNib];
[self createCityListTableView];
}
//使用代碼創(chuàng)建的時(shí)候加載此方法
- (instancetype)initWithFrame:(CGRect)frame{
self = [super initWithFrame:frame];
if (self) {
[self createCityListTableView];
}
return self;
}
這樣就算是封裝好了,以后不管是SB還是代碼方式,都可以直接的把剛才寫好的城市列表進(jìn)行引用了。