最終效果圖:
關鍵代碼:
搜索結果控制器:<喎?/kf/ware/vc/" target="_blank" class="keylink">vcD4KPHA+PC9wPgo8cHJlIGNsYXNzPQ=="brush:java;">//
// SearchResultController.m
// 帥哥_團購
//
// Created by beyond on 14-8-15.
// Copyright (c) 2014年 com.beyond. All rights reserved.
// 當搜索框searchBar裡面的文字change的時候,會創建本控制器,展示搜索結果列表,本控制器只有唯一一個成員變量,那就是從CityLocationController控制器的searchBar textDidChange:方法中傳遞過來的搜索文本
#import "SearchResultController.h"
#import "PinYin4Objc.h"
#import "MetaDataTool.h"
#import "City.h"
@interface SearchResultController ()
{
// 數組,放著所有符合搜索條件的 城市對象
NSMutableArray *_resultCityArr;
}
@end
@implementation SearchResultController
- (void)viewDidLoad
{
[super viewDidLoad];
self.view.backgroundColor = [UIColor whiteColor];
// 數組初始化,放著所有符合搜索條件的 城市對象
_resultCityArr = [NSMutableArray array];
}
// 唯一一個成員,從CityLocationController控制器的searchBar textDidChange:方法中傳遞過來的搜索文本
-(void)setSearchText:(NSString *)searchText
{
_searchText = searchText;
// 1.清除數組之前存放的搜索結果
[_resultCityArr removeAllObjects];
// 2.篩選城市
// 拼音輸出格式
HanyuPinyinOutputFormat *fmt = [[HanyuPinyinOutputFormat alloc] init];
// 全大寫
fmt.caseType = CaseTypeUppercase;
// 無音調
fmt.toneType = ToneTypeWithoutTone;
// V字處理方式
fmt.vCharType = VCharTypeWithUUnicode;
// 下面是三種條件,關鍵代碼!
NSDictionary *citiesDict = [MetaDataTool sharedMetaDataTool].allCitiesDict;
[citiesDict enumerateKeysAndObjectsUsingBlock:^(NSString *key, City *city, BOOL *stop) {
// SHI#JIA#ZHUANG
// 1.拼音字符串,用#連接一個個拼音
NSString *pinyinStr = [PinyinHelper toHanyuPinyinStringWithNSString:city.name withHanyuPinyinOutputFormat:fmt withNSString:@"#"];
// 2.拼音首字母
NSArray *wordsArr = [pinyinStr componentsSeparatedByString:@"#"];
// 用於所有首字母拼接 如BJ
NSMutableString *pinyinInitial = [NSMutableString string];
for (NSString *word in wordsArr) {
[pinyinInitial appendString:[word substringToIndex:1]];
}
// 去掉所有的# 如beijing
pinyinStr = [pinyinStr stringByReplacingOccurrencesOfString:@"#" withString:@""];
// 3.城市名 city.name 中包含了搜索條件 如北京
// 拼音 pinyinStr 中包含了搜索條件 如BEIJING
// 拼音首字母 pinyinInitial 中包含了搜索條件 如BJ
if (([city.name rangeOfString:searchText].length != 0) ||
([pinyinStr rangeOfString:searchText.uppercaseString].length != 0)||
([pinyinInitial rangeOfString:searchText.uppercaseString].length != 0))
{
// 來到這,說明城市名中包含了搜索條件,添加到成員變量(對象數組),以便為tableView提供數據源
[_resultCityArr addObject:city];
}
}];
// 3.刷新表格
[self.tableView reloadData];
}
#pragma mark - 數據源方法
// 符合搜索條件的結果 有多少行
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return _resultCityArr.count;
}
// 每一行的獨一無二內容
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = @"SearchResultCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
// 取出本行對應的 城市對象
City *city = _resultCityArr[indexPath.row];
cell.textLabel.text = city.name;
return cell;
}
// 友好提示,共有多少個符合條件的結果
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
return [NSString stringWithFormat:@"共%d個搜索結果", _resultCityArr.count];
}
#pragma mark - 代理方法
// 選中了符合搜索條件的所有搜索結果中的某一行
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
// 取出本行對應的 城市對象
City *city = _resultCityArr[indexPath.row];
// 設置工具類的當前城市,其內部會攔截setter操作,更新最近訪問的城市數組
[MetaDataTool sharedMetaDataTool].currentCity = city;
}
@end
點擊 dock下面的倒數第2個定位按鈕,
彈出控制器CityLocationController
// // CityLocationController.m // 帥哥_團購 // // Created by beyond on 14-8-14. // Copyright (c) 2014年 com.beyond. All rights reserved. // 點擊dock下面的倒數第2個定位按鈕,彈出的用Popover包裝的城市選擇控制器,其上面是一個搜索框,下面是一個tableView(按城市的拼音分的組),當搜索框文字改變時候,(懶加載)創建一個搜索結果控制器,並且顯示搜索界面在遮罩上面 #import "CityLocationController.h" // 蒙板 #import "CoverOnTableView.h" // 元數據工具 #import "MetaDataTool.h" // 數據模型---分組 #import "Section.h" // 數據模型---城市 #import "City.h" // 數據模型---行政區 #import "District.h" // 當搜索框searchBar裡面的文字change的時候,會創建本控制器,展示搜索結果列表 #import "SearchResultController.h" // 上面的searchBar高度 #define kSearchBarH 44 @interface CityLocationController (){ // 從plist中加載的數組,共23個成員,每個成員是個字典,每個字典有兩對KV,一對是name-->A,另一對是cities--->數組(數組中的成員是字典,一個字典對應一個城市,該字典又有三對KV,分別是:name--->北京,hot---->1,districts--->數組(該數組對應的又是一個字典,代表一個區,字典中有兩對KV,分別是:name--->朝陽區,neighbours--->數組(該數組的成員是string.....))) NSMutableArray *_sections; // 所有的城市組信息 // view上方是UISearchBar,下方是UITableView UISearchBar *_searchBar; UITableView *_tableView; // UITableView上面有一層蒙板,遮蓋 CoverOnTableView *_cover; // 搜索結果控制器 SearchResultController *_searchResultCtrl; } @end @implementation CityLocationController - (void)viewDidLoad { [super viewDidLoad]; // 1.添加上方的搜索框UISearchBar [self addSearchBar]; // 2.添加下方的tableView [self addTableView]; // 3.使用工具類,加載城市數組數據 [self loadCitiesMetaData]; } // 1.添加搜索框UISearchBar - (void)addSearchBar { _searchBar = [[UISearchBar alloc] init]; _searchBar.autoresizingMask = UIViewAutoresizingFlexibleWidth; _searchBar.frame = CGRectMake(0, 0, self.view.frame.size.width, kSearchBarH); // 監聽searchBar的獲得焦點,失去焦點,字符變化等事件 _searchBar.delegate = self; _searchBar.placeholder = @"請輸入城市名或拼音"; // search.tintColor 漸變色 // search.barStyle 樣式 [self.view addSubview:_searchBar]; } // 2.添加下方的tableView - (void)addTableView { _tableView = [[UITableView alloc] init]; CGFloat tableViewH = self.view.frame.size.height - kSearchBarH; _tableView.frame = CGRectMake(0, kSearchBarH, self.view.frame.size.width, tableViewH); _tableView.dataSource = self; _tableView.delegate = self; // 重要~因為本控制器是在Popover控制器裡面,Popover又設置了內容SIZE只有320, 480 _tableView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; [self.view addSubview:_tableView]; } // 3.使用工具類,加載城市數組數據 - (void)loadCitiesMetaData { // 從plist中加載的數組,共23個成員,每個成員是個字典,每個字典有兩對KV,一對是name-->A,另一對是cities--->數組(數組中的成員是字典,一個字典對應一個城市,該字典又有三對KV,分別是:name--->北京,hot---->1,districts--->數組(該數組對應的又是一個字典,代表一個區,字典中有兩對KV,分別是:name--->朝陽區,neighbours--->數組(該數組的成員是string.....))) _sections = [NSMutableArray array]; NSArray *sections = [MetaDataTool sharedMetaDataTool].allSectionsArr; // 將工具類返回的section數組賦值給成員變量,以供tableView的數據源使用 [_sections addObjectsFromArray:sections]; } #pragma mark - 數據源方法 #pragma mark - 數據源方法 // 共有多少分組(23個字母組) - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { return _sections.count; } // 每一組有多少行(多少個城市就有多少行) - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { // 第幾組 Section *s = _sections[section]; // 第幾組的城市數組的個數 return s.cities.count; } // 每一行的cell獨一無二的內容 - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { static NSString *CellIdentifier = @"CityListCell"; UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier]; if (cell == nil) { cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier]; } // 第幾組 Section *s = _sections[indexPath.section]; // 第幾行(城市) City *city = s.cities[indexPath.row]; // 城市名 cell.textLabel.text = city.name; return cell; } // 每一組的HeaderTitle - (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section { // 第幾組 Section *s = _sections[section]; // 組名,如ABCD return s.name; } // 表格右側的分組索引標題 - (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView { // 重要~取出_sections數組中每一組的鍵為name的值(如ABCD...),並且將這些值全放到一個新的數組中,返回的這個新數組,就是分組索引的標題 return [_sections valueForKeyPath:@"name"]; } // 點擊某一行時,設置當前城市 - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { // 第幾組 Section *s = _sections[indexPath.section]; // 哪一個城市 City *city = s.cities[indexPath.row]; // 設置工具類的當前城市,其內部會攔截setter操作,更新最近訪問的城市數組 [MetaDataTool sharedMetaDataTool].currentCity = city; } #pragma mark - 搜索框代理方法 // 搜索框開始編輯(開始聚焦,取得焦點) - (void)searchBarTextDidBeginEditing:(UISearchBar *)searchBar { // 1.動畫效果,顯示其右邊的 取消按鈕 [searchBar setShowsCancelButton:YES animated:YES]; // 2.動畫顯示遮蓋(蒙板),並在內部綁定了一個,tap手勢監聽器 if (_cover == nil) { _cover = [CoverOnTableView coverWithTarget:self action:@selector(coverClick)]; } // 3.必須讓cover完全覆蓋在tableView上面 _cover.frame = _tableView.frame; [self.view addSubview:_cover]; // 4.開始全透明(看不見) _cover.alpha = 0.0; [UIView animateWithDuration:0.3 animations:^{ // 讓cover變成黑色 [_cover alphaReset]; }]; } // 監聽 遮蓋 被tap點擊,移除遮蓋,隱藏取消按鈕,退出鍵盤 - (void)coverClick { // 1.動畫完成後,移除遮蓋 [UIView animateWithDuration:0.3 animations:^{ _cover.alpha = 0.0; } completion:^(BOOL finished) { [_cover removeFromSuperview]; }]; // 2.隱藏_searchBar最右邊的取消按鈕 [_searchBar setShowsCancelButton:NO animated:YES]; // 3.讓_searchBar取消第一響應者,即退出鍵盤 [_searchBar resignFirstResponder]; // [self.view endEditing:YES]; } // 當點擊了 搜索框的鍵盤上面取消鍵時(即_searchBar失去了焦點) - (void)searchBarTextDidEndEditing:(UISearchBar *)searchBar { [self coverClick]; } // 當點擊了 搜索框的右邊的取消按鈕時 - (void)searchBarCancelButtonClicked:(UISearchBar *)searchBar { [self coverClick]; } #pragma mark 監聽搜索框的文字改變 - (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText { // 如果搜索框裡面沒有文字,隱藏搜索結果控制器的view if (searchText.length == 0) { [_searchResultCtrl.view removeFromSuperview]; } else { // 懶加載,創建並顯示搜索界面,frame與遮罩相同 if (_searchResultCtrl == nil) { _searchResultCtrl = [[SearchResultController alloc] init]; _searchResultCtrl.view.frame = _cover.frame; _searchResultCtrl.view.autoresizingMask = _cover.autoresizingMask; // 如果另一個控制器的view要展示在本控制器的view裡,官方建議是讓另一個控制器成為本控制器的子控制器 [self addChildViewController:_searchResultCtrl]; } // 傳遞搜索框裡面的文本給 搜索結果控制器 _searchResultCtrl.searchText = searchText; [self.view addSubview:_searchResultCtrl.view]; } } @end
Dock下方的Location按鈕
// // DockItemLocation.m // 帥哥_團購 // // Created by beyond on 14-8-13. // Copyright (c) 2014年 com.beyond. All rights reserved. // dock下方倒數第2個按鈕【定位】,繼承自DockItem,點擊本按鈕,負責創建一個封裝了控制器CityLocationController的Popover控制器,本按鈕同時還負責監聽屏幕的橫豎屏切換通知,同時還負責監聽CityLocationController控制器裡面的某一行被選中時,發出的CityDidChanged通知,目的是能夠讓本按鈕掌控Popover控制器的位置,以及其出現和隱藏 #import "DockItemLocation.h" // 點擊dock上面的locationBtn,彈出的Popover封裝的控制器,其上方是搜索欄,下方是tableView #import "CityLocationController.h" // 工具類中currentCity獲取當前城市 #import "MetaDataTool.h" #import "City.h" // 按鈕上面是圖片,下面是文字,這是圖片在高度上的比例 #define kImageHeightRatioInBtn 0.5 @interface DockItemLocation(){ //popover控制器,創建出來之後,show方法顯示,因此不可以是局部變量,必須用成員變量記住,否則方法btnClick調用完畢就銷毀了,還如何 顯示捏? UIPopoverController *_popoverCtrl; } @end @implementation DockItemLocation - (id)initWithFrame:(CGRect)frame { self = [super initWithFrame:frame]; if (self) { // 1.調用父類的方法,設置內部的圖片 [self setIcon:@"ic_district.png" selectedIcon:@"ic_district_hl.png"]; // 2.自動伸縮 self.autoresizingMask = UIViewAutoresizingFlexibleTopMargin; // 3.設置默認的文字 [self setTitle:@"定位中" forState:UIControlStateNormal]; self.titleLabel.font = [UIFont systemFontOfSize:16]; self.titleLabel.textAlignment = NSTextAlignmentCenter; [self setTitleColor:[UIColor whiteColor] forState:UIControlStateDisabled]; [self setTitleColor:[UIColor grayColor] forState:UIControlStateNormal]; // 4.設置圖片屬性 self.imageView.contentMode = UIViewContentModeCenter; // 5.監聽點擊【Location定位】,彈出一個Popover控制器 [self addTarget:self action:@selector(locationBtnOnDockClicked) forControlEvents:UIControlEventTouchDown]; // 6.添加監聽城市改變的通知,當接收到其他其他東東(如工具類裡面的setterCurrentCity方法中)發出的kCityChangeNote通知,就會調用下面的方法 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(cityDidChanged) name:kCityChangeNote object:nil]; } return self; } // 6.添加監聽城市改變的通知,當接收到其他其他東東(如工具類裡面的setterCurrentCity方法中)發出的kCityChangeNote通知,就會調用下面的方法 - (void)cityDidChanged { // 0.先從工具類中,獲取當前選中城市 City *city = [MetaDataTool sharedMetaDataTool].currentCity; // 1.更改本按鈕 顯示的城市名稱 [self setTitle:city.name forState:UIControlStateNormal]; // 2.關閉popover(代碼關閉popover不會觸發代理方法) [_popoverCtrl dismissPopoverAnimated:YES]; // 3.更改本按鈕 變為enable self.enabled = YES; // 4.設置圖標 [self setIcon:@"ic_district.png" selectedIcon:@"ic_district_hl.png"]; } // 5.監聽點擊【Location定位】,彈出一個Popover控制器 - (void)locationBtnOnDockClicked { // 禁用,只可點擊一次 self.enabled = NO; // 點擊dock上面的locationBtn,彈出的Popover封裝的控制器,其上方是搜索欄,下方是tableView CityLocationController *cityVC = [[CityLocationController alloc] init]; // 唯一一個不是繼承自UIViewController的控制器,它繼承自NSObject //popover控制器,創建出來之後,show方法顯示,因此不可以是局部變量,必須用成員變量記住,否則方法btnClick調用完畢就銷毀了,還如何 顯示捏? _popoverCtrl = [[UIPopoverController alloc] initWithContentViewController:cityVC]; // 設置這個Popover控制器的顯示的大小 _popoverCtrl.popoverContentSize = CGSizeMake(320, 480); // 代理,監聽Popover控制器的XX事件 _popoverCtrl.delegate = self; // 因為其他方法也要顯示,_popoverCtrl,所以抽取成自定義方法 [self showPopoverCtrl]; // 因為屏幕旋轉時,彈出的popover的指向的位置就不對了,所以有必要注冊監聽屏幕旋轉的通知 // 先移除監聽器,保證健壯性 [[NSNotificationCenter defaultCenter] removeObserver:self name:UIDeviceOrientationDidChangeNotification object:nil]; // 再添加一個監聽器,一旦設備出現UIDeviceOrientationDidChangeNotification,就會調用observer的selector方法 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(screenDidRotated) name:UIDeviceOrientationDidChangeNotification object:nil]; } // 5-1,因為偵聽到屏幕旋轉了,也要再次顯示_popoverCtrl,所以抽取成自定義方法 - (void)showPopoverCtrl { // 顯示到哪裡? 如果目標view是self自己,則rect使用bounds,因為bounds的原點才是相對於自己 // 如果目標view是self.superView,則rect使用frame,因為frame的原點才是相對於父控件 [_popoverCtrl presentPopoverFromRect:self.bounds inView:self permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES]; } // 5-2,再添加一個監聽器,一旦設備出現UIDeviceOrientationDidChangeNotification,就會調用observer的selector方法 - (void)screenDidRotated { if (_popoverCtrl.popoverVisible) { // 1. 關閉之前位置上面的_popoverCtrl [_popoverCtrl dismissPopoverAnimated:NO]; // 2. 0.5秒後創建新的位置上的_popoverCtrl [self performSelector:@selector(showPopoverCtrl) withObject:nil afterDelay:0.5]; } } #pragma mark - popOver代理方法 - (void)popoverControllerDidDismissPopover:(UIPopoverController *)popoverController { // 消失前,讓定位按鈕恢復可以點擊狀態 self.enabled = YES; // 消失前,即popover被銷毀的時候,移除注冊的監聽器(通知) [[NSNotificationCenter defaultCenter] removeObserver:self name:UIDeviceOrientationDidChangeNotification object:nil]; } #pragma mark - 銷毀時,移除當前對控制器對屏幕的監聽,防止野指針 - (void)dealloc { [[NSNotificationCenter defaultCenter] removeObserver:self]; } #pragma mark - 覆寫調整圖片和文字在按鈕中的Frame - (CGRect)imageRectForContentRect:(CGRect)contentRect { CGFloat btnW = contentRect.size.width; CGFloat imgH = contentRect.size.height * kImageHeightRatioInBtn; return CGRectMake(0, 0, btnW, imgH); } - (CGRect)titleRectForContentRect:(CGRect)contentRect { CGFloat btnW = contentRect.size.width; CGFloat textH = contentRect.size.height * (1 - kImageHeightRatioInBtn); // 文字在下面,圖片在上面 CGFloat textY = contentRect.size.height - textH; return CGRectMake(0, textY, btnW, textH); } #pragma mark - json轉成Plist - (void)json2plist { // json文件讀取成為數組 NSString *filePath = @"/Users/beyond/Desktop/cities.json"; NSData *data = [NSData dataWithContentsOfFile:filePath]; // options: // NSJSONReadingMutableContainers 返回可變容器,NSMutableDictionary或NSMutableArray // NSJSONReadingAllowFragments:允許JSON字符串最外層既不是NSArray也不是NSDictionary,但必須是有效的JSON Fragment。例如使用這個選項可以解析 @“123” 這樣的字符串。 // NSJSONReadingMutableLeaves:返回的JSON對象中字符串的值為NSMutableString NSArray *arr = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:nil]; // 數組寫到文件後,就是plist [arr writeToFile:@"/Users/beyond/Desktop/cities.plist" atomically:YES]; } @end
用到的工具類
@interface MetaDataTool : NSObject singleton_interface(MetaDataTool) // readonly只可讀,NSArray,不允許外部隨便增刪改 // 所有的城市分組數組,數組中的元素是section對象 @property (nonatomic, strong, readonly) NSArray *allSectionsArr; // 所有城市字典,Key是城市名,Value是城市對象 @property (nonatomic, strong, readonly) NSMutableDictionary *allCitiesDict; // 當前選中的城市, 當點擊了控制器下方的tableView的某一行時,會設置當前城市,攔截setter操作,更新最近訪問的城市數組 @property (nonatomic, strong) City *currentCity; // 當前選中的城市 @end // // MetaDataTool.m // 帥哥_團購 // // Created by beyond on 14-8-14. // Copyright (c) 2014年 com.beyond. All rights reserved. // 元數據管理類 // 1.城市數據 // 2.下屬分區數據 // 3.分類數據 #import "MetaDataTool.h" // 一個分組模型 #import "Section.h" #import "City.h" //#import "TGCategory.h" //#import "TGOrder.h" // 沙盒裡面放的是所有曾經訪問過的城市名字 #define kFilePath [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0] stringByAppendingPathComponent:@"visitedCityNamesArr.data"] @interface MetaDataTool () { // 數組,存儲曾經訪問過城市的名稱 NSMutableArray *_visitedCityNamesArr; // 訪問過的組section Section *_visitedSection; // 最近訪問的城市組數組 } @end @implementation MetaDataTool singleton_implementation(MetaDataTool) - (id)init { if (self = [super init]) { // 初始化項目中的所有元數據 // 1.初始化城市數據 [self loadCitiesData]; } return self; } // 1.初始化城市數據 - (void)loadCitiesData { // 所有城市對象組成的字典,Key是城市名,Value是城市對象 _allCitiesDict = [NSMutableDictionary dictionary]; // 臨時變量,存放所有的section NSMutableArray *tempSectionsArr = [NSMutableArray array]; // 1.創建一個熱門城市分組 Section *hotSection = [[Section alloc] init]; // 組名是 熱門 hotSection.name = @"熱門"; // 分組的成員cities,初始化 hotSection.cities = [NSMutableArray array]; // 為了將熱門這一組加在分組數組的最前面,准備了一個臨時的section數組 [tempSectionsArr addObject:hotSection]; // 2.添加A-Z分組,到臨時section數組後面 // 加載plist數據 NSArray *sectionsArr = [NSArray arrayWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"Cities.plist" ofType:nil]]; for (NSDictionary *dict in sectionsArr) { // 創建城市分組對象模型 Section *section = [[Section alloc] init]; // 調用分類方法,將字典轉成模型 [section setValuesWithDict:dict]; // 將所有的section對象,加到臨時的section對象數組的後面 [tempSectionsArr addObject:section]; // 遍歷每一組的所有城市,找出熱門的加到hotSection裡面 for (City *city in section.cities) { if (city.hot) { // 如果是熱門城市 [hotSection.cities addObject:city]; } // 並且將所有的城市對象,以城市名作為鍵,存入字典中 [_allCitiesDict setObject:city forKey:city.name]; } } // 3.從沙盒中讀取之前訪問過的城市名稱 _visitedCityNamesArr = [NSKeyedUnarchiver unarchiveObjectWithFile:kFilePath]; // 如果是首次使用,則沙盒中返回的是空數組,需要懶加載,創建一個數組 if (_visitedCityNamesArr == nil) { _visitedCityNamesArr = [NSMutableArray array]; } // 4.創建並添加一個section, 最近訪問城市組(section) _visitedSection = [[Section alloc] init]; _visitedSection.name = @"最近訪問"; _visitedSection.cities = [NSMutableArray array]; // 5.遍歷沙盒中取出來的城市名組成的數組,轉成一個個城市對象 for (NSString *name in _visitedCityNamesArr) { // 根據城市名,從對象字典中取出城市對象,並添加到最近訪問城市組(section)中的城市對象數組 City *city = _allCitiesDict[name]; [_visitedSection.cities addObject:city]; } // 6.如果最近訪問城市組(section)中的城市對象數組中有城市,那麼就可以將最近訪問組插入到sections最前面 if (_visitedSection.cities.count) { [tempSectionsArr insertObject:_visitedSection atIndex:0]; } // 將所有的section組成的數組賦值給成員變量供調用者訪問 _allSectionsArr = tempSectionsArr; } // 當點擊了控制器下方的tableView的某一行時,會設置當前城市,攔截setter操作,更新最近訪問的城市數組,發出CityDidChanged通知給Dock上的定位按鈕,讓它隱藏popoverCtroller - (void)setCurrentCity:(City *)currentCity { _currentCity = currentCity; // 1.先從最近訪問的城市名數組中,移除該的城市名 [_visitedCityNamesArr removeObject:currentCity.name]; // 2.再將新的城市名插入到數組的最前面(最近訪問的在最前) [_visitedCityNamesArr insertObject:currentCity.name atIndex:0]; // 3.同時,要將新的城市對象,放到_visitedSection的城市對象數組的最前面 [_visitedSection.cities removeObject:currentCity]; [_visitedSection.cities insertObject:currentCity atIndex:0]; // 4.歸檔最近訪問的城市名組成的數組,以便下次再解檔 [NSKeyedArchiver archiveRootObject:_visitedCityNamesArr toFile:kFilePath]; // 5.每一次點擊,攔截setter當前城市之後,都要發出通知,做什麼用??? [[NSNotificationCenter defaultCenter] postNotificationName:kCityChangeNote object:nil]; } @end