標籤:ios
最終:
核心代碼:
NSObject+Dict.h
//// NSObject+Dict.h// 帥哥_團購//// Created by beyond on 14-8-14.// Copyright (c) 2014年 com.beyond. All rights reserved.// 使用運行時,將dict轉成對象#import <Foundation/Foundation.h>@interface NSObject (Dict)// 一個對象,調用此方法,參數 傳遞一個字典,便可以通過運行時,自動將字典中所有的值V,賦值到對象對應的成員屬性上面去- (void)setValuesWithDict:(NSDictionary *)dict;@end
NSObject+Dict.m
//// NSObject+Dict.m// 帥哥_團購//// Created by beyond on 14-8-14.// Copyright (c) 2014年 com.beyond. All rights reserved.//#import "NSObject+Dict.h"// 運行時需要#import <objc/message.h>#import <objc/runtime.h>@implementation NSObject (Dict)// 一個對象,調用此方法,參數 傳遞一個字典,便可以通過運行時,自動將字典中所有的值V,賦值到對象對應的成員屬性上面去- (void)setValuesWithDict:(NSDictionary *)dict{ // 本類的類名 Class c = [self class]; while (c) { // 1.獲得本類 所有的成員變數 unsigned int outCount = 0; Ivar *ivars = class_copyIvarList(c, &outCount); for (int i = 0; i<outCount; i++) { Ivar ivar = ivars[i]; // 2.屬性名稱 NSMutableString *name = [NSMutableString stringWithUTF8String:ivar_getName(ivar)]; // 刪除最前面的_ [name replaceCharactersInRange:NSMakeRange(0, 1) withString:@""]; // 3.取出屬性值 NSString *key = name; if ([key isEqualToString:@"desc"]) { key = @"description"; } if ([key isEqualToString:@"ID"]) { key = @"id"; } // 取出參數字典中對應的值 id value = dict[key]; // 健壯性判斷,如果字典中沒有這個鍵對應的值,則進入下一次for迴圈 if (!value) continue; // 4.構造SEL // 首字母 NSString *cap = [name substringToIndex:1]; // 首字母變大寫 cap = cap.uppercaseString; // 將大寫字母調換掉原首字母 [name replaceCharactersInRange:NSMakeRange(0, 1) withString:cap]; // 拼接set [name insertString:@"set" atIndex:0]; // 拼接冒號: [name appendString:@":"]; SEL selector = NSSelectorFromString(name); // 5.屬性類型,如果是基本類型,要轉成對象 NSString *type = [NSString stringWithUTF8String:ivar_getTypeEncoding(ivar)]; if ([type hasPrefix:@"@"]) { // 若為物件類型,直接發送訊息 objc_msgSend(self, selector, value); } else { // 若為非物件類型,即基本物件類型,則要轉成物件類型 if ([type isEqualToString:@"d"]) { objc_msgSend(self, selector, [value doubleValue]); } else if ([type isEqualToString:@"f"]) { objc_msgSend(self, selector, [value floatValue]); } else if ([type isEqualToString:@"i"]) { objc_msgSend(self, selector, [value intValue]); } else { objc_msgSend(self, selector, [value longLongValue]); } } } // for迴圈遍曆完了本類中所有的成員之後,擷取其父類繼續上述操作,直至其父類為空白 c = class_getSuperclass(c); }}@end/* class_getSuperclass Returns the superclass of a class. Class class_getSuperclass(Class cls) Parameters cls A class object. Return Value The superclass of the class, or Nil if cls is a root class, or Nil if cls is Nil. Discussion You should usually use NSObject‘s superclass method instead of this function. Availability Available in iOS 2.0 and later. Declared In objc/runtime.h */
Cities.plist檔案轉物件模型:
一組對應的模型Section.m
//// CitySectionByLetter.m// 帥哥_團購//// Created by beyond on 14-8-14.// Copyright (c) 2014年 com.beyond. All rights reserved.// 資料模型,一個組Section,成員1:組名,如B組 成員2:是一個數組,cities,裝著B組這個組下面所有的城市,數組中每個成員是一個City對象#import "Section.h"//城市模型#import "City.h"@implementation Section// 關鍵!字典中cities對應的值還是字典數組,而我們需要的是city對象數組,所以要攔截setCities方法,將字典數組,遍曆,轉成一個個對象,並添加到一個數組中,再將對象數組賦值給成員屬性cities- (void)setCities:(NSMutableArray *)cities{ //攔截【運行時】將字典中的key--cities對應的值:字典數組 賦值到成員cities,而應該是把它轉成一個個對象之後,再賦值給成員屬性 // 當cities為空白或者裡面裝的已經是物件模型,就可以直接賦值,而不用再調用分類方法將字典轉成物件模型了 id obj = [cities lastObject]; if (![obj isKindOfClass:[NSDictionary class]]){ _cities = cities; return; } NSMutableArray *cityArr = [NSMutableArray array]; for (NSDictionary *dict in cities) { City *city = [[City alloc] init]; // 調用分類中運行時方法,將字典中的K - V 轉成對象 [city setValuesWithDict:dict]; [cityArr addObject:city]; } // 最後,再將對象數組,賦值給成員屬性cities數組 _cities = cityArr;}@end
一個城市對應的模型
//// City.m// 帥哥_團購//// Created by beyond on 14-8-14.// Copyright (c) 2014年 com.beyond. All rights reserved.// 資料模型,一個city,成員1:城市名,如北京 成員2:是一個數組,districts,裝著北京這個城市下面所有的行政區,數組中每個成員是一個District對象#import "City.h"// 資料模型#import "District.h"@implementation City// 關鍵!字典中districts對應的值還是字典數組,而我們需要的是district對象數組,所以要攔截setDistricts方法,將字典數組,遍曆,轉成一個個對象,並添加到一個數組中,再將對象數組賦值給成員屬性districts- (void)setDistricts:(NSMutableArray *)districts{ //攔截【運行時】將字典中的key--cities對應的值:字典數組 賦值到成員cities,而應該是把它轉成一個個對象之後,再賦值給成員屬性 NSMutableArray *districtArr = [NSMutableArray array]; for (NSDictionary *dict in districts) { District *district = [[District alloc] init]; // 調用分類中運行時方法,將字典中的K - V 轉成對象 [district setValuesWithDict:dict]; [districtArr addObject:district]; } // 最後,再將對象數組,賦值給成員屬性districts數組 _districts = districtArr;}@end
CityLocationController.m
//// 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高度#define kSearchBarH 44@interface CityLocationController ()<UITableViewDataSource, UITableViewDelegate, UISearchBarDelegate>{ // 從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; // TGSearchResultController *_searchResult;}@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].allSections; // 將工具類返回的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"];}#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];}@end