├── .gitignore ├── JFCitySelector ├── Assets │ └── .gitkeep ├── Classes │ ├── .gitkeep │ ├── JFCSAreaModel.h │ ├── JFCSAreaModel.m │ ├── JFCSBaseInfoModel.h │ ├── JFCSBaseInfoModel.m │ ├── JFCSCityModel.h │ ├── JFCSCityModel.m │ ├── JFCSConfiguration.h │ ├── JFCSConfiguration.m │ ├── JFCSData.h │ ├── JFCSData.m │ ├── JFCSDataOpreation.h │ ├── JFCSDataOpreation.m │ ├── JFCSFileManager.h │ ├── JFCSFileManager.m │ ├── JFCSPopularCitiesModel.h │ ├── JFCSPopularCitiesModel.m │ ├── JFCSProvinceModel.h │ ├── JFCSProvinceModel.m │ ├── JFCSSearchTableViewController.h │ ├── JFCSSearchTableViewController.m │ ├── JFCSTableViewController.h │ ├── JFCSTableViewController.m │ ├── JFCSTableViewHeaderView.h │ ├── JFCSTableViewHeaderView.m │ ├── JFCSTopToolsTableViewCell.h │ ├── JFCSTopToolsTableViewCell.m │ └── JFCitySelector.h ├── Example │ └── JFCitySelector Demo │ │ ├── JFCitySelector Demo.xcodeproj │ │ ├── project.pbxproj │ │ └── project.xcworkspace │ │ │ ├── contents.xcworkspacedata │ │ │ └── xcshareddata │ │ │ └── IDEWorkspaceChecks.plist │ │ ├── JFCitySelector Demo.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ └── IDEWorkspaceChecks.plist │ │ ├── JFCitySelector Demo │ │ ├── AppDelegate.h │ │ ├── AppDelegate.m │ │ ├── Assets.xcassets │ │ │ ├── AppIcon.appiconset │ │ │ │ └── Contents.json │ │ │ └── Contents.json │ │ ├── Base.lproj │ │ │ ├── LaunchScreen.storyboard │ │ │ └── Main.storyboard │ │ ├── Info.plist │ │ ├── ViewController.h │ │ ├── ViewController.m │ │ └── main.m │ │ ├── Podfile │ │ ├── Podfile.lock │ │ └── Pods │ │ ├── Manifest.lock │ │ ├── Pods.xcodeproj │ │ └── project.pbxproj │ │ ├── Target Support Files │ │ ├── Pods-JFCitySelector Demo │ │ │ ├── Pods-JFCitySelector Demo-Info.plist │ │ │ ├── Pods-JFCitySelector Demo-acknowledgements.markdown │ │ │ ├── Pods-JFCitySelector Demo-acknowledgements.plist │ │ │ ├── Pods-JFCitySelector Demo-dummy.m │ │ │ ├── Pods-JFCitySelector Demo-frameworks-Debug-input-files.xcfilelist │ │ │ ├── Pods-JFCitySelector Demo-frameworks-Debug-output-files.xcfilelist │ │ │ ├── Pods-JFCitySelector Demo-frameworks-Release-input-files.xcfilelist │ │ │ ├── Pods-JFCitySelector Demo-frameworks-Release-output-files.xcfilelist │ │ │ ├── Pods-JFCitySelector Demo-frameworks.sh │ │ │ ├── Pods-JFCitySelector Demo-umbrella.h │ │ │ ├── Pods-JFCitySelector Demo.debug.xcconfig │ │ │ ├── Pods-JFCitySelector Demo.modulemap │ │ │ └── Pods-JFCitySelector Demo.release.xcconfig │ │ └── YYModel │ │ │ ├── YYModel-Info.plist │ │ │ ├── YYModel-dummy.m │ │ │ ├── YYModel-prefix.pch │ │ │ ├── YYModel-umbrella.h │ │ │ ├── YYModel.modulemap │ │ │ └── YYModel.xcconfig │ │ └── YYModel │ │ ├── LICENSE │ │ ├── README.md │ │ └── YYModel │ │ ├── NSObject+YYModel.h │ │ ├── NSObject+YYModel.m │ │ ├── YYClassInfo.h │ │ ├── YYClassInfo.m │ │ └── YYModel.h └── Resources │ └── JFCitySelector.bundle │ ├── areas.plist │ ├── cities.plist │ ├── jf_icon_down@3x.png │ ├── jf_icon_navi_return@2x.png │ ├── jf_icon_navi_return@3x.png │ ├── jf_icon_search_button@3x.png │ ├── jf_icon_up@3x.png │ └── provinces.plist ├── LICENSE └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | # OS X 2 | .DS_Store 3 | 4 | # Xcode 5 | build/ 6 | *.pbxuser 7 | !default.pbxuser 8 | *.mode1v3 9 | !default.mode1v3 10 | *.mode2v3 11 | !default.mode2v3 12 | *.perspectivev3 13 | !default.perspectivev3 14 | xcuserdata/ 15 | *.xccheckout 16 | profile 17 | *.moved-aside 18 | DerivedData 19 | *.hmap 20 | *.ipa 21 | *.podspec 22 | 23 | # Bundler 24 | .bundle 25 | 26 | 27 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 28 | # Carthage/Checkouts 29 | 30 | Carthage/Build 31 | 32 | # We recommend against adding the Pods directory to your .gitignore. However 33 | # you should judge for yourself, the pros and cons are mentioned at: 34 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control 35 | # 36 | # Note: if you ignore the Pods directory, make sure to uncomment 37 | # `pod install` in .travis.yml 38 | # 39 | # Pods/ 40 | -------------------------------------------------------------------------------- /JFCitySelector/Assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhifenx/JFCitySelector/ccc1bb113959df9066d5f639a1ed807323f62d74/JFCitySelector/Assets/.gitkeep -------------------------------------------------------------------------------- /JFCitySelector/Classes/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhifenx/JFCitySelector/ccc1bb113959df9066d5f639a1ed807323f62d74/JFCitySelector/Classes/.gitkeep -------------------------------------------------------------------------------- /JFCitySelector/Classes/JFCSAreaModel.h: -------------------------------------------------------------------------------- 1 | // 2 | // JFCSAreaModel.h 3 | // jfcityselector 4 | // 5 | // Created by zhifenx on 2019/7/11. 6 | // Copyright © 2019 zhifenx. All rights reserved. 7 | // 8 | 9 | #import "JFCSBaseInfoModel.h" 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | @interface JFCSArea : JFCSBaseInfoModel 14 | 15 | @property (nonatomic, assign) NSInteger provinceCode; 16 | @property (nonatomic, assign) NSInteger cityCode; 17 | 18 | @end 19 | 20 | @interface JFCSAreaModel : NSObject 21 | 22 | @property (nonatomic, strong) NSArray *areas; 23 | 24 | @end 25 | 26 | NS_ASSUME_NONNULL_END 27 | -------------------------------------------------------------------------------- /JFCitySelector/Classes/JFCSAreaModel.m: -------------------------------------------------------------------------------- 1 | // 2 | // JFCSAreaModel.m 3 | // jfcityselector 4 | // 5 | // Created by zhifenx on 2019/7/11. 6 | // Copyright © 2019 zhifenx. All rights reserved. 7 | // 8 | 9 | #import "JFCSAreaModel.h" 10 | 11 | @implementation JFCSArea 12 | 13 | @end 14 | 15 | @implementation JFCSAreaModel 16 | 17 | + (NSDictionary *)modelContainerPropertyGenericClass { 18 | return @{@"areas" : [JFCSArea class] 19 | }; 20 | } 21 | 22 | @end 23 | -------------------------------------------------------------------------------- /JFCitySelector/Classes/JFCSBaseInfoModel.h: -------------------------------------------------------------------------------- 1 | // 2 | // JFCSBaseInfoModel.h 3 | // jfcityselector 4 | // 5 | // Created by zhifenx on 2019/7/11. 6 | // Copyright © 2019 zhifenx. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | @interface JFCSBaseInfoModel : NSObject 14 | 15 | @property (nonatomic, assign) NSInteger code; 16 | @property (nonatomic, copy) NSString *name; 17 | @property (nonatomic, copy) NSString *pinyin; 18 | @property (nonatomic, copy) NSString *firstLetter; 19 | 20 | /** 21 | 重复的城市名称,用此区别 22 | */ 23 | @property (nonatomic, copy) NSString *alias; 24 | 25 | @end 26 | 27 | NS_ASSUME_NONNULL_END 28 | -------------------------------------------------------------------------------- /JFCitySelector/Classes/JFCSBaseInfoModel.m: -------------------------------------------------------------------------------- 1 | // 2 | // JFCSBaseInfoModel.m 3 | // jfcityselector 4 | // 5 | // Created by zhifenx on 2019/7/11. 6 | // Copyright © 2019 zhifenx. All rights reserved. 7 | // 8 | 9 | #import "JFCSBaseInfoModel.h" 10 | 11 | @implementation JFCSBaseInfoModel 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /JFCitySelector/Classes/JFCSCityModel.h: -------------------------------------------------------------------------------- 1 | // 2 | // JFCSCityModel.h 3 | // jfcityselector 4 | // 5 | // Created by zhifenx on 2019/7/11. 6 | // Copyright © 2019 zhifenx. All rights reserved. 7 | // 8 | 9 | #import "JFCSBaseInfoModel.h" 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | @interface JFCSCity : JFCSBaseInfoModel 14 | 15 | @property (nonatomic, assign) NSInteger provinceCode; 16 | 17 | @end 18 | 19 | @interface JFCSCityModel : NSObject 20 | 21 | @property (nonatomic, strong) NSArray *cities; 22 | 23 | @end 24 | 25 | NS_ASSUME_NONNULL_END 26 | -------------------------------------------------------------------------------- /JFCitySelector/Classes/JFCSCityModel.m: -------------------------------------------------------------------------------- 1 | // 2 | // JFCSCityModel.m 3 | // jfcityselector 4 | // 5 | // Created by zhifenx on 2019/7/11. 6 | // Copyright © 2019 zhifenx. All rights reserved. 7 | // 8 | 9 | #import "JFCSCityModel.h" 10 | 11 | @implementation JFCSCity 12 | 13 | @end 14 | 15 | @implementation JFCSCityModel 16 | 17 | + (NSDictionary *)modelContainerPropertyGenericClass { 18 | return @{@"cities" : [JFCSCity class] 19 | }; 20 | } 21 | 22 | @end 23 | -------------------------------------------------------------------------------- /JFCitySelector/Classes/JFCSConfiguration.h: -------------------------------------------------------------------------------- 1 | // 2 | // JFCSConfiguration.h 3 | // jfcityselector 4 | // 5 | // Created by zhifenx on 2019/7/11. 6 | // Copyright © 2019 zhifenx. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @import UIKit; 12 | 13 | @class JFCSPopularCitiesModel; 14 | 15 | NS_ASSUME_NONNULL_BEGIN 16 | 17 | #pragma mark - 数据相关配置 18 | 19 | @interface JFCSConfiguration : NSObject 20 | 21 | /** 22 | 是否可以拼音搜索,默认 YES 23 | */ 24 | @property (nonatomic, assign) BOOL isPinyinSearch; 25 | 26 | /** 27 | 搜索时字母是否区分大小写 默认 NO 不区分大小写 28 | */ 29 | @property (nonatomic, assign) BOOL isLowercaseString; 30 | 31 | /** 32 | 关闭热门城市,默认 NO 33 | */ 34 | @property (nonatomic, assign) BOOL hidePopularCities; 35 | 36 | /** 37 | 关闭最近访问 默认 NO 38 | */ 39 | @property (nonatomic, assign) BOOL hideHistoricalRecord; 40 | 41 | /** 42 | 热门城市,默认 北京、上海、广州、深圳、杭州 43 | 注意:1、name要和本地数据的一致; 44 | 2、type 省级传 JFCSPopularCitiesTypeProvince,市级传 JFCSPopularCitiesTypeCity,县级传 JFCSPopularCitiesTypeArea“ 45 | */ 46 | @property (nonatomic, strong) NSMutableArray *popularCitiesMutableArray; 47 | 48 | /** 49 | 默认 ”热门城市” 50 | */ 51 | @property (nonatomic, copy) NSString *popularCitiesTitle; 52 | 53 | /** 54 | 热门城市位于Table view 左侧index标识 默认取popularCitiesTitle 的前两个字符,若popularCitiesTitle length < 2 则为”*“ 55 | */ 56 | @property (nonatomic, copy) NSString *popularCitiesAbbreviation; 57 | 58 | /** 59 | 默认 最近访问 60 | */ 61 | @property (nonatomic, copy) NSString *historicalRecordTitle; 62 | 63 | 64 | /** 65 | 最近访问位于Table view 左侧index标识 默认取historicalRecordTitle 的前两个字符,若historicalRecordTitle length < 2 则为”#“ 66 | */ 67 | @property (nonatomic, copy) NSString *historicalRecordAbbreviation; 68 | 69 | /** 70 | 热门城市cell的高度 71 | */ 72 | @property (nonatomic, assign, readonly) CGFloat popularCitiesCellHeight; 73 | 74 | /** 75 | 最近访问记录的最大值 默认 3 76 | */ 77 | @property (nonatomic, assign) NSInteger maxHistoricalRecordCount; 78 | 79 | 80 | #pragma mark - JFCSTableViewController 相关配置 81 | 82 | /** 83 | 隐藏区域切换按钮 默认 NO 84 | */ 85 | @property (nonatomic, assign) BOOL hideAreaSwitchButton; 86 | 87 | /** 88 | JFCSTableViewController 顶部 title view 的button 89 | */ 90 | @property (nonatomic, strong) UIButton *searchButton; 91 | 92 | /** 93 | JFCSTableViewController leftBarButtonItem icon 94 | */ 95 | @property (nonatomic, copy) NSString *leftBarButtonItemImageName; 96 | 97 | /** 98 | JFCSTableViewController 顶部searchButton title 99 | */ 100 | @property (nonatomic, copy) NSString *searchButtonTitle; 101 | 102 | /** 103 | JFCSTableViewController 顶部searchButton icon name 104 | */ 105 | @property (nonatomic, copy) NSString *searchButtonImageName; 106 | 107 | /** 108 | JFCSTableViewController 顶部searchButton backgroundColor 109 | */ 110 | @property (nonatomic, strong) UIColor *searchButtonBackgroundColor; 111 | 112 | /** 113 | JFCSTableViewController 顶部searchButton title backgroundColor 114 | */ 115 | @property (nonatomic, strong) UIColor *searchButtonTitleColor; 116 | 117 | /** 118 | JFCSTableViewController 左侧 index color 119 | */ 120 | @property (nonatomic, strong) UIColor *sectionIndexColor; 121 | 122 | /** 123 | Table view cell text label的字体 124 | */ 125 | @property (nonatomic, strong) UIFont *tableViewCellTextLabelFont; 126 | 127 | #pragma mark -- JFCSSearchTableViewController 相关配置 128 | 129 | /** 130 | JFCSSearchTableViewController 顶部搜索取消按钮文字 默认”取消“ 131 | */ 132 | @property (nonatomic, strong) NSString *searchControllerCancelButtonTitle; 133 | 134 | /** 135 | JFCSSearchTableViewController 数据库里没有该数据时搜索页面提示的信息 默认”抱歉,未找到相关位置,可尝试修改后重试“ 136 | */ 137 | @property (nonatomic, strong) NSString *promptInformation; 138 | 139 | /** 140 | JFCSSearchTableViewController 顶部搜索取消按钮font 默认[UIFont systemFontOfSize:12.0] 141 | */ 142 | @property (nonatomic, strong) UIFont *searchControllerCancelButtonTitleFont; 143 | 144 | /** 145 | 统一计算cell的高度方法 146 | 147 | @param count 展示的城市个数 148 | @return cell高度 149 | */ 150 | - (CGFloat)calculateCellHeightWithCount:(NSInteger)count; 151 | 152 | @end 153 | NS_ASSUME_NONNULL_END 154 | -------------------------------------------------------------------------------- /JFCitySelector/Classes/JFCSConfiguration.m: -------------------------------------------------------------------------------- 1 | // 2 | // JFCSConfiguration.m 3 | // jfcityselector 4 | // 5 | // Created by zhifenx on 2019/7/11. 6 | // Copyright © 2019 zhifenx. All rights reserved. 7 | // 8 | 9 | #import "JFCSConfiguration.h" 10 | 11 | #import "JFCSPopularCitiesModel.h" 12 | 13 | @implementation JFCSConfiguration 14 | 15 | - (instancetype)init { 16 | self = [super init]; 17 | if (self) { 18 | _isPinyinSearch = YES; 19 | _isLowercaseString = NO; 20 | _hidePopularCities = NO; 21 | _hideHistoricalRecord = NO; 22 | _hideAreaSwitchButton = NO; 23 | _searchButtonBackgroundColor = [UIColor whiteColor]; 24 | _searchButtonTitleColor = [UIColor colorWithRed:0.5 green:0.5 blue:0.5 alpha:1]; 25 | _sectionIndexColor = [UIColor colorWithRed:0/255.0f green:132/255.0f blue:255/255.0f alpha:1]; 26 | _maxHistoricalRecordCount = 3; 27 | 28 | self.popularCitiesMutableArray = [self defealtPopularCities]; 29 | self.popularCitiesTitle = @"热门城市"; 30 | self.historicalRecordTitle = @"最近访问"; 31 | 32 | _leftBarButtonItemImageName = @"jf_icon_navi_return"; 33 | _searchButtonImageName = @"jf_icon_search_button"; 34 | _searchButtonTitle = @"城市名/拼音"; 35 | _searchControllerCancelButtonTitle = @"取消"; 36 | _searchControllerCancelButtonTitleFont = [UIFont systemFontOfSize:14.0]; 37 | _tableViewCellTextLabelFont = [UIFont systemFontOfSize:14.0]; 38 | _promptInformation = @"抱歉,未找到相关位置,可尝试修改后重试"; 39 | } 40 | return self; 41 | } 42 | 43 | #pragma mark -- Set 44 | 45 | - (void)setPopularCitiesMutableArray:(NSMutableArray *)popularCitiesMutableArray { 46 | _popularCitiesMutableArray = popularCitiesMutableArray; 47 | _popularCitiesCellHeight = [self calculateCellHeightWithCount:popularCitiesMutableArray.count]; 48 | } 49 | 50 | - (void)setIsPinyinSearch:(BOOL)isPinyinSearch { 51 | _isPinyinSearch = isPinyinSearch; 52 | _searchButtonTitle = isPinyinSearch ? _searchButtonTitle : @"城市名"; 53 | } 54 | 55 | - (void)setPopularCitiesTitle:(NSString *)popularCitiesTitle { 56 | _popularCitiesTitle = popularCitiesTitle; 57 | _popularCitiesAbbreviation = popularCitiesTitle.length >= 2 ? [popularCitiesTitle substringToIndex:2] : @"*"; 58 | } 59 | 60 | - (void)setHistoricalRecordTitle:(NSString *)historicalRecordTitle { 61 | _historicalRecordTitle = historicalRecordTitle; 62 | _historicalRecordAbbreviation = historicalRecordTitle.length >= 2 ? [historicalRecordTitle substringToIndex:2] : @"#"; 63 | } 64 | 65 | #pragma mark -- Get 66 | 67 | - (NSMutableArray *)defealtPopularCities { 68 | JFCSPopularCitiesModel *bjModel = [[JFCSPopularCitiesModel alloc] initWithName:@"北京" type:JFCSPopularCitiesTypeCity]; 69 | JFCSPopularCitiesModel *shModel = [[JFCSPopularCitiesModel alloc] initWithName:@"上海" type:JFCSPopularCitiesTypeCity]; 70 | JFCSPopularCitiesModel *gzModel = [[JFCSPopularCitiesModel alloc] initWithName:@"广州" type:JFCSPopularCitiesTypeCity]; 71 | JFCSPopularCitiesModel *szModel = [[JFCSPopularCitiesModel alloc] initWithName:@"深圳" type:JFCSPopularCitiesTypeCity]; 72 | JFCSPopularCitiesModel *hzModel = [[JFCSPopularCitiesModel alloc] initWithName:@"杭州" type:JFCSPopularCitiesTypeCity]; 73 | return [NSMutableArray arrayWithObjects:bjModel, shModel, gzModel, szModel, hzModel, nil]; 74 | } 75 | 76 | #pragma mark -- Action 77 | 78 | - (CGFloat)calculateCellHeightWithCount:(NSInteger)count { 79 | NSInteger tempCount = count / 3; 80 | if ((count % 3) > 0) { 81 | tempCount += 1; 82 | } 83 | return tempCount * 48; 84 | } 85 | 86 | @end 87 | -------------------------------------------------------------------------------- /JFCitySelector/Classes/JFCSData.h: -------------------------------------------------------------------------------- 1 | // 2 | // JFCSData.h 3 | // jfcityselector 4 | // 5 | // Created by zhifenx on 2019/7/11. 6 | // Copyright © 2019 zhifenx. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | #import "JFCSProvinceModel.h" 12 | #import "JFCSCityModel.h" 13 | #import "JFCSAreaModel.h" 14 | 15 | NS_ASSUME_NONNULL_BEGIN 16 | 17 | #define JF_SINGLETON_DEF(_type_) + (_type_ *)shareInstance;\ 18 | +(instancetype) alloc __attribute__((unavailable("call shareInstance instead")));\ 19 | +(instancetype) new __attribute__((unavailable("call shareInstance instead")));\ 20 | -(instancetype) copy __attribute__((unavailable("call shareInstance instead")));\ 21 | -(instancetype) mutableCopy __attribute__((unavailable("call shareInstance instead")));\ 22 | 23 | @interface JFCSData : NSObject 24 | 25 | JF_SINGLETON_DEF(JFCSData); 26 | 27 | @property (nonatomic, strong) NSArray *provinces; 28 | @property (nonatomic, strong) NSArray *cities; 29 | @property (nonatomic, strong) NSArray *areas; 30 | 31 | @end 32 | 33 | NS_ASSUME_NONNULL_END 34 | -------------------------------------------------------------------------------- /JFCitySelector/Classes/JFCSData.m: -------------------------------------------------------------------------------- 1 | // 2 | // JFCSData.m 3 | // jfcityselector 4 | // 5 | // Created by zhifenx on 2019/7/11. 6 | // Copyright © 2019 zhifenx. All rights reserved. 7 | // 8 | 9 | #import "JFCSData.h" 10 | 11 | #import "JFCSFileManager.h" 12 | #import 13 | 14 | #define JF_SINGLETON_IMP(_type_) + (_type_ *)shareInstance{\ 15 | static _type_ *theshareInstance = nil;\ 16 | static dispatch_once_t onceToken;\ 17 | dispatch_once(&onceToken, ^{\ 18 | theshareInstance = [[super alloc] init];\ 19 | });\ 20 | return theshareInstance;\ 21 | } 22 | 23 | @interface JFCSData () 24 | 25 | @property (nonatomic, strong) JFCSProvinceModel *provincemodel; 26 | @property (nonatomic, strong) JFCSCityModel *citymodel; 27 | @property (nonatomic, strong) JFCSAreaModel *areaModel; 28 | 29 | @end 30 | 31 | @implementation JFCSData 32 | 33 | JF_SINGLETON_IMP(JFCSData); 34 | 35 | - (instancetype)init { 36 | self = [super init]; 37 | if (self) { 38 | [self initData]; 39 | } 40 | return self; 41 | } 42 | 43 | - (void)initData { 44 | NSString *provincePlistPath = [[JFCSFileManager jFCitySelectorBundle] pathForResource:@"provinces" ofType:@"plist"]; 45 | NSString *cityPlistPath = [[JFCSFileManager jFCitySelectorBundle] pathForResource:@"cities" ofType:@"plist"]; 46 | NSString *areaPlistPath = [[JFCSFileManager jFCitySelectorBundle] pathForResource:@"areas" ofType:@"plist"]; 47 | NSDictionary *provinceDic = [[NSDictionary alloc] initWithContentsOfFile:provincePlistPath]; 48 | NSDictionary *cityDic = [[NSDictionary alloc] initWithContentsOfFile:cityPlistPath]; 49 | NSDictionary *areaDic = [[NSDictionary alloc] initWithContentsOfFile:areaPlistPath]; 50 | self.provincemodel = [JFCSProvinceModel yy_modelWithJSON:provinceDic]; 51 | self.citymodel = [JFCSCityModel yy_modelWithJSON:cityDic]; 52 | self.areaModel = [JFCSAreaModel yy_modelWithJSON:areaDic]; 53 | } 54 | 55 | #pragma mark -- Get 56 | 57 | - (NSArray *)provinces { 58 | if (!_provinces) { 59 | _provinces = [NSArray new]; 60 | NSSortDescriptor *sortDes = [[NSSortDescriptor alloc] initWithKey:@"pinyin" ascending:YES]; 61 | _provinces = [[self.provincemodel.provinces sortedArrayUsingDescriptors:@[sortDes]] mutableCopy]; 62 | } 63 | return _provinces; 64 | } 65 | 66 | - (NSArray *)cities { 67 | if (!_cities) { 68 | _cities = [NSArray new]; 69 | NSSortDescriptor *sortDes = [[NSSortDescriptor alloc] initWithKey:@"pinyin" ascending:YES]; 70 | _cities = [[self.citymodel.cities sortedArrayUsingDescriptors:@[sortDes]] mutableCopy]; 71 | } 72 | return _cities; 73 | } 74 | 75 | - (NSArray *)areas { 76 | if (!_areas) { 77 | _areas = [NSArray new]; 78 | NSSortDescriptor *sortDes = [[NSSortDescriptor alloc] initWithKey:@"pinyin" ascending:YES]; 79 | _areas = [[self.areaModel.areas sortedArrayUsingDescriptors:@[sortDes]] mutableCopy]; 80 | } 81 | return _areas; 82 | } 83 | 84 | @end 85 | -------------------------------------------------------------------------------- /JFCitySelector/Classes/JFCSDataOpreation.h: -------------------------------------------------------------------------------- 1 | // 2 | // JFCSDataOpreation.h 3 | // jfcityselector 4 | // 5 | // Created by zhifenx on 2019/7/10. 6 | // Copyright © 2019 zhifenx. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @class JFCSData; 12 | @class JFCSProvince; 13 | @class JFCSCity; 14 | @class JFCSArea; 15 | @class JFCSBaseInfoModel; 16 | @class JFCSConfiguration; 17 | @class JFCSPopularCitiesModel; 18 | 19 | NS_ASSUME_NONNULL_BEGIN 20 | 21 | @interface JFCSDataOpreation : NSObject 22 | 23 | /** 24 | 初始化 需传入JFCSConfiguration的对象 25 | 26 | @param config config 27 | @return JFCSDataOpreation实例 28 | */ 29 | - (instancetype)initWithConfiguration:(JFCSConfiguration *)config; 30 | 31 | /** 32 | 省市县三级城市数据 33 | */ 34 | @property (nonatomic, strong, readonly) JFCSData *data; 35 | 36 | /** 37 | 热门城市,可通过JFCSConfiguration进行自定义 38 | */ 39 | @property (nonatomic, strong, readonly) NSMutableArray *popularCitiesMutaleArray; 40 | 41 | /** 42 | 省份名字包含的首字母 43 | */ 44 | @property (nonatomic, strong) NSArray *firstLetterArraryOfProvince; 45 | 46 | /** 47 | 城市名字包含的首字母 48 | */ 49 | @property (nonatomic, strong) NSArray *firstLetterArraryOfCity; 50 | 51 | /** 52 | 县区名字包含的首字母 53 | */ 54 | @property (nonatomic, strong) NSArray *firstLetterArraryOfArea; 55 | 56 | /** 57 | 所有名称包含的首字母 58 | */ 59 | @property (nonatomic, strong) NSArray *firstLetterArraryOfAllCities; 60 | 61 | /** 62 | 市县包含的首字母 63 | */ 64 | @property (nonatomic, strong) NSArray *firstLetterArraryOfCityOrArea; 65 | 66 | /** 67 | 所有省份数据 68 | 69 | @param block 数据回调 70 | */ 71 | - (void)provinces:(void(^)(NSArray *provinces))block; 72 | 73 | /** 74 | 所有市区数据 75 | 76 | @param block 数据回调 77 | */ 78 | - (void)cities:(void(^)(NSArray *cities))block; 79 | 80 | /** 81 | 所有县城数据 82 | 83 | @param block 数据回调 84 | */ 85 | - (void)areas:(void(^)(NSArray *areas))block; 86 | 87 | /** 88 | 市县城市名称 89 | 90 | @param block 数据回调 91 | */ 92 | - (void)cityList:(void(^)(NSArray *cities))block; 93 | 94 | /** 95 | 按首字母分组的市县数据 96 | 97 | @param block 数据回调 98 | */ 99 | - (void)arrayOfCityAndArea:(void(^)(NSMutableArray *data))block; 100 | 101 | #pragma mark -- 通过code查询城市model 102 | 103 | - (JFCSProvince *)getProvinceWithCode:(NSInteger)code; 104 | 105 | - (JFCSCity *)getCityWithCode:(NSInteger)code; 106 | 107 | - (JFCSArea *)getAreaWithCode:(NSInteger)code; 108 | 109 | #pragma mark -- 通过name查询城市model 110 | 111 | - (JFCSProvince *)getProvinceWithName:(NSString *)name; 112 | 113 | - (JFCSCity *)getCityWithName:(NSString *)name; 114 | 115 | - (JFCSArea *)getAreaWithName:(NSString *)name; 116 | 117 | #pragma mark -- 模糊查找 118 | 119 | /** 120 | 通过汉字或者pinyin搜索城市 是否开启拼音搜索通过 配置 JFCSConfiguration 121 | 122 | @param keyword 关键字 123 | @param result 结果 124 | */ 125 | - (void)searchWithKeyword:(NSString *)keyword resultBlock:(void(^)(NSArray *dataArray))result; 126 | 127 | /** 128 | 通过汉字或者pinyin搜索省份 129 | 130 | @param string 关键字 131 | @return 结果 132 | */ 133 | - (NSArray *)searchProvinceWithString:(NSString *)string; 134 | 135 | /** 136 | 通过汉字或者pinyin搜索市区 137 | 138 | @param string 关键字 139 | @return 结果 140 | */ 141 | - (NSArray *)searchCityWithString:(NSString *)string; 142 | 143 | /** 144 | 通过汉字或者pinyin搜索县区 145 | 146 | @param string 关键字 147 | @return 结果 148 | */ 149 | - (NSArray *)searchAreaWithString:(NSString *)string; 150 | 151 | #pragma mark -- 最近访问的城市 最多存储三个 152 | 153 | /** 154 | 添加新的最近访问城市 155 | 156 | @param model JFCSBaseInfoModel实例 157 | */ 158 | - (void)insertHistoryRecordCityModel:(JFCSBaseInfoModel *)model; 159 | 160 | /** 161 | 最近访问城市的数组 162 | 163 | @return 数据 164 | */ 165 | - (NSArray *)historyRecordCities; 166 | 167 | /** 168 | 存储当前城市 169 | 170 | @param model JFCSBaseInfoModel实例 171 | */ 172 | - (void)cacheCurrentCity:(JFCSBaseInfoModel *)model; 173 | 174 | /** 175 | 当前城市 176 | 177 | @return JFCSBaseInfoModel实例 178 | */ 179 | - (JFCSBaseInfoModel *)currentCity; 180 | 181 | #pragma mark -- 处理切换区县所需的数据 182 | 183 | /** 184 | 1、传入县级城市,则返回该城市所属市下同级的县级城市 185 | 2、传入市级城市,则返回该市下所有的县级城市数据 186 | 3、传入省,返回的则是该省下的所有市数据 187 | 188 | @param model JFCSBaseInfoModel实例 189 | @param block 县级城市数据 190 | */ 191 | - (void)otherCitiesWithCityModel:(JFCSBaseInfoModel *)model resultArray:(void(^)(NSArray * dataArray))block; 192 | 193 | @end 194 | 195 | NS_ASSUME_NONNULL_END 196 | -------------------------------------------------------------------------------- /JFCitySelector/Classes/JFCSDataOpreation.m: -------------------------------------------------------------------------------- 1 | // 2 | // JFCSDataOpreation.m 3 | // jfcityselector 4 | // 5 | // Created by zhifenx on 2019/7/10. 6 | // Copyright © 2019 zhifenx. All rights reserved. 7 | // 8 | 9 | #import "JFCSDataOpreation.h" 10 | 11 | #import "JFCSProvinceModel.h" 12 | #import "JFCSCityModel.h" 13 | #import "JFCSAreaModel.h" 14 | #import "JFCSData.h" 15 | #import "JFCSConfiguration.h" 16 | #import 17 | 18 | // 弱引用 19 | #define JFWeakSelf(type) __weak typeof(type) weak##type = type; 20 | // 强引用 21 | #define JFStrongSelf(type) __strong typeof(type) type = weak##type; 22 | 23 | @interface JFCSDataOpreation () 24 | 25 | @property (nonatomic, strong) JFCSConfiguration *configuration; 26 | @property (nonatomic, strong) NSMutableArray *arrayOfCityAndAreaModel; 27 | @property (nonatomic, strong) NSMutableArray *arrayOfCityAndArea; 28 | 29 | @end 30 | 31 | @implementation JFCSDataOpreation 32 | 33 | - (instancetype)initWithConfiguration:(JFCSConfiguration *)config { 34 | self = [super init]; 35 | if (self) { 36 | _configuration = config; 37 | _data = [JFCSData shareInstance]; 38 | } 39 | return self; 40 | } 41 | 42 | - (void)cityList:(void(^)(NSArray *cities))block { 43 | if (block) { 44 | block([self.arrayOfCityAndAreaModel mutableCopy]); 45 | } 46 | } 47 | 48 | - (void)arrayOfCityAndArea:(void(^)(NSMutableArray *data))block { 49 | if (block) { 50 | block(self.arrayOfCityAndArea); 51 | } 52 | } 53 | 54 | - (void)provinces:(void(^)(NSArray *provinces))block { 55 | if (block) { 56 | block(_data.provinces); 57 | } 58 | } 59 | 60 | - (void)cities:(void(^)(NSArray *cities))block { 61 | if (block) { 62 | block(_data.cities); 63 | } 64 | } 65 | 66 | - (void)areas:(void(^)(NSArray *areas))block { 67 | if (block) { 68 | block(_data.areas); 69 | } 70 | } 71 | 72 | #pragma mark -- Get 73 | 74 | - (NSMutableArray *)popularCitiesMutaleArray { 75 | return [_configuration popularCitiesMutableArray]; 76 | } 77 | 78 | #pragma mark -- 通过code查询城市model 79 | 80 | - (JFCSProvince *)getProvinceWithCode:(NSInteger)code { 81 | __block JFCSProvince *model = [[JFCSProvince alloc] init]; 82 | [_data.provinces enumerateObjectsUsingBlock:^(JFCSProvince * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { 83 | if (obj.code == code) { 84 | model = obj; 85 | *stop = YES; 86 | } 87 | }]; 88 | return model; 89 | } 90 | 91 | - (JFCSCity *)getCityWithCode:(NSInteger)code { 92 | __block JFCSCity *model = [[JFCSCity alloc] init]; 93 | [_data.cities enumerateObjectsUsingBlock:^(JFCSCity * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { 94 | if (obj.code == code) { 95 | model = obj; 96 | *stop = YES; 97 | } 98 | }]; 99 | return model; 100 | } 101 | 102 | - (JFCSArea *)getAreaWithCode:(NSInteger)code { 103 | __block JFCSArea *model = [[JFCSArea alloc] init]; 104 | [_data.areas enumerateObjectsUsingBlock:^(JFCSArea * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { 105 | if (obj.code == code) { 106 | model = obj; 107 | *stop = YES; 108 | } 109 | }]; 110 | return model; 111 | } 112 | 113 | #pragma mark -- 通过name查询城市model 114 | 115 | - (JFCSProvince *)getProvinceWithName:(NSString *)name { 116 | __block JFCSProvince *model = [[JFCSProvince alloc] init]; 117 | [_data.provinces enumerateObjectsUsingBlock:^(JFCSProvince * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { 118 | if ([obj.name isEqualToString:name]) { 119 | model = obj; 120 | *stop = YES; 121 | } 122 | }]; 123 | return model; 124 | } 125 | 126 | - (JFCSCity *)getCityWithName:(NSString *)name { 127 | __block JFCSCity *model = [[JFCSCity alloc] init]; 128 | [_data.cities enumerateObjectsUsingBlock:^(JFCSCity * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { 129 | if ([obj.name isEqualToString:name]) { 130 | model = obj; 131 | *stop = YES; 132 | } 133 | }]; 134 | return model; 135 | } 136 | 137 | - (JFCSArea *)getAreaWithName:(NSString *)name { 138 | __block JFCSArea *model = [[JFCSArea alloc] init]; 139 | [_data.areas enumerateObjectsUsingBlock:^(JFCSArea * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { 140 | if ([obj.name isEqualToString:name]) { 141 | model = obj; 142 | *stop = YES; 143 | } 144 | }]; 145 | return model; 146 | } 147 | 148 | #pragma mark -- 模糊查找(汉字或者拼音) 149 | - (void)searchWithKeyword:(NSString *)keyword resultBlock:(void (^)(NSArray * _Nonnull))result { 150 | if (!_configuration.isLowercaseString) {keyword = [keyword lowercaseString];} 151 | NSArray *pArr = [self searchProvinceWithString:keyword]; 152 | NSArray *cArr = [self searchCityWithString:keyword]; 153 | NSArray *aArr = [self searchAreaWithString:keyword]; 154 | NSMutableArray *rArr = [NSMutableArray new]; 155 | [rArr addObjectsFromArray:pArr]; 156 | [rArr addObjectsFromArray:cArr]; 157 | [rArr addObjectsFromArray:aArr]; 158 | if (result) { 159 | result([rArr mutableCopy]); 160 | } 161 | } 162 | 163 | - (NSArray *)searchProvinceWithString:(NSString *)string { 164 | if (!_configuration.isLowercaseString) {string = [string lowercaseString];} 165 | __block NSMutableArray * arr = [NSMutableArray new]; 166 | [_data.provinces enumerateObjectsUsingBlock:^(JFCSProvince * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { 167 | if ([obj.name containsString:string] || (([obj.pinyin containsString:string] && self.configuration.isPinyinSearch) && self.configuration.isPinyinSearch)) { 168 | [arr addObject:obj]; 169 | } 170 | }]; 171 | return arr; 172 | } 173 | 174 | - (NSArray *)searchCityWithString:(NSString *)string { 175 | if (!_configuration.isLowercaseString) {string = [string lowercaseString];} 176 | __block NSMutableArray * arr = [NSMutableArray new]; 177 | [_data.cities enumerateObjectsUsingBlock:^(JFCSCity * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { 178 | if ([obj.name containsString:string] || ([obj.pinyin containsString:string] && self.configuration.isPinyinSearch)) { 179 | [arr addObject:obj]; 180 | } 181 | }]; 182 | return arr; 183 | } 184 | 185 | - (NSArray *)searchAreaWithString:(NSString *)string { 186 | if (!_configuration.isLowercaseString) {string = [string lowercaseString];} 187 | __block NSMutableArray * arr = [NSMutableArray new]; 188 | [_data.areas enumerateObjectsUsingBlock:^(JFCSArea * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { 189 | if ([obj.name containsString:string] || ([obj.pinyin containsString:string] && self.configuration.isPinyinSearch)) { 190 | [arr addObject:obj]; 191 | } 192 | }]; 193 | return arr; 194 | } 195 | 196 | #pragma mark -- Lazy 197 | 198 | - (NSMutableArray *)arrayOfCityAndArea { 199 | if (!_arrayOfCityAndArea) { 200 | _arrayOfCityAndArea = [[NSMutableArray alloc] init]; 201 | for (NSString *str in self.firstLetterArraryOfCityOrArea) { 202 | NSMutableArray *tempArr = [NSMutableArray new]; 203 | for (JFCSBaseInfoModel *model in self.arrayOfCityAndAreaModel) { 204 | if ([model.firstLetter isEqualToString:str]) { 205 | [tempArr addObject:model]; 206 | } 207 | } 208 | [_arrayOfCityAndArea addObject:tempArr]; 209 | } 210 | } 211 | return _arrayOfCityAndArea; 212 | } 213 | 214 | - (NSMutableArray *)arrayOfCityAndAreaModel { 215 | if (!_arrayOfCityAndAreaModel) { 216 | _arrayOfCityAndAreaModel = [[NSMutableArray alloc] init]; 217 | [_arrayOfCityAndAreaModel addObjectsFromArray:_data.cities]; 218 | [_arrayOfCityAndAreaModel addObjectsFromArray:_data.areas]; 219 | } 220 | return _arrayOfCityAndAreaModel; 221 | } 222 | 223 | - (NSArray *)firstLetterArraryOfAllCities { 224 | if (!_firstLetterArraryOfAllCities) { 225 | _firstLetterArraryOfAllCities = [[NSArray alloc] init]; 226 | NSMutableSet *tempSet = [NSMutableSet new]; 227 | [tempSet addObjectsFromArray:self.firstLetterArraryOfProvince]; 228 | [tempSet addObjectsFromArray:self.firstLetterArraryOfCity]; 229 | [tempSet addObjectsFromArray:self.firstLetterArraryOfArea]; 230 | _firstLetterArraryOfAllCities = [[tempSet allObjects] sortedArrayUsingSelector:@selector(compare:)]; 231 | } 232 | return _firstLetterArraryOfAllCities; 233 | } 234 | 235 | - (NSArray *)firstLetterArraryOfCityOrArea { 236 | if (!_firstLetterArraryOfCityOrArea) { 237 | _firstLetterArraryOfCityOrArea = [[NSArray alloc] init]; 238 | NSMutableSet *tempSet = [NSMutableSet new]; 239 | [tempSet addObjectsFromArray:self.firstLetterArraryOfCity]; 240 | [tempSet addObjectsFromArray:self.firstLetterArraryOfArea]; 241 | _firstLetterArraryOfCityOrArea = [[[tempSet allObjects] sortedArrayUsingSelector:@selector(compare:)] mutableCopy]; 242 | } 243 | return _firstLetterArraryOfCityOrArea; 244 | } 245 | 246 | - (NSArray *)firstLetterArraryOfProvince { 247 | if (!_firstLetterArraryOfProvince) { 248 | _firstLetterArraryOfProvince = [NSMutableArray arrayWithArray:[self firstLetterArrary:_data.provinces]]; 249 | } 250 | return _firstLetterArraryOfProvince; 251 | } 252 | 253 | - (NSArray *)firstLetterArraryOfCity { 254 | if (!_firstLetterArraryOfCity) { 255 | _firstLetterArraryOfCity = [[NSArray alloc] init]; 256 | _firstLetterArraryOfCity = [self firstLetterArrary:_data.cities]; 257 | } 258 | return _firstLetterArraryOfCity; 259 | } 260 | 261 | - (NSArray *)firstLetterArraryOfArea { 262 | if (!_firstLetterArraryOfArea) { 263 | _firstLetterArraryOfArea = [[NSArray alloc] init]; 264 | _firstLetterArraryOfArea = [self firstLetterArrary:_data.areas]; 265 | } 266 | return _firstLetterArraryOfArea; 267 | } 268 | 269 | - (NSArray *)firstLetterArrary:(NSArray *)arr { 270 | __block NSMutableSet *set = [NSMutableSet new]; 271 | [arr enumerateObjectsUsingBlock:^(JFCSBaseInfoModel * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { 272 | [set addObject:obj.firstLetter]; 273 | }]; 274 | return [[set allObjects] sortedArrayUsingSelector:@selector(compare:)]; 275 | } 276 | 277 | #pragma mark -- 最近访问的城市 最多存储三个 278 | 279 | #define kHistoryRecordCities @"historyRecordCities" 280 | #define kCurrentCity @"currentCity" 281 | 282 | - (void)cacheCurrentCity:(JFCSBaseInfoModel *)model { 283 | [[NSUserDefaults standardUserDefaults] setObject:[model yy_modelToJSONString] forKey:kCurrentCity]; 284 | } 285 | 286 | - (JFCSBaseInfoModel *)currentCity { 287 | JFCSBaseInfoModel *model = [JFCSBaseInfoModel yy_modelWithJSON:[[NSUserDefaults standardUserDefaults] objectForKey:kCurrentCity]]; 288 | return model; 289 | } 290 | 291 | - (NSArray *)historyRecordCities { 292 | NSMutableArray *arr = [NSMutableArray new]; 293 | NSArray *tempArr = [self jf_getHistoryRecordCities]; 294 | [tempArr enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { 295 | JFCSBaseInfoModel *model = [JFCSBaseInfoModel yy_modelWithJSON:obj]; 296 | [arr addObject:model]; 297 | }]; 298 | return arr; 299 | } 300 | 301 | - (void)insertHistoryRecordCityModel:(JFCSBaseInfoModel *)model { 302 | NSMutableArray *arr = [NSMutableArray arrayWithArray:[[self jf_getHistoryRecordCities] mutableCopy]]; 303 | [arr enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { 304 | JFCSBaseInfoModel *tModel = [JFCSBaseInfoModel yy_modelWithJSON:obj]; 305 | if ([tModel.name isEqualToString:model.name]) { 306 | [arr removeObject:obj]; 307 | *stop = YES; 308 | } 309 | }]; 310 | NSString *json = [model yy_modelToJSONString]; 311 | [arr insertObject:json atIndex:0]; 312 | if (arr.count > _configuration.maxHistoricalRecordCount) { 313 | arr = [[arr subarrayWithRange:NSMakeRange(0, _configuration.maxHistoricalRecordCount)] mutableCopy]; 314 | } 315 | [[NSUserDefaults standardUserDefaults] setObject:[arr copy] forKey:kHistoryRecordCities]; 316 | } 317 | 318 | - (NSArray *)jf_getHistoryRecordCities { 319 | return [[NSUserDefaults standardUserDefaults] objectForKey:kHistoryRecordCities]; 320 | } 321 | 322 | #pragma mark -- 处理切换区县所需的数据 323 | 324 | - (void)otherCitiesWithCityModel:(JFCSBaseInfoModel *)model resultArray:(void (^)(NSArray * _Nonnull))block { 325 | if (model.code > 999 && model.code < 10000) {//市级 326 | if (block) { 327 | block([self otherAreasWithCode:model.code]); 328 | } 329 | }else if (model.code > 99999 && model.code < 1000000) {//县级 330 | if (block) { 331 | block([self otherAreasWithCode:model.code / 100]); 332 | } 333 | }else if (model.code > 0 && model.code < 100) {//省级 334 | if (block) { 335 | block([self otherCitiesWithCode:model.code]); 336 | } 337 | } 338 | } 339 | 340 | - (NSArray *)otherCitiesWithCode:(NSInteger)code { 341 | __block NSMutableArray *cities = [NSMutableArray new]; 342 | [_data.cities enumerateObjectsUsingBlock:^(JFCSCity * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { 343 | if ((obj.code / 100) == code) { 344 | [cities addObject:obj]; 345 | } 346 | }]; 347 | return [cities mutableCopy]; 348 | } 349 | 350 | - (NSArray *)otherAreasWithCode:(NSInteger)code { 351 | __block NSMutableArray *areas = [NSMutableArray new]; 352 | [_data.areas enumerateObjectsUsingBlock:^(JFCSArea * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { 353 | if ((obj.code / 100) == code) { 354 | [areas addObject:obj]; 355 | } 356 | }]; 357 | return [areas mutableCopy]; 358 | } 359 | 360 | @end 361 | -------------------------------------------------------------------------------- /JFCitySelector/Classes/JFCSFileManager.h: -------------------------------------------------------------------------------- 1 | // 2 | // JFCSFileManager.h 3 | // jfcityselector 4 | // 5 | // Created by zhifenx on 2019/7/30. 6 | // Copyright © 2019 zhifenx. All rights reserved. 7 | // 8 | 9 | #import 10 | @import UIKit; 11 | 12 | NS_ASSUME_NONNULL_BEGIN 13 | 14 | @interface JFCSFileManager : NSObject 15 | 16 | + (NSBundle *)jFCitySelectorBundle; 17 | 18 | + (UIImage *)getImageWithName:(NSString *)name; 19 | 20 | @end 21 | 22 | NS_ASSUME_NONNULL_END 23 | -------------------------------------------------------------------------------- /JFCitySelector/Classes/JFCSFileManager.m: -------------------------------------------------------------------------------- 1 | // 2 | // JFCSFileManager.m 3 | // jfcityselector 4 | // 5 | // Created by zhifenx on 2019/7/30. 6 | // Copyright © 2019 zhifenx. All rights reserved. 7 | // 8 | 9 | #import "JFCSFileManager.h" 10 | 11 | // The best order for path scale search. 12 | static NSArray *_NSBundlePreferredScales() { 13 | static NSArray *scales; 14 | static dispatch_once_t onceToken; 15 | dispatch_once(&onceToken, ^{ 16 | CGFloat screenScale = [UIScreen mainScreen].scale; 17 | if (screenScale <= 1) { 18 | scales = @[@1,@2,@3]; 19 | } else if (screenScale <= 2) { 20 | scales = @[@2,@3,@1]; 21 | } else { 22 | scales = @[@3,@2,@1]; 23 | } 24 | }); 25 | return scales; 26 | } 27 | 28 | // Add scale modifier to the file name (without path extension), from @"name" to @"name@2x". 29 | static NSString *_NSStringByAppendingNameScale(NSString *string, CGFloat scale) { 30 | if (!string) return nil; 31 | if (fabs(scale - 1) <= __FLT_EPSILON__ || string.length == 0 || [string hasSuffix:@"/"]) return string.copy; 32 | return [string stringByAppendingFormat:@"@%@x", @(scale)]; 33 | } 34 | 35 | @implementation JFCSFileManager 36 | 37 | + (NSBundle *)jFCitySelectorBundle { 38 | static NSBundle *imageBrowserBundle = nil; 39 | if (imageBrowserBundle == nil) { 40 | NSBundle *bundle = [NSBundle bundleForClass:self.class]; 41 | NSString *path = [bundle pathForResource:@"JFCitySelector" ofType:@"bundle"]; 42 | imageBrowserBundle = [NSBundle bundleWithPath:path]; 43 | } 44 | return imageBrowserBundle; 45 | } 46 | 47 | + (UIImage *)getImageWithName:(NSString *)name { 48 | //Imitate 'YYImage', but don't need to determine image type, they are all 'png'. 49 | NSString *res = name, *path = nil; 50 | CGFloat scale = 1; 51 | NSArray *scales = _NSBundlePreferredScales(); 52 | for (int s = 0; s < scales.count; s++) { 53 | scale = ((NSNumber *)scales[s]).floatValue; 54 | NSString *scaledName = _NSStringByAppendingNameScale(res, scale); 55 | path = [[self jFCitySelectorBundle] pathForResource:scaledName ofType:@"png"]; 56 | if (path) break; 57 | } 58 | if (!path.length) return nil; 59 | return [UIImage imageWithContentsOfFile:path]; 60 | } 61 | 62 | @end 63 | -------------------------------------------------------------------------------- /JFCitySelector/Classes/JFCSPopularCitiesModel.h: -------------------------------------------------------------------------------- 1 | // 2 | // JFCSPopularCitiesModel.h 3 | // jfcityselector 4 | // 5 | // Created by zhifenx on 2019/7/24. 6 | // Copyright © 2019 zhifenx. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | typedef NS_ENUM(NSInteger, JFCSPopularCitiesType) { 14 | JFCSPopularCitiesTypeProvince = 0, 15 | JFCSPopularCitiesTypeCity = 1, 16 | JFCSPopularCitiesTypeArea = 2, 17 | }; 18 | 19 | @interface JFCSPopularCitiesModel : NSObject 20 | 21 | @property (nonatomic, copy) NSString *name; 22 | @property (nonatomic, assign) JFCSPopularCitiesType type; 23 | 24 | - (instancetype)initWithName:(NSString *)name type:(JFCSPopularCitiesType)type; 25 | 26 | @end 27 | 28 | NS_ASSUME_NONNULL_END 29 | -------------------------------------------------------------------------------- /JFCitySelector/Classes/JFCSPopularCitiesModel.m: -------------------------------------------------------------------------------- 1 | // 2 | // JFCSPopularCitiesModel.m 3 | // jfcityselector 4 | // 5 | // Created by zhifenx on 2019/7/24. 6 | // Copyright © 2019 zhifenx. All rights reserved. 7 | // 8 | 9 | #import "JFCSPopularCitiesModel.h" 10 | 11 | @implementation JFCSPopularCitiesModel 12 | 13 | - (instancetype)initWithName:(NSString *)name type:(JFCSPopularCitiesType)type { 14 | self = [super init]; 15 | if (self) { 16 | _name = name; 17 | _type = type; 18 | } 19 | return self; 20 | } 21 | 22 | @end 23 | -------------------------------------------------------------------------------- /JFCitySelector/Classes/JFCSProvinceModel.h: -------------------------------------------------------------------------------- 1 | // 2 | // JFCSProvinceModel.h 3 | // jfcityselector 4 | // 5 | // Created by zhifenx on 2019/7/11. 6 | // Copyright © 2019 zhifenx. All rights reserved. 7 | // 8 | 9 | #import "JFCSBaseInfoModel.h" 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | @interface JFCSProvince : JFCSBaseInfoModel 14 | 15 | @end 16 | 17 | @interface JFCSProvinceModel : NSObject 18 | 19 | @property (nonatomic, strong) NSArray *provinces; 20 | 21 | @end 22 | 23 | NS_ASSUME_NONNULL_END 24 | -------------------------------------------------------------------------------- /JFCitySelector/Classes/JFCSProvinceModel.m: -------------------------------------------------------------------------------- 1 | // 2 | // JFCSProvinceModel.m 3 | // jfcityselector 4 | // 5 | // Created by zhifenx on 2019/7/11. 6 | // Copyright © 2019 zhifenx. All rights reserved. 7 | // 8 | 9 | #import "JFCSProvinceModel.h" 10 | 11 | @implementation JFCSProvince 12 | 13 | @end 14 | 15 | @implementation JFCSProvinceModel 16 | 17 | + (NSDictionary *)modelContainerPropertyGenericClass { 18 | return @{@"provinces" : [JFCSProvince class] 19 | }; 20 | } 21 | 22 | @end 23 | -------------------------------------------------------------------------------- /JFCitySelector/Classes/JFCSSearchTableViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // JFCSSearchTableViewController.h 3 | // jfcityselector 4 | // 5 | // Created by zhifenx on 2019/7/18. 6 | // Copyright © 2019 zhifenx. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | @class JFCSBaseInfoModel; 14 | @class JFCSConfiguration; 15 | @class JFCSDataOpreation; 16 | 17 | typedef void(^searchControllerSelectCityBlock)(JFCSBaseInfoModel *model); 18 | 19 | @interface JFCSSearchTableViewController : UITableViewController 20 | 21 | @property (nonatomic, copy) searchControllerSelectCityBlock selectCityBlock; 22 | 23 | - (instancetype)initWithConfig:(JFCSConfiguration *)config dataOpreation:(JFCSDataOpreation *)dataOpreation; 24 | 25 | - (void)reloadData:(NSArray *)arr; 26 | 27 | - (void)selectCityBlock:(searchControllerSelectCityBlock)blcok; 28 | 29 | @end 30 | 31 | NS_ASSUME_NONNULL_END 32 | -------------------------------------------------------------------------------- /JFCitySelector/Classes/JFCSSearchTableViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // JFCSSearchTableViewController.m 3 | // jfcityselector 4 | // 5 | // Created by zhifenx on 2019/7/18. 6 | // Copyright © 2019 zhifenx. All rights reserved. 7 | // 8 | 9 | #import "JFCSSearchTableViewController.h" 10 | 11 | #import "JFCSBaseInfoModel.h" 12 | #import "JFCSConfiguration.h" 13 | #import "JFCSDataOpreation.h" 14 | #import "JFCSFileManager.h" 15 | 16 | #define JFWeakSelf(type) __weak typeof(type) weak##type = type; 17 | #define JFStrongSelf(type) __strong typeof(type) type = weak##type; 18 | 19 | @interface JFCSSearchTableViewController () 20 | 21 | @property (nonatomic, strong) NSMutableArray *dataArr; 22 | @property (nonatomic, strong) UIView *searchBarBackgroundView; 23 | @property (nonatomic, strong) UITextField *searchTextField; 24 | @property (nonatomic, strong) UIButton *cancelButton; 25 | @property (nonatomic, strong) JFCSConfiguration *config; 26 | @property (nonatomic, strong) JFCSDataOpreation *dataOpreation; 27 | 28 | @end 29 | 30 | @implementation JFCSSearchTableViewController { 31 | CGFloat _searchTextFieldR; 32 | CGFloat _searchBarBackgroundViewH; 33 | } 34 | 35 | - (instancetype)initWithConfig:(JFCSConfiguration *)config dataOpreation:(JFCSDataOpreation *)dataOpreation { 36 | self = [super init]; 37 | if (self) { 38 | _config = config; 39 | _searchTextFieldR = 10; 40 | _searchBarBackgroundViewH = 30; 41 | _dataOpreation = dataOpreation; 42 | } 43 | return self; 44 | } 45 | 46 | - (void)viewDidLoad { 47 | [super viewDidLoad]; 48 | 49 | [self.tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:@"cell"]; 50 | [self initUI]; 51 | [self.searchTextField becomeFirstResponder]; 52 | } 53 | 54 | - (void)initUI { 55 | self.navigationItem.titleView = self.searchBarBackgroundView; 56 | [self.searchBarBackgroundView addSubview:self.cancelButton]; 57 | [self.searchBarBackgroundView addSubview:self.searchTextField]; 58 | } 59 | 60 | - (void)reloadData:(NSArray *)arr { 61 | self.dataArr = [arr mutableCopy]; 62 | [self.tableView reloadData]; 63 | } 64 | 65 | #pragma mark - Table view data source 66 | 67 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { 68 | return self.dataArr.count; 69 | } 70 | 71 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { 72 | UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cell" forIndexPath:indexPath]; 73 | if (indexPath.row < self.dataArr.count) { 74 | cell.textLabel.text = self.dataArr[indexPath.row].alias ? self.dataArr[indexPath.row].alias : self.dataArr[indexPath.row].name; 75 | } 76 | cell.textLabel.font = _config.tableViewCellTextLabelFont; 77 | return cell; 78 | } 79 | 80 | #pragma mark - Table view delegate 81 | 82 | - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { 83 | [tableView deselectRowAtIndexPath:indexPath animated:YES]; 84 | if (indexPath.row < self.dataArr.count) { 85 | if (![self.dataArr[indexPath.row].name isEqualToString:_config.promptInformation]) { 86 | if (self.selectCityBlock) { 87 | self.selectCityBlock(self.dataArr[indexPath.row]); 88 | } 89 | [self dismissViewController]; 90 | } 91 | } 92 | } 93 | 94 | - (UIView *)searchBarBackgroundView { 95 | if (!_searchBarBackgroundView) { 96 | CGFloat x = 12; 97 | CGFloat w = [UIScreen mainScreen].bounds.size.width - x * 2; 98 | _searchBarBackgroundView = [[UIView alloc] initWithFrame:CGRectMake(x, 0, w, _searchBarBackgroundViewH)]; 99 | } 100 | return _searchBarBackgroundView; 101 | } 102 | 103 | - (UIButton *)cancelButton { 104 | if (!_cancelButton) { 105 | CGFloat w = [self sizeWithString:_config.searchControllerCancelButtonTitle].width; 106 | CGFloat x = self.searchBarBackgroundView.frame.size.width - w; 107 | _cancelButton = [[UIButton alloc] initWithFrame:CGRectMake(x, 0, w, _searchBarBackgroundViewH)]; 108 | [_cancelButton setTitle:_config.searchControllerCancelButtonTitle forState:UIControlStateNormal]; 109 | [_cancelButton.titleLabel setFont:_config.searchControllerCancelButtonTitleFont]; 110 | [_cancelButton setTitleColor:[UIColor grayColor] forState:UIControlStateNormal]; 111 | [_cancelButton addTarget:self action:@selector(cancelButtonAction:) forControlEvents:UIControlEventTouchUpInside]; 112 | } 113 | return _cancelButton; 114 | } 115 | 116 | - (UITextField *)searchTextField { 117 | if (!_searchTextField) { 118 | CGFloat w = self.searchBarBackgroundView.frame.size.width - self.cancelButton.frame.size.width - _searchTextFieldR; 119 | _searchTextField = [[UITextField alloc] initWithFrame:CGRectMake(0, 0, w, _searchBarBackgroundViewH)]; 120 | _searchTextField.placeholder = _config.searchButtonTitle; 121 | _searchTextField.font = _config.searchControllerCancelButtonTitleFont; 122 | UIImageView *leftImageView = [[UIImageView alloc] initWithImage:[JFCSFileManager getImageWithName:_config.searchButtonImageName]]; 123 | CGFloat leftImageViewH = 18; 124 | CGFloat leftImageViewY = (_searchBarBackgroundViewH - leftImageViewH) / 2; 125 | leftImageView.frame = CGRectMake(5, leftImageViewY, leftImageViewH, leftImageViewH); 126 | UIView *leftView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, _searchBarBackgroundViewH, _searchBarBackgroundViewH)]; 127 | [leftView addSubview:leftImageView]; 128 | _searchTextField.leftViewMode = UITextFieldViewModeAlways; 129 | _searchTextField.leftView = leftView; 130 | _searchTextField.layer.cornerRadius = _searchBarBackgroundViewH / 2; 131 | _searchTextField.backgroundColor = [UIColor whiteColor]; 132 | _searchTextField.delegate = self; 133 | _searchTextField.returnKeyType = UIReturnKeyDone; 134 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(textFieldTextDidChangeNotification:) name:UITextFieldTextDidChangeNotification object:nil]; 135 | } 136 | return _searchTextField; 137 | } 138 | 139 | - (CGSize)sizeWithString:(NSString*)str { 140 | NSDictionary*attrs = @{NSFontAttributeName: _config.searchControllerCancelButtonTitleFont}; 141 | return [str boundingRectWithSize:CGSizeMake(60, 30) options:NSStringDrawingUsesLineFragmentOrigin attributes:attrs context:nil].size; 142 | } 143 | 144 | #pragma mark - UITextFieldDelegate 145 | 146 | - (BOOL)textFieldShouldReturn:(UITextField *)textField { 147 | return [textField resignFirstResponder]; 148 | } 149 | 150 | #pragma mark -- Action 151 | 152 | - (void)cancelButtonAction:(UIButton *)sender { 153 | [self dismissViewController]; 154 | } 155 | 156 | - (void)dismissViewController { 157 | [self dismissViewControllerAnimated:NO completion:nil]; 158 | } 159 | 160 | - (void)search:(NSString *)text { 161 | JFWeakSelf(self); 162 | [self.dataOpreation searchWithKeyword:text 163 | resultBlock:^(NSArray * _Nonnull dataArray) { 164 | JFStrongSelf(self); 165 | if (dataArray.count > 0) { 166 | self.dataArr = [NSMutableArray arrayWithArray:[dataArray mutableCopy]]; 167 | }else { 168 | JFCSBaseInfoModel *model = [[JFCSBaseInfoModel alloc] init]; 169 | model.name = self.config.promptInformation; 170 | self.dataArr = [NSMutableArray arrayWithObject:model]; 171 | } 172 | [self.tableView reloadData]; 173 | }]; 174 | } 175 | 176 | #pragma mark -- UITextFieldTextDidChangeNotification 177 | - (void)textFieldTextDidChangeNotification:(NSNotification *)notification { 178 | UITextField *textField = [notification object]; 179 | if (textField.text.length > 0) { 180 | [self search:textField.text]; 181 | } 182 | } 183 | 184 | #pragma mark -- Blcok 185 | 186 | - (void)selectCityBlock:(searchControllerSelectCityBlock)blcok { 187 | if (blcok) { 188 | self.selectCityBlock = blcok; 189 | } 190 | } 191 | 192 | @end 193 | -------------------------------------------------------------------------------- /JFCitySelector/Classes/JFCSTableViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // JFCSTableViewController.h 3 | // jfcityselector 4 | // 5 | // Created by zhifenx on 2019/7/10. 6 | // Copyright © 2019 zhifenx. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | @class JFCSConfiguration; 14 | @class JFCSBaseInfoModel; 15 | @protocol JFCSTableViewControllerDelegate; 16 | 17 | @interface JFCSTableViewController : UIViewController 18 | 19 | - (instancetype)initWithConfiguration:(JFCSConfiguration *)config delegate:(id)delegate; 20 | 21 | @end 22 | 23 | @protocol JFCSTableViewControllerDelegate 24 | 25 | - (void)viewController:(JFCSTableViewController *)viewController didSelectCity:(JFCSBaseInfoModel *)model; 26 | 27 | @end 28 | 29 | NS_ASSUME_NONNULL_END 30 | -------------------------------------------------------------------------------- /JFCitySelector/Classes/JFCSTableViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // JFCSTableViewController.m 3 | // jfcityselector 4 | // 5 | // Created by zhifenx on 2019/7/10. 6 | // Copyright © 2019 zhifenx. All rights reserved. 7 | // 8 | 9 | #import "JFCSTableViewController.h" 10 | 11 | #import "JFCSDataOpreation.h" 12 | #import "JFCSBaseInfoModel.h" 13 | #import "JFCSAreaModel.h" 14 | #import "JFCSPopularCitiesModel.h" 15 | #import "JFCSConfiguration.h" 16 | #import "JFCSSearchTableViewController.h" 17 | #import "JFCSTopToolsTableViewCell.h" 18 | #import "JFCSTableViewHeaderView.h" 19 | #import "JFCSFileManager.h" 20 | 21 | 22 | #define JFWeakSelf(type) __weak typeof(type) weak##type = type; 23 | #define JFStrongSelf(type) __strong typeof(type) type = weak##type; 24 | 25 | #define kHeaderCitiesSectionIndex @"切换区县" 26 | 27 | @interface JFCSTableViewController () 28 | 29 | @property (nonatomic, strong) UITableView *tableView; 30 | @property (nonatomic, strong) NSMutableArray *dataMutableArray; 31 | @property (nonatomic, strong) NSMutableArray *firstLetterMutableArrary; 32 | @property (nonatomic, strong) NSMutableArray *sectionFirstLetterMutableArrary; 33 | @property (nonatomic, strong) NSMutableArray *popularCitiesNameMutableArray; 34 | @property (nonatomic, strong) NSMutableArray *historyRecordNameMutableArray; 35 | @property (nonatomic, strong) NSMutableArray *headerCitiesNameMutableArray; 36 | @property (nonatomic, strong) NSMutableArray *historyRecordMutableArray; 37 | @property (nonatomic, strong) NSMutableArray *headerCitiesMutableArray; 38 | @property (nonatomic, strong) UIButton *searchButton; 39 | @property (nonatomic, strong) JFCSSearchTableViewController *resultVC; 40 | @property (nonatomic, strong) JFCSTopToolsTableViewCell *popularCitiesCell; 41 | @property (nonatomic, strong) JFCSTopToolsTableViewCell *historyRecordCell; 42 | @property (nonatomic, strong) JFCSTopToolsTableViewCell *headerCitiesCell; 43 | @property (nonatomic, strong) JFCSTableViewHeaderView *headerView; 44 | 45 | @property (nonatomic, strong) JFCSDataOpreation *dataOpreation; 46 | @property (nonatomic, strong) JFCSConfiguration *config; 47 | @property (nonatomic, weak) id delegate; 48 | @property (nonatomic, strong) JFCSBaseInfoModel *currentCityModel; 49 | 50 | @end 51 | 52 | @implementation JFCSTableViewController 53 | 54 | - (instancetype)initWithConfiguration:(JFCSConfiguration *)config delegate:(id)delegate { 55 | self = [super init]; 56 | if (self) { 57 | _config = config; 58 | _delegate = delegate; 59 | } 60 | return self; 61 | } 62 | 63 | - (instancetype)init { 64 | self = [super init]; 65 | if (self) { 66 | _config = [[JFCSConfiguration alloc] init]; 67 | } 68 | return self; 69 | } 70 | 71 | - (void)viewDidLoad { 72 | [super viewDidLoad]; 73 | 74 | self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithImage:[[JFCSFileManager getImageWithName:_config.leftBarButtonItemImageName] imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal] style:UIBarButtonItemStylePlain target:self action:@selector(leftBarButtonItemAction)]; 75 | 76 | if (_config.searchButton) { 77 | self.searchButton = _config.searchButton; 78 | } 79 | self.navigationItem.titleView = self.searchButton; 80 | 81 | [self.view addSubview:self.tableView]; 82 | [self initData]; 83 | } 84 | 85 | #pragma mark -- UITableViewDelegate/UITableViewDataSource 86 | 87 | - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { 88 | return self.dataMutableArray.count; 89 | } 90 | 91 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { 92 | NSArray *arr = self.dataMutableArray[section]; 93 | return arr.count; 94 | } 95 | 96 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { 97 | UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cell"]; 98 | if (indexPath.section < self.dataMutableArray.count) { 99 | if (indexPath.row < [self.dataMutableArray[indexPath.section] count]) { 100 | 101 | 102 | if (self.headerCitiesMutableArray.count > 0 && [self.sectionFirstLetterMutableArrary[indexPath.section] isEqualToString:kHeaderCitiesSectionIndex]) {//切换城市 103 | if (!_headerCitiesCell) { 104 | _headerCitiesCell = [[JFCSTopToolsTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"JFCSTopToolsTableViewCell"]; 105 | [_headerCitiesCell setSelectionStyle:UITableViewCellSelectionStyleNone]; 106 | JFWeakSelf(self); 107 | [self.headerCitiesCell topToolsCellSelectCityBlock:^(NSInteger index) { 108 | JFStrongSelf(self); 109 | [self selectCityCallBack:self.headerCitiesMutableArray[index]]; 110 | }]; 111 | } 112 | [_headerCitiesCell setupData:self.headerCitiesNameMutableArray]; 113 | return _headerCitiesCell; 114 | } else if (!_config.hidePopularCities && _config.popularCitiesMutableArray.count > 0 && [self.sectionFirstLetterMutableArrary[indexPath.section] isEqualToString:_config.popularCitiesTitle]) {//热门城市 115 | if (!_popularCitiesCell) { 116 | _popularCitiesCell = [[JFCSTopToolsTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"JFCSTopToolsTableViewCell"]; 117 | [_popularCitiesCell setupData:self.popularCitiesNameMutableArray]; 118 | [_popularCitiesCell setSelectionStyle:UITableViewCellSelectionStyleNone]; 119 | JFWeakSelf(self); 120 | [self.popularCitiesCell topToolsCellSelectCityBlock:^(NSInteger index) { 121 | JFStrongSelf(self); 122 | [self didSelectPopularCities:self.config.popularCitiesMutableArray[index]]; 123 | }]; 124 | } 125 | return _popularCitiesCell; 126 | }else if (!_config.hideHistoricalRecord && self.historyRecordNameMutableArray.count > 0 && [self.sectionFirstLetterMutableArrary[indexPath.section] isEqualToString:_config.historicalRecordTitle]) {//最近访问 127 | if (!_historyRecordCell) { 128 | _historyRecordCell = [[JFCSTopToolsTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"JFCSTopToolsTableViewCell"]; 129 | [_historyRecordCell setSelectionStyle:UITableViewCellSelectionStyleNone]; 130 | JFWeakSelf(self); 131 | [self.historyRecordCell topToolsCellSelectCityBlock:^(NSInteger index) { 132 | JFStrongSelf(self); 133 | [self selectCityCallBack:self.historyRecordMutableArray[index]]; 134 | }]; 135 | } 136 | [_historyRecordCell setupData:self.historyRecordNameMutableArray]; 137 | return _historyRecordCell; 138 | } else { 139 | JFCSBaseInfoModel *model = self.dataMutableArray[indexPath.section][indexPath.row]; 140 | cell.textLabel.text = model.alias ? : model.name; 141 | cell.textLabel.font = _config.tableViewCellTextLabelFont; 142 | } 143 | 144 | 145 | } 146 | } 147 | return cell; 148 | } 149 | 150 | - (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section { 151 | return self.sectionFirstLetterMutableArrary[section]; 152 | } 153 | 154 | - (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView { 155 | return self.firstLetterMutableArrary; 156 | } 157 | 158 | - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { 159 | if (indexPath.section < self.dataMutableArray.count) { 160 | if (indexPath.row < [self.dataMutableArray[indexPath.section] count] && ![self.sectionFirstLetterMutableArrary[indexPath.section] isEqualToString:_config.popularCitiesTitle]) { 161 | JFCSBaseInfoModel *model = self.dataMutableArray[indexPath.section][indexPath.row]; 162 | [self selectCityCallBack:model]; 163 | } 164 | } 165 | } 166 | 167 | - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { 168 | CGFloat height = 44; 169 | if (indexPath.section < self.dataMutableArray.count) { 170 | if (indexPath.row < [self.dataMutableArray[indexPath.section] count]) { 171 | if (!_config.hidePopularCities && _config.popularCitiesMutableArray.count > 0 && [self.sectionFirstLetterMutableArrary[indexPath.section] isEqualToString:_config.popularCitiesTitle]) {//热门城市 172 | height = _config.popularCitiesCellHeight; 173 | } 174 | 175 | if (!_config.hideHistoricalRecord && self.historyRecordNameMutableArray.count > 0 && [self.sectionFirstLetterMutableArrary[indexPath.section] isEqualToString:_config.historicalRecordTitle]) {//最近访问 176 | height = [self.config calculateCellHeightWithCount:self.historyRecordNameMutableArray.count]; 177 | } 178 | 179 | if (self.headerCitiesNameMutableArray.count > indexPath.section && [self.sectionFirstLetterMutableArrary[indexPath.section] isEqualToString:kHeaderCitiesSectionIndex]) { 180 | height = [self.config calculateCellHeightWithCount:self.headerCitiesNameMutableArray.count]; 181 | } 182 | 183 | } 184 | } 185 | return height; 186 | } 187 | 188 | - (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section { 189 | CGFloat height = 30; 190 | if (self.sectionFirstLetterMutableArrary.count > section && [self.sectionFirstLetterMutableArrary[section] isEqualToString:kHeaderCitiesSectionIndex]) { 191 | height = 0; 192 | } 193 | return height; 194 | } 195 | 196 | #pragma mark -- Data 197 | 198 | - (void)initData { 199 | self.popularCitiesNameMutableArray = [NSMutableArray new]; 200 | self.historyRecordMutableArray = [NSMutableArray new]; 201 | self.historyRecordNameMutableArray = [NSMutableArray new]; 202 | self.headerCitiesNameMutableArray = [NSMutableArray new]; 203 | self.dataOpreation = [[JFCSDataOpreation alloc] initWithConfiguration:_config]; 204 | self.currentCityModel = [self.dataOpreation currentCity]; 205 | self.historyRecordMutableArray = [[self.dataOpreation historyRecordCities] mutableCopy]; 206 | [self.historyRecordMutableArray enumerateObjectsUsingBlock:^(JFCSBaseInfoModel * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { 207 | [self.historyRecordNameMutableArray addObject:obj.alias ? : obj.name]; 208 | }]; 209 | [self.config.popularCitiesMutableArray enumerateObjectsUsingBlock:^(JFCSPopularCitiesModel * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { 210 | [self.popularCitiesNameMutableArray addObject:obj.name]; 211 | }]; 212 | JFWeakSelf(self); 213 | [self.dataOpreation arrayOfCityAndArea:^(NSMutableArray * _Nonnull data) { 214 | JFStrongSelf(self); 215 | [self configData:data]; 216 | }]; 217 | [self.headerView updateCurrentCity:self.currentCityModel.alias ? : self.currentCityModel.name]; 218 | } 219 | 220 | - (void)configData:(NSMutableArray *)dataArr { 221 | self.dataMutableArray = [NSMutableArray arrayWithArray:[dataArr mutableCopy]]; 222 | self.firstLetterMutableArrary = [self.dataOpreation.firstLetterArraryOfCityOrArea mutableCopy]; 223 | self.sectionFirstLetterMutableArrary = [self.dataOpreation.firstLetterArraryOfCityOrArea mutableCopy]; 224 | if (!self.config.hidePopularCities && self.config.popularCitiesMutableArray.count > 0) { 225 | [self insertPopularCitiesCellData]; 226 | } 227 | if (!self.config.hideHistoricalRecord && [self.dataOpreation historyRecordCities].count > 0) { 228 | [self insertHistoricalRecordCellData]; 229 | } 230 | [self.tableView reloadData]; 231 | } 232 | 233 | - (void)insertPopularCitiesCellData { 234 | [self.dataMutableArray insertObject:@[@"****** 我是热门城市 Cell ******"] atIndex:0]; 235 | [self.firstLetterMutableArrary insertObject:_config.popularCitiesAbbreviation atIndex:0]; 236 | [self.sectionFirstLetterMutableArrary insertObject:_config.popularCitiesTitle atIndex:0]; 237 | } 238 | 239 | - (void)insertHistoricalRecordCellData { 240 | [self.dataMutableArray insertObject:@[@"****** 我是最近访问 Cell ******"] atIndex:0]; 241 | [self.firstLetterMutableArrary insertObject:_config.historicalRecordAbbreviation atIndex:0]; 242 | [self.sectionFirstLetterMutableArrary insertObject:_config.historicalRecordTitle atIndex:0]; 243 | } 244 | 245 | - (void)insertHeaderCitiesCellData { 246 | [self.dataMutableArray insertObject:@[@"****** 我是切换区县 Cell ******"] atIndex:0]; 247 | [self.firstLetterMutableArrary insertObject:@"区县" atIndex:0]; 248 | [self.sectionFirstLetterMutableArrary insertObject:kHeaderCitiesSectionIndex atIndex:0]; 249 | } 250 | 251 | #pragma mark -- Lazy 252 | 253 | - (UITableView *)tableView { 254 | if (!_tableView) { 255 | _tableView = [[UITableView alloc] initWithFrame:[UIScreen mainScreen].bounds style:UITableViewStylePlain]; 256 | _tableView.sectionIndexColor = _config.sectionIndexColor; 257 | [_tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:@"cell"]; 258 | [_tableView registerClass:[JFCSTopToolsTableViewCell class] forCellReuseIdentifier:@"JFCSTopToolsTableViewCell"]; 259 | _tableView.tableHeaderView = self.headerView; 260 | _tableView.delegate = self; 261 | _tableView.dataSource = self; 262 | } 263 | return _tableView; 264 | } 265 | 266 | - (UIButton *)searchButton { 267 | if (!_searchButton) { 268 | _searchButton = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, 240, 30)]; 269 | [_searchButton setTitle:_config.searchButtonTitle forState:UIControlStateNormal]; 270 | [_searchButton.titleLabel setFont:[UIFont systemFontOfSize:12.0]]; 271 | [_searchButton setTitleColor:_config.searchButtonTitleColor forState:UIControlStateNormal]; 272 | [_searchButton setImage:[JFCSFileManager getImageWithName:_config.searchButtonImageName] forState:UIControlStateNormal]; 273 | [_searchButton setBackgroundColor:_config.searchButtonBackgroundColor]; 274 | [_searchButton.layer setCornerRadius:30 / 2]; 275 | [_searchButton addTarget:self action:@selector(searchButtonAction:) forControlEvents:UIControlEventTouchUpInside]; 276 | } 277 | return _searchButton; 278 | } 279 | 280 | - (JFCSTableViewHeaderView *)headerView { 281 | if (!_headerView) { 282 | _headerView = [[JFCSTableViewHeaderView alloc] initWithFrame:CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width, 40) hideSwitchButton:self.config.hideAreaSwitchButton]; 283 | JFWeakSelf(self); 284 | [_headerView headerViewBlock:^(BOOL selected) { 285 | JFStrongSelf(self); 286 | if (selected) { 287 | [self showOtherCityCell]; 288 | }else { 289 | [self hiddenOtherCityCell]; 290 | } 291 | }]; 292 | } 293 | return _headerView; 294 | } 295 | 296 | #pragma mark - Action 297 | 298 | - (void)selectCityCallBack:(JFCSBaseInfoModel *)model { 299 | self.currentCityModel = model; 300 | [self.dataOpreation cacheCurrentCity:model]; 301 | [self.headerView updateCurrentCity:model.alias ? : model.name]; 302 | if (!_config.hideHistoricalRecord) { 303 | [self.dataOpreation insertHistoryRecordCityModel:model]; 304 | } 305 | if (self.delegate && [self.delegate respondsToSelector:@selector(viewController:didSelectCity:)]) { 306 | [self.delegate viewController:self didSelectCity:model]; 307 | } 308 | [self leftBarButtonItemAction]; 309 | } 310 | 311 | - (void)leftBarButtonItemAction { 312 | [self.navigationController popViewControllerAnimated:YES]; 313 | } 314 | 315 | - (void)searchButtonAction:(UIButton *)sender { 316 | JFCSSearchTableViewController *searchVC = [[JFCSSearchTableViewController alloc] initWithConfig:_config dataOpreation:_dataOpreation]; 317 | [searchVC selectCityBlock:^(JFCSBaseInfoModel * _Nonnull model) { 318 | [self selectCityCallBack:model]; 319 | }]; 320 | UINavigationController *nvi = [[UINavigationController alloc] initWithRootViewController:searchVC]; 321 | [self presentViewController:nvi animated:NO completion:nil]; 322 | } 323 | 324 | - (void)didSelectPopularCities:(JFCSPopularCitiesModel *)popularCitiesModel { 325 | NSArray *arr = [NSArray new]; 326 | 327 | switch (popularCitiesModel.type) { 328 | case JFCSPopularCitiesTypeProvince: { 329 | arr = [self.dataOpreation searchProvinceWithString:popularCitiesModel.name]; 330 | } 331 | break; 332 | case JFCSPopularCitiesTypeCity: { 333 | arr = [self.dataOpreation searchCityWithString:popularCitiesModel.name]; 334 | } 335 | break; 336 | case JFCSPopularCitiesTypeArea: { 337 | arr = [self.dataOpreation searchAreaWithString:popularCitiesModel.name]; 338 | } 339 | break; 340 | default: 341 | break; 342 | } 343 | 344 | if (arr.count > 0) { 345 | [self selectCityCallBack:(JFCSBaseInfoModel *)arr[0]]; 346 | } 347 | } 348 | 349 | #pragma maek -- 切换区县 350 | 351 | - (void)showOtherCityCell { 352 | JFWeakSelf(self); 353 | [self.dataOpreation otherCitiesWithCityModel:self.currentCityModel 354 | resultArray:^(NSArray * _Nonnull dataArray) { 355 | JFStrongSelf(self); 356 | if (dataArray.count > 0) { 357 | [self insertHeaderCitiesCellData]; 358 | self.headerCitiesMutableArray = [dataArray mutableCopy]; 359 | [self.headerCitiesNameMutableArray removeAllObjects]; 360 | [dataArray enumerateObjectsUsingBlock:^(JFCSBaseInfoModel * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { 361 | [self.headerCitiesNameMutableArray addObject:obj.name]; 362 | }]; 363 | 364 | [self.tableView beginUpdates]; 365 | [self.tableView insertSections:[[NSIndexSet alloc] initWithIndex:0] withRowAnimation:UITableViewRowAnimationNone]; 366 | [self.tableView endUpdates]; 367 | } 368 | }]; 369 | } 370 | 371 | - (void)hiddenOtherCityCell { 372 | if (self.headerCitiesMutableArray.count > 0) { 373 | [self.headerCitiesNameMutableArray removeAllObjects]; 374 | [self.headerCitiesMutableArray removeAllObjects]; 375 | [self.dataMutableArray removeObjectAtIndex:0]; 376 | [self.sectionFirstLetterMutableArrary removeObjectAtIndex:0]; 377 | [self.firstLetterMutableArrary removeObjectAtIndex:0]; 378 | [self.tableView beginUpdates]; 379 | [self.tableView deleteSections:[[NSIndexSet alloc] initWithIndex:0] withRowAnimation:UITableViewRowAnimationNone]; 380 | [self.tableView endUpdates]; 381 | } 382 | } 383 | 384 | @end 385 | -------------------------------------------------------------------------------- /JFCitySelector/Classes/JFCSTableViewHeaderView.h: -------------------------------------------------------------------------------- 1 | // 2 | // JFCSTableViewHeaderView.h 3 | // jfcityselector 4 | // 5 | // Created by zhifenx on 2019/7/24. 6 | // Copyright © 2019 zhifenx. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | typedef void(^headerViewBlock)(BOOL selected); 14 | 15 | @interface JFCSTableViewHeaderView : UIView 16 | 17 | @property (nonatomic, strong) UILabel *currentCityLabel; 18 | @property (nonatomic, strong) UIButton *rightButton; 19 | 20 | @property (nonatomic, copy) headerViewBlock headerBlock; 21 | 22 | - (instancetype)initWithFrame:(CGRect)frame hideSwitchButton:(BOOL)hidden; 23 | 24 | - (void)updateCurrentCity:(NSString *)name; 25 | 26 | - (void)headerViewBlock:(headerViewBlock)blcok; 27 | @end 28 | 29 | NS_ASSUME_NONNULL_END 30 | -------------------------------------------------------------------------------- /JFCitySelector/Classes/JFCSTableViewHeaderView.m: -------------------------------------------------------------------------------- 1 | // 2 | // JFCSTableViewHeaderView.m 3 | // jfcityselector 4 | // 5 | // Created by zhifenx on 2019/7/24. 6 | // Copyright © 2019 zhifenx. All rights reserved. 7 | // 8 | 9 | #import "JFCSTableViewHeaderView.h" 10 | 11 | #import "JFCSFileManager.h" 12 | 13 | @implementation JFCSTableViewHeaderView 14 | 15 | - (instancetype)initWithFrame:(CGRect)frame hideSwitchButton:(BOOL)hidden { 16 | self = [super initWithFrame:frame]; 17 | if (self) { 18 | UIFont *font = [UIFont systemFontOfSize:13.0]; 19 | CGFloat buttonW = 100; 20 | CGFloat buttonR = 16; 21 | CGFloat buttonX = frame.size.width - buttonW - buttonR; 22 | self.rightButton = [[UIButton alloc] initWithFrame:CGRectMake(buttonX, 0, buttonW, frame.size.height)]; 23 | [self.rightButton setImage:[JFCSFileManager getImageWithName:@"jf_icon_down"] forState:UIControlStateNormal]; 24 | [self.rightButton setTitle:@"切换区县" forState:UIControlStateNormal]; 25 | [self.rightButton setImageEdgeInsets:UIEdgeInsetsMake(0, 0, 0, -buttonW - 20)]; 26 | [self.rightButton setTitleEdgeInsets:UIEdgeInsetsMake(0, 0, 0, 10)]; 27 | [self.rightButton.titleLabel setFont:font]; 28 | [self.rightButton setTitleColor:[UIColor blackColor] forState:UIControlStateNormal]; 29 | [self.rightButton addTarget:self action:@selector(rightButtonAction:) forControlEvents:UIControlEventTouchUpInside]; 30 | [self addSubview:self.rightButton]; 31 | [self.rightButton setHidden:hidden]; 32 | CGFloat labelR = 5; 33 | CGFloat labelW = self.rightButton.frame.origin.x - labelR - buttonR; 34 | self.currentCityLabel = [[UILabel alloc] initWithFrame:CGRectMake(buttonR, 0, labelW, frame.size.height)]; 35 | self.currentCityLabel.font = font; 36 | self.currentCityLabel.lineBreakMode = NSLineBreakByTruncatingMiddle; 37 | [self addSubview:self.currentCityLabel]; 38 | [self updateCurrentCity:@""]; 39 | } 40 | return self; 41 | } 42 | 43 | - (void)updateCurrentCity:(NSString *)name { 44 | if (name) { 45 | self.currentCityLabel.text = [NSString stringWithFormat:@"当前:%@",name]; 46 | } 47 | } 48 | 49 | - (void)rightButtonAction:(UIButton *)sender { 50 | sender.selected = !sender.selected; 51 | if (sender.selected) { 52 | [sender setImage:[JFCSFileManager getImageWithName:@"jf_icon_up"] forState:UIControlStateNormal]; 53 | }else { 54 | [sender setImage:[JFCSFileManager getImageWithName:@"jf_icon_down"] forState:UIControlStateNormal]; 55 | } 56 | if (self.headerBlock) { 57 | self.headerBlock(sender.selected); 58 | } 59 | } 60 | 61 | - (void)headerViewBlock:(headerViewBlock)blcok { 62 | if (blcok) { 63 | self.headerBlock = blcok; 64 | } 65 | } 66 | 67 | @end 68 | -------------------------------------------------------------------------------- /JFCitySelector/Classes/JFCSTopToolsTableViewCell.h: -------------------------------------------------------------------------------- 1 | // 2 | // JFCSTopToolsTableViewCell.h 3 | // jfcityselector 4 | // 5 | // Created by zhifenx on 2019/7/23. 6 | // Copyright © 2019 zhifenx. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @class JFCSPopularCitiesModel; 12 | 13 | NS_ASSUME_NONNULL_BEGIN 14 | 15 | typedef void(^topToolsCellSelectCityBlock)(NSInteger index); 16 | 17 | @interface JFCSTopToolsTableViewCell : UITableViewCell 18 | 19 | @property (nonatomic, strong) NSMutableArray *buttonArr; 20 | 21 | @property (nonatomic, copy) topToolsCellSelectCityBlock selectCityBlock; 22 | 23 | - (void)setupData:(NSArray *)dataArr; 24 | 25 | - (void)topToolsCellSelectCityBlock:(topToolsCellSelectCityBlock)block; 26 | 27 | @end 28 | 29 | NS_ASSUME_NONNULL_END 30 | -------------------------------------------------------------------------------- /JFCitySelector/Classes/JFCSTopToolsTableViewCell.m: -------------------------------------------------------------------------------- 1 | // 2 | // JFCSTopToolsTableViewCell.m 3 | // jfcityselector 4 | // 5 | // Created by zhifenx on 2019/7/23. 6 | // Copyright © 2019 zhifenx. All rights reserved. 7 | // 8 | 9 | #import "JFCSTopToolsTableViewCell.h" 10 | 11 | #import "JFCSPopularCitiesModel.h" 12 | 13 | @implementation JFCSTopToolsTableViewCell 14 | 15 | - (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier { 16 | if (self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]) { 17 | self.backgroundColor = [UIColor colorWithRed:247 / 255.0 green:247 / 255.0 blue:247 / 255.0 alpha:1.0]; 18 | } 19 | return self; 20 | } 21 | 22 | - (void)setupData:(NSArray *)dataArr { 23 | self.buttonArr = [NSMutableArray new]; 24 | [dataArr enumerateObjectsUsingBlock:^(NSString * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { 25 | [self createButton:idx title:obj]; 26 | }]; 27 | } 28 | 29 | - (void)createButton:(NSInteger)index title:(NSString *)title { 30 | NSInteger indexX = index % 3; 31 | NSInteger indexY = index / 3; 32 | UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom]; 33 | CGFloat buttonIntervalH = 16; 34 | CGFloat buttonIntervalV = 10; 35 | CGFloat buttonW = ([UIScreen mainScreen].bounds.size.width - buttonIntervalH * 4) / 3 - 5; 36 | CGFloat buttonH = 36; 37 | CGFloat buttonX = buttonIntervalH * (indexX + 1) + indexX * buttonW; 38 | CGFloat buttonY = buttonIntervalV * (indexY + 1) + indexY * buttonH; 39 | button.frame = CGRectMake(buttonX, buttonY, buttonW, buttonH); 40 | [button setTag:index]; 41 | [button.layer setCornerRadius:3]; 42 | [button setTitle:title forState:UIControlStateNormal]; 43 | [button setBackgroundColor:[UIColor whiteColor]]; 44 | [button setTitleColor:[UIColor blackColor] forState:UIControlStateNormal]; 45 | [button .titleLabel setFont:[UIFont systemFontOfSize:13.0]]; 46 | [button addTarget:self action:@selector(buttonAction:) forControlEvents:UIControlEventTouchUpInside]; 47 | [self.contentView addSubview:button]; 48 | [self.buttonArr addObject:button]; 49 | } 50 | 51 | - (void)buttonAction:(UIButton *)sender { 52 | if (self.selectCityBlock) { 53 | self.selectCityBlock(sender.tag); 54 | } 55 | } 56 | 57 | - (void)topToolsCellSelectCityBlock:(topToolsCellSelectCityBlock)block { 58 | if (block) { 59 | self.selectCityBlock = block; 60 | } 61 | } 62 | 63 | @end 64 | -------------------------------------------------------------------------------- /JFCitySelector/Classes/JFCitySelector.h: -------------------------------------------------------------------------------- 1 | // 2 | // JFCitySelector.h 3 | // JFCitySelector Demo 4 | // 5 | // Created by zhifenx on 2019/8/1. 6 | // Copyright © 2019 zhifenx. All rights reserved. 7 | // 8 | 9 | #ifndef JFCitySelector_h 10 | #define JFCitySelector_h 11 | 12 | #import 13 | 14 | 15 | #import "JFCSTableViewController.h" 16 | #import "JFCSConfiguration.h" 17 | #import "JFCSBaseInfoModel.h" 18 | #import "JFCSDataOpreation.h" 19 | #import "JFCSPopularCitiesModel.h" 20 | #import 21 | 22 | 23 | #endif /* JFCitySelector_h */ 24 | -------------------------------------------------------------------------------- /JFCitySelector/Example/JFCitySelector Demo/JFCitySelector Demo.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 51; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 1E3CA6BC22F1834E008893BF /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 1E3CA6BB22F1834E008893BF /* AppDelegate.m */; }; 11 | 1E3CA6BF22F1834E008893BF /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 1E3CA6BE22F1834E008893BF /* ViewController.m */; }; 12 | 1E3CA6C222F1834E008893BF /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 1E3CA6C022F1834E008893BF /* Main.storyboard */; }; 13 | 1E3CA6C422F1834E008893BF /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 1E3CA6C322F1834E008893BF /* Assets.xcassets */; }; 14 | 1E3CA6C722F1834E008893BF /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 1E3CA6C522F1834E008893BF /* LaunchScreen.storyboard */; }; 15 | 1E3CA6CA22F1834E008893BF /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 1E3CA6C922F1834E008893BF /* main.m */; }; 16 | 1E3CA6F422F18372008893BF /* JFCitySelector.bundle in Resources */ = {isa = PBXBuildFile; fileRef = 1E3CA6D122F18372008893BF /* JFCitySelector.bundle */; }; 17 | 1E3CA6F522F18372008893BF /* JFCSBaseInfoModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 1E3CA6D422F18372008893BF /* JFCSBaseInfoModel.m */; }; 18 | 1E3CA6F622F18372008893BF /* JFCSCityModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 1E3CA6D522F18372008893BF /* JFCSCityModel.m */; }; 19 | 1E3CA6F722F18372008893BF /* JFCSFileManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 1E3CA6D922F18372008893BF /* JFCSFileManager.m */; }; 20 | 1E3CA6F822F18372008893BF /* .gitkeep in Resources */ = {isa = PBXBuildFile; fileRef = 1E3CA6DB22F18372008893BF /* .gitkeep */; }; 21 | 1E3CA6F922F18372008893BF /* JFCSTopToolsTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 1E3CA6DC22F18372008893BF /* JFCSTopToolsTableViewCell.m */; }; 22 | 1E3CA6FA22F18372008893BF /* JFCSTableViewHeaderView.m in Sources */ = {isa = PBXBuildFile; fileRef = 1E3CA6DD22F18372008893BF /* JFCSTableViewHeaderView.m */; }; 23 | 1E3CA6FB22F18372008893BF /* JFCSTableViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 1E3CA6DE22F18372008893BF /* JFCSTableViewController.m */; }; 24 | 1E3CA6FC22F18372008893BF /* JFCSPopularCitiesModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 1E3CA6DF22F18372008893BF /* JFCSPopularCitiesModel.m */; }; 25 | 1E3CA6FF22F18372008893BF /* JFCSConfiguration.m in Sources */ = {isa = PBXBuildFile; fileRef = 1E3CA6E922F18372008893BF /* JFCSConfiguration.m */; }; 26 | 1E3CA70022F18372008893BF /* JFCSProvinceModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 1E3CA6EA22F18372008893BF /* JFCSProvinceModel.m */; }; 27 | 1E3CA70122F18372008893BF /* JFCSAreaModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 1E3CA6EB22F18372008893BF /* JFCSAreaModel.m */; }; 28 | 1E3CA70222F18372008893BF /* JFCSSearchTableViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 1E3CA6EE22F18372008893BF /* JFCSSearchTableViewController.m */; }; 29 | 1E3CA70322F18372008893BF /* JFCSDataOpreation.m in Sources */ = {isa = PBXBuildFile; fileRef = 1E3CA6EF22F18372008893BF /* JFCSDataOpreation.m */; }; 30 | 1E3CA70422F18372008893BF /* JFCSData.m in Sources */ = {isa = PBXBuildFile; fileRef = 1E3CA6F022F18372008893BF /* JFCSData.m */; }; 31 | DEB1BB3DAF2C32B471936922 /* Pods_JFCitySelector_Demo.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4904F847039BB610842E1BD2 /* Pods_JFCitySelector_Demo.framework */; }; 32 | /* End PBXBuildFile section */ 33 | 34 | /* Begin PBXFileReference section */ 35 | 1E0E003022F2C27200E0E30F /* JFCitySelector.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = JFCitySelector.h; sourceTree = ""; }; 36 | 1E3CA6B722F1834E008893BF /* JFCitySelector Demo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "JFCitySelector Demo.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 37 | 1E3CA6BA22F1834E008893BF /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 38 | 1E3CA6BB22F1834E008893BF /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 39 | 1E3CA6BD22F1834E008893BF /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; 40 | 1E3CA6BE22F1834E008893BF /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; 41 | 1E3CA6C122F1834E008893BF /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 42 | 1E3CA6C322F1834E008893BF /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 43 | 1E3CA6C622F1834E008893BF /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 44 | 1E3CA6C822F1834E008893BF /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 45 | 1E3CA6C922F1834E008893BF /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 46 | 1E3CA6D122F18372008893BF /* JFCitySelector.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; path = JFCitySelector.bundle; sourceTree = ""; }; 47 | 1E3CA6D322F18372008893BF /* JFCSConfiguration.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JFCSConfiguration.h; sourceTree = ""; }; 48 | 1E3CA6D422F18372008893BF /* JFCSBaseInfoModel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JFCSBaseInfoModel.m; sourceTree = ""; }; 49 | 1E3CA6D522F18372008893BF /* JFCSCityModel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JFCSCityModel.m; sourceTree = ""; }; 50 | 1E3CA6D622F18372008893BF /* JFCSAreaModel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JFCSAreaModel.h; sourceTree = ""; }; 51 | 1E3CA6D722F18372008893BF /* JFCSProvinceModel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JFCSProvinceModel.h; sourceTree = ""; }; 52 | 1E3CA6D822F18372008893BF /* JFCSDataOpreation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JFCSDataOpreation.h; sourceTree = ""; }; 53 | 1E3CA6D922F18372008893BF /* JFCSFileManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JFCSFileManager.m; sourceTree = ""; }; 54 | 1E3CA6DA22F18372008893BF /* JFCSSearchTableViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JFCSSearchTableViewController.h; sourceTree = ""; }; 55 | 1E3CA6DB22F18372008893BF /* .gitkeep */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = .gitkeep; sourceTree = ""; }; 56 | 1E3CA6DC22F18372008893BF /* JFCSTopToolsTableViewCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JFCSTopToolsTableViewCell.m; sourceTree = ""; }; 57 | 1E3CA6DD22F18372008893BF /* JFCSTableViewHeaderView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JFCSTableViewHeaderView.m; sourceTree = ""; }; 58 | 1E3CA6DE22F18372008893BF /* JFCSTableViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JFCSTableViewController.m; sourceTree = ""; }; 59 | 1E3CA6DF22F18372008893BF /* JFCSPopularCitiesModel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JFCSPopularCitiesModel.m; sourceTree = ""; }; 60 | 1E3CA6E022F18372008893BF /* JFCSData.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JFCSData.h; sourceTree = ""; }; 61 | 1E3CA6E722F18372008893BF /* JFCSCityModel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JFCSCityModel.h; sourceTree = ""; }; 62 | 1E3CA6E822F18372008893BF /* JFCSBaseInfoModel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JFCSBaseInfoModel.h; sourceTree = ""; }; 63 | 1E3CA6E922F18372008893BF /* JFCSConfiguration.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JFCSConfiguration.m; sourceTree = ""; }; 64 | 1E3CA6EA22F18372008893BF /* JFCSProvinceModel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JFCSProvinceModel.m; sourceTree = ""; }; 65 | 1E3CA6EB22F18372008893BF /* JFCSAreaModel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JFCSAreaModel.m; sourceTree = ""; }; 66 | 1E3CA6EC22F18372008893BF /* JFCSTopToolsTableViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JFCSTopToolsTableViewCell.h; sourceTree = ""; }; 67 | 1E3CA6ED22F18372008893BF /* JFCSFileManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JFCSFileManager.h; sourceTree = ""; }; 68 | 1E3CA6EE22F18372008893BF /* JFCSSearchTableViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JFCSSearchTableViewController.m; sourceTree = ""; }; 69 | 1E3CA6EF22F18372008893BF /* JFCSDataOpreation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JFCSDataOpreation.m; sourceTree = ""; }; 70 | 1E3CA6F022F18372008893BF /* JFCSData.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JFCSData.m; sourceTree = ""; }; 71 | 1E3CA6F122F18372008893BF /* JFCSPopularCitiesModel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JFCSPopularCitiesModel.h; sourceTree = ""; }; 72 | 1E3CA6F222F18372008893BF /* JFCSTableViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JFCSTableViewController.h; sourceTree = ""; }; 73 | 1E3CA6F322F18372008893BF /* JFCSTableViewHeaderView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JFCSTableViewHeaderView.h; sourceTree = ""; }; 74 | 4904F847039BB610842E1BD2 /* Pods_JFCitySelector_Demo.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_JFCitySelector_Demo.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 75 | BFE3D4EF771DDA414C932F83 /* Pods-JFCitySelector Demo.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-JFCitySelector Demo.debug.xcconfig"; path = "Target Support Files/Pods-JFCitySelector Demo/Pods-JFCitySelector Demo.debug.xcconfig"; sourceTree = ""; }; 76 | EA4FC451CC07FC74FB2CBBE0 /* Pods-JFCitySelector Demo.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-JFCitySelector Demo.release.xcconfig"; path = "Target Support Files/Pods-JFCitySelector Demo/Pods-JFCitySelector Demo.release.xcconfig"; sourceTree = ""; }; 77 | /* End PBXFileReference section */ 78 | 79 | /* Begin PBXFrameworksBuildPhase section */ 80 | 1E3CA6B422F1834E008893BF /* Frameworks */ = { 81 | isa = PBXFrameworksBuildPhase; 82 | buildActionMask = 2147483647; 83 | files = ( 84 | DEB1BB3DAF2C32B471936922 /* Pods_JFCitySelector_Demo.framework in Frameworks */, 85 | ); 86 | runOnlyForDeploymentPostprocessing = 0; 87 | }; 88 | /* End PBXFrameworksBuildPhase section */ 89 | 90 | /* Begin PBXGroup section */ 91 | 1E3CA6AE22F1834E008893BF = { 92 | isa = PBXGroup; 93 | children = ( 94 | 1E3CA6B922F1834E008893BF /* JFCitySelector Demo */, 95 | 1E3CA6B822F1834E008893BF /* Products */, 96 | BAF4922E613148AB473629F8 /* Pods */, 97 | E33E2B460FBC5AE7BDC83D1C /* Frameworks */, 98 | ); 99 | sourceTree = ""; 100 | }; 101 | 1E3CA6B822F1834E008893BF /* Products */ = { 102 | isa = PBXGroup; 103 | children = ( 104 | 1E3CA6B722F1834E008893BF /* JFCitySelector Demo.app */, 105 | ); 106 | name = Products; 107 | sourceTree = ""; 108 | }; 109 | 1E3CA6B922F1834E008893BF /* JFCitySelector Demo */ = { 110 | isa = PBXGroup; 111 | children = ( 112 | 1E3CA6D222F18372008893BF /* Classes */, 113 | 1E3CA6D022F18372008893BF /* Resources */, 114 | 1E3CA6BA22F1834E008893BF /* AppDelegate.h */, 115 | 1E3CA6BB22F1834E008893BF /* AppDelegate.m */, 116 | 1E3CA6BD22F1834E008893BF /* ViewController.h */, 117 | 1E3CA6BE22F1834E008893BF /* ViewController.m */, 118 | 1E3CA6C022F1834E008893BF /* Main.storyboard */, 119 | 1E3CA6C322F1834E008893BF /* Assets.xcassets */, 120 | 1E3CA6C522F1834E008893BF /* LaunchScreen.storyboard */, 121 | 1E3CA6C822F1834E008893BF /* Info.plist */, 122 | 1E3CA6C922F1834E008893BF /* main.m */, 123 | ); 124 | path = "JFCitySelector Demo"; 125 | sourceTree = ""; 126 | }; 127 | 1E3CA6D022F18372008893BF /* Resources */ = { 128 | isa = PBXGroup; 129 | children = ( 130 | 1E3CA6D122F18372008893BF /* JFCitySelector.bundle */, 131 | ); 132 | name = Resources; 133 | path = ../../../Resources; 134 | sourceTree = ""; 135 | }; 136 | 1E3CA6D222F18372008893BF /* Classes */ = { 137 | isa = PBXGroup; 138 | children = ( 139 | 1E3CA6DB22F18372008893BF /* .gitkeep */, 140 | 1E3CA6D622F18372008893BF /* JFCSAreaModel.h */, 141 | 1E3CA6EB22F18372008893BF /* JFCSAreaModel.m */, 142 | 1E3CA6E822F18372008893BF /* JFCSBaseInfoModel.h */, 143 | 1E3CA6D422F18372008893BF /* JFCSBaseInfoModel.m */, 144 | 1E3CA6E722F18372008893BF /* JFCSCityModel.h */, 145 | 1E3CA6D522F18372008893BF /* JFCSCityModel.m */, 146 | 1E3CA6D322F18372008893BF /* JFCSConfiguration.h */, 147 | 1E3CA6E922F18372008893BF /* JFCSConfiguration.m */, 148 | 1E3CA6E022F18372008893BF /* JFCSData.h */, 149 | 1E3CA6F022F18372008893BF /* JFCSData.m */, 150 | 1E3CA6D822F18372008893BF /* JFCSDataOpreation.h */, 151 | 1E3CA6EF22F18372008893BF /* JFCSDataOpreation.m */, 152 | 1E3CA6ED22F18372008893BF /* JFCSFileManager.h */, 153 | 1E3CA6D922F18372008893BF /* JFCSFileManager.m */, 154 | 1E3CA6F122F18372008893BF /* JFCSPopularCitiesModel.h */, 155 | 1E3CA6DF22F18372008893BF /* JFCSPopularCitiesModel.m */, 156 | 1E3CA6D722F18372008893BF /* JFCSProvinceModel.h */, 157 | 1E3CA6EA22F18372008893BF /* JFCSProvinceModel.m */, 158 | 1E3CA6DA22F18372008893BF /* JFCSSearchTableViewController.h */, 159 | 1E3CA6EE22F18372008893BF /* JFCSSearchTableViewController.m */, 160 | 1E3CA6F222F18372008893BF /* JFCSTableViewController.h */, 161 | 1E3CA6DE22F18372008893BF /* JFCSTableViewController.m */, 162 | 1E3CA6F322F18372008893BF /* JFCSTableViewHeaderView.h */, 163 | 1E3CA6DD22F18372008893BF /* JFCSTableViewHeaderView.m */, 164 | 1E3CA6EC22F18372008893BF /* JFCSTopToolsTableViewCell.h */, 165 | 1E3CA6DC22F18372008893BF /* JFCSTopToolsTableViewCell.m */, 166 | 1E0E003022F2C27200E0E30F /* JFCitySelector.h */, 167 | ); 168 | name = Classes; 169 | path = ../../../Classes; 170 | sourceTree = ""; 171 | }; 172 | BAF4922E613148AB473629F8 /* Pods */ = { 173 | isa = PBXGroup; 174 | children = ( 175 | BFE3D4EF771DDA414C932F83 /* Pods-JFCitySelector Demo.debug.xcconfig */, 176 | EA4FC451CC07FC74FB2CBBE0 /* Pods-JFCitySelector Demo.release.xcconfig */, 177 | ); 178 | path = Pods; 179 | sourceTree = ""; 180 | }; 181 | E33E2B460FBC5AE7BDC83D1C /* Frameworks */ = { 182 | isa = PBXGroup; 183 | children = ( 184 | 4904F847039BB610842E1BD2 /* Pods_JFCitySelector_Demo.framework */, 185 | ); 186 | name = Frameworks; 187 | sourceTree = ""; 188 | }; 189 | /* End PBXGroup section */ 190 | 191 | /* Begin PBXNativeTarget section */ 192 | 1E3CA6B622F1834E008893BF /* JFCitySelector Demo */ = { 193 | isa = PBXNativeTarget; 194 | buildConfigurationList = 1E3CA6CD22F1834E008893BF /* Build configuration list for PBXNativeTarget "JFCitySelector Demo" */; 195 | buildPhases = ( 196 | E51500857FCE0E7117F1FA24 /* [CP] Check Pods Manifest.lock */, 197 | 1E3CA6B322F1834E008893BF /* Sources */, 198 | 1E3CA6B422F1834E008893BF /* Frameworks */, 199 | 1E3CA6B522F1834E008893BF /* Resources */, 200 | 97DC44D0F6F63FD84BE9AF53 /* [CP] Embed Pods Frameworks */, 201 | ); 202 | buildRules = ( 203 | ); 204 | dependencies = ( 205 | ); 206 | name = "JFCitySelector Demo"; 207 | productName = "JFCitySelector Demo"; 208 | productReference = 1E3CA6B722F1834E008893BF /* JFCitySelector Demo.app */; 209 | productType = "com.apple.product-type.application"; 210 | }; 211 | /* End PBXNativeTarget section */ 212 | 213 | /* Begin PBXProject section */ 214 | 1E3CA6AF22F1834E008893BF /* Project object */ = { 215 | isa = PBXProject; 216 | attributes = { 217 | LastUpgradeCheck = 1020; 218 | ORGANIZATIONNAME = zhifenx; 219 | TargetAttributes = { 220 | 1E3CA6B622F1834E008893BF = { 221 | CreatedOnToolsVersion = 10.2.1; 222 | }; 223 | }; 224 | }; 225 | buildConfigurationList = 1E3CA6B222F1834E008893BF /* Build configuration list for PBXProject "JFCitySelector Demo" */; 226 | compatibilityVersion = "Xcode 9.3"; 227 | developmentRegion = en; 228 | hasScannedForEncodings = 0; 229 | knownRegions = ( 230 | en, 231 | Base, 232 | ); 233 | mainGroup = 1E3CA6AE22F1834E008893BF; 234 | productRefGroup = 1E3CA6B822F1834E008893BF /* Products */; 235 | projectDirPath = ""; 236 | projectRoot = ""; 237 | targets = ( 238 | 1E3CA6B622F1834E008893BF /* JFCitySelector Demo */, 239 | ); 240 | }; 241 | /* End PBXProject section */ 242 | 243 | /* Begin PBXResourcesBuildPhase section */ 244 | 1E3CA6B522F1834E008893BF /* Resources */ = { 245 | isa = PBXResourcesBuildPhase; 246 | buildActionMask = 2147483647; 247 | files = ( 248 | 1E3CA6C722F1834E008893BF /* LaunchScreen.storyboard in Resources */, 249 | 1E3CA6C422F1834E008893BF /* Assets.xcassets in Resources */, 250 | 1E3CA6F822F18372008893BF /* .gitkeep in Resources */, 251 | 1E3CA6C222F1834E008893BF /* Main.storyboard in Resources */, 252 | 1E3CA6F422F18372008893BF /* JFCitySelector.bundle in Resources */, 253 | ); 254 | runOnlyForDeploymentPostprocessing = 0; 255 | }; 256 | /* End PBXResourcesBuildPhase section */ 257 | 258 | /* Begin PBXShellScriptBuildPhase section */ 259 | 97DC44D0F6F63FD84BE9AF53 /* [CP] Embed Pods Frameworks */ = { 260 | isa = PBXShellScriptBuildPhase; 261 | buildActionMask = 2147483647; 262 | files = ( 263 | ); 264 | inputFileListPaths = ( 265 | "${PODS_ROOT}/Target Support Files/Pods-JFCitySelector Demo/Pods-JFCitySelector Demo-frameworks-${CONFIGURATION}-input-files.xcfilelist", 266 | ); 267 | name = "[CP] Embed Pods Frameworks"; 268 | outputFileListPaths = ( 269 | "${PODS_ROOT}/Target Support Files/Pods-JFCitySelector Demo/Pods-JFCitySelector Demo-frameworks-${CONFIGURATION}-output-files.xcfilelist", 270 | ); 271 | runOnlyForDeploymentPostprocessing = 0; 272 | shellPath = /bin/sh; 273 | shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-JFCitySelector Demo/Pods-JFCitySelector Demo-frameworks.sh\"\n"; 274 | showEnvVarsInLog = 0; 275 | }; 276 | E51500857FCE0E7117F1FA24 /* [CP] Check Pods Manifest.lock */ = { 277 | isa = PBXShellScriptBuildPhase; 278 | buildActionMask = 2147483647; 279 | files = ( 280 | ); 281 | inputFileListPaths = ( 282 | ); 283 | inputPaths = ( 284 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock", 285 | "${PODS_ROOT}/Manifest.lock", 286 | ); 287 | name = "[CP] Check Pods Manifest.lock"; 288 | outputFileListPaths = ( 289 | ); 290 | outputPaths = ( 291 | "$(DERIVED_FILE_DIR)/Pods-JFCitySelector Demo-checkManifestLockResult.txt", 292 | ); 293 | runOnlyForDeploymentPostprocessing = 0; 294 | shellPath = /bin/sh; 295 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; 296 | showEnvVarsInLog = 0; 297 | }; 298 | /* End PBXShellScriptBuildPhase section */ 299 | 300 | /* Begin PBXSourcesBuildPhase section */ 301 | 1E3CA6B322F1834E008893BF /* Sources */ = { 302 | isa = PBXSourcesBuildPhase; 303 | buildActionMask = 2147483647; 304 | files = ( 305 | 1E3CA6F522F18372008893BF /* JFCSBaseInfoModel.m in Sources */, 306 | 1E3CA6FA22F18372008893BF /* JFCSTableViewHeaderView.m in Sources */, 307 | 1E3CA6BF22F1834E008893BF /* ViewController.m in Sources */, 308 | 1E3CA70022F18372008893BF /* JFCSProvinceModel.m in Sources */, 309 | 1E3CA6FB22F18372008893BF /* JFCSTableViewController.m in Sources */, 310 | 1E3CA6CA22F1834E008893BF /* main.m in Sources */, 311 | 1E3CA6FF22F18372008893BF /* JFCSConfiguration.m in Sources */, 312 | 1E3CA6F622F18372008893BF /* JFCSCityModel.m in Sources */, 313 | 1E3CA6FC22F18372008893BF /* JFCSPopularCitiesModel.m in Sources */, 314 | 1E3CA6BC22F1834E008893BF /* AppDelegate.m in Sources */, 315 | 1E3CA70122F18372008893BF /* JFCSAreaModel.m in Sources */, 316 | 1E3CA70322F18372008893BF /* JFCSDataOpreation.m in Sources */, 317 | 1E3CA70222F18372008893BF /* JFCSSearchTableViewController.m in Sources */, 318 | 1E3CA70422F18372008893BF /* JFCSData.m in Sources */, 319 | 1E3CA6F922F18372008893BF /* JFCSTopToolsTableViewCell.m in Sources */, 320 | 1E3CA6F722F18372008893BF /* JFCSFileManager.m in Sources */, 321 | ); 322 | runOnlyForDeploymentPostprocessing = 0; 323 | }; 324 | /* End PBXSourcesBuildPhase section */ 325 | 326 | /* Begin PBXVariantGroup section */ 327 | 1E3CA6C022F1834E008893BF /* Main.storyboard */ = { 328 | isa = PBXVariantGroup; 329 | children = ( 330 | 1E3CA6C122F1834E008893BF /* Base */, 331 | ); 332 | name = Main.storyboard; 333 | sourceTree = ""; 334 | }; 335 | 1E3CA6C522F1834E008893BF /* LaunchScreen.storyboard */ = { 336 | isa = PBXVariantGroup; 337 | children = ( 338 | 1E3CA6C622F1834E008893BF /* Base */, 339 | ); 340 | name = LaunchScreen.storyboard; 341 | sourceTree = ""; 342 | }; 343 | /* End PBXVariantGroup section */ 344 | 345 | /* Begin XCBuildConfiguration section */ 346 | 1E3CA6CB22F1834E008893BF /* Debug */ = { 347 | isa = XCBuildConfiguration; 348 | buildSettings = { 349 | ALWAYS_SEARCH_USER_PATHS = NO; 350 | CLANG_ANALYZER_NONNULL = YES; 351 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 352 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 353 | CLANG_CXX_LIBRARY = "libc++"; 354 | CLANG_ENABLE_MODULES = YES; 355 | CLANG_ENABLE_OBJC_ARC = YES; 356 | CLANG_ENABLE_OBJC_WEAK = YES; 357 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 358 | CLANG_WARN_BOOL_CONVERSION = YES; 359 | CLANG_WARN_COMMA = YES; 360 | CLANG_WARN_CONSTANT_CONVERSION = YES; 361 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 362 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 363 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 364 | CLANG_WARN_EMPTY_BODY = YES; 365 | CLANG_WARN_ENUM_CONVERSION = YES; 366 | CLANG_WARN_INFINITE_RECURSION = YES; 367 | CLANG_WARN_INT_CONVERSION = YES; 368 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 369 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 370 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 371 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 372 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 373 | CLANG_WARN_STRICT_PROTOTYPES = YES; 374 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 375 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 376 | CLANG_WARN_UNREACHABLE_CODE = YES; 377 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 378 | CODE_SIGN_IDENTITY = "iPhone Developer"; 379 | COPY_PHASE_STRIP = NO; 380 | DEBUG_INFORMATION_FORMAT = dwarf; 381 | ENABLE_STRICT_OBJC_MSGSEND = YES; 382 | ENABLE_TESTABILITY = YES; 383 | GCC_C_LANGUAGE_STANDARD = gnu11; 384 | GCC_DYNAMIC_NO_PIC = NO; 385 | GCC_NO_COMMON_BLOCKS = YES; 386 | GCC_OPTIMIZATION_LEVEL = 0; 387 | GCC_PREPROCESSOR_DEFINITIONS = ( 388 | "DEBUG=1", 389 | "$(inherited)", 390 | ); 391 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 392 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 393 | GCC_WARN_UNDECLARED_SELECTOR = YES; 394 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 395 | GCC_WARN_UNUSED_FUNCTION = YES; 396 | GCC_WARN_UNUSED_VARIABLE = YES; 397 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 398 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 399 | MTL_FAST_MATH = YES; 400 | ONLY_ACTIVE_ARCH = YES; 401 | SDKROOT = iphoneos; 402 | }; 403 | name = Debug; 404 | }; 405 | 1E3CA6CC22F1834E008893BF /* Release */ = { 406 | isa = XCBuildConfiguration; 407 | buildSettings = { 408 | ALWAYS_SEARCH_USER_PATHS = NO; 409 | CLANG_ANALYZER_NONNULL = YES; 410 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 411 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 412 | CLANG_CXX_LIBRARY = "libc++"; 413 | CLANG_ENABLE_MODULES = YES; 414 | CLANG_ENABLE_OBJC_ARC = YES; 415 | CLANG_ENABLE_OBJC_WEAK = YES; 416 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 417 | CLANG_WARN_BOOL_CONVERSION = YES; 418 | CLANG_WARN_COMMA = YES; 419 | CLANG_WARN_CONSTANT_CONVERSION = YES; 420 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 421 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 422 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 423 | CLANG_WARN_EMPTY_BODY = YES; 424 | CLANG_WARN_ENUM_CONVERSION = YES; 425 | CLANG_WARN_INFINITE_RECURSION = YES; 426 | CLANG_WARN_INT_CONVERSION = YES; 427 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 428 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 429 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 430 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 431 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 432 | CLANG_WARN_STRICT_PROTOTYPES = YES; 433 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 434 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 435 | CLANG_WARN_UNREACHABLE_CODE = YES; 436 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 437 | CODE_SIGN_IDENTITY = "iPhone Developer"; 438 | COPY_PHASE_STRIP = NO; 439 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 440 | ENABLE_NS_ASSERTIONS = NO; 441 | ENABLE_STRICT_OBJC_MSGSEND = YES; 442 | GCC_C_LANGUAGE_STANDARD = gnu11; 443 | GCC_NO_COMMON_BLOCKS = YES; 444 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 445 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 446 | GCC_WARN_UNDECLARED_SELECTOR = YES; 447 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 448 | GCC_WARN_UNUSED_FUNCTION = YES; 449 | GCC_WARN_UNUSED_VARIABLE = YES; 450 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 451 | MTL_ENABLE_DEBUG_INFO = NO; 452 | MTL_FAST_MATH = YES; 453 | SDKROOT = iphoneos; 454 | VALIDATE_PRODUCT = YES; 455 | }; 456 | name = Release; 457 | }; 458 | 1E3CA6CE22F1834E008893BF /* Debug */ = { 459 | isa = XCBuildConfiguration; 460 | baseConfigurationReference = BFE3D4EF771DDA414C932F83 /* Pods-JFCitySelector Demo.debug.xcconfig */; 461 | buildSettings = { 462 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 463 | CODE_SIGN_STYLE = Automatic; 464 | DEVELOPMENT_TEAM = JJ55WSWQUD; 465 | INFOPLIST_FILE = "JFCitySelector Demo/Info.plist"; 466 | LD_RUNPATH_SEARCH_PATHS = ( 467 | "$(inherited)", 468 | "@executable_path/Frameworks", 469 | ); 470 | PRODUCT_BUNDLE_IDENTIFIER = "jf.JFCitySelector-Demo"; 471 | PRODUCT_NAME = "$(TARGET_NAME)"; 472 | TARGETED_DEVICE_FAMILY = "1,2"; 473 | }; 474 | name = Debug; 475 | }; 476 | 1E3CA6CF22F1834E008893BF /* Release */ = { 477 | isa = XCBuildConfiguration; 478 | baseConfigurationReference = EA4FC451CC07FC74FB2CBBE0 /* Pods-JFCitySelector Demo.release.xcconfig */; 479 | buildSettings = { 480 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 481 | CODE_SIGN_STYLE = Automatic; 482 | DEVELOPMENT_TEAM = JJ55WSWQUD; 483 | INFOPLIST_FILE = "JFCitySelector Demo/Info.plist"; 484 | LD_RUNPATH_SEARCH_PATHS = ( 485 | "$(inherited)", 486 | "@executable_path/Frameworks", 487 | ); 488 | PRODUCT_BUNDLE_IDENTIFIER = "jf.JFCitySelector-Demo"; 489 | PRODUCT_NAME = "$(TARGET_NAME)"; 490 | TARGETED_DEVICE_FAMILY = "1,2"; 491 | }; 492 | name = Release; 493 | }; 494 | /* End XCBuildConfiguration section */ 495 | 496 | /* Begin XCConfigurationList section */ 497 | 1E3CA6B222F1834E008893BF /* Build configuration list for PBXProject "JFCitySelector Demo" */ = { 498 | isa = XCConfigurationList; 499 | buildConfigurations = ( 500 | 1E3CA6CB22F1834E008893BF /* Debug */, 501 | 1E3CA6CC22F1834E008893BF /* Release */, 502 | ); 503 | defaultConfigurationIsVisible = 0; 504 | defaultConfigurationName = Release; 505 | }; 506 | 1E3CA6CD22F1834E008893BF /* Build configuration list for PBXNativeTarget "JFCitySelector Demo" */ = { 507 | isa = XCConfigurationList; 508 | buildConfigurations = ( 509 | 1E3CA6CE22F1834E008893BF /* Debug */, 510 | 1E3CA6CF22F1834E008893BF /* Release */, 511 | ); 512 | defaultConfigurationIsVisible = 0; 513 | defaultConfigurationName = Release; 514 | }; 515 | /* End XCConfigurationList section */ 516 | }; 517 | rootObject = 1E3CA6AF22F1834E008893BF /* Project object */; 518 | } 519 | -------------------------------------------------------------------------------- /JFCitySelector/Example/JFCitySelector Demo/JFCitySelector Demo.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /JFCitySelector/Example/JFCitySelector Demo/JFCitySelector Demo.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /JFCitySelector/Example/JFCitySelector Demo/JFCitySelector Demo.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /JFCitySelector/Example/JFCitySelector Demo/JFCitySelector Demo.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /JFCitySelector/Example/JFCitySelector Demo/JFCitySelector Demo/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // JFCitySelector Demo 4 | // 5 | // Created by zhifenx on 2019/7/31. 6 | // Copyright © 2019 zhifenx. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface AppDelegate : UIResponder 12 | 13 | @property (strong, nonatomic) UIWindow *window; 14 | 15 | 16 | @end 17 | 18 | -------------------------------------------------------------------------------- /JFCitySelector/Example/JFCitySelector Demo/JFCitySelector Demo/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // JFCitySelector Demo 4 | // 5 | // Created by zhifenx on 2019/7/31. 6 | // Copyright © 2019 zhifenx. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | 11 | @interface AppDelegate () 12 | 13 | @end 14 | 15 | @implementation AppDelegate 16 | 17 | 18 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 19 | // Override point for customization after application launch. 20 | return YES; 21 | } 22 | 23 | 24 | - (void)applicationWillResignActive:(UIApplication *)application { 25 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 26 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. 27 | } 28 | 29 | 30 | - (void)applicationDidEnterBackground:(UIApplication *)application { 31 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 32 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 33 | } 34 | 35 | 36 | - (void)applicationWillEnterForeground:(UIApplication *)application { 37 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background. 38 | } 39 | 40 | 41 | - (void)applicationDidBecomeActive:(UIApplication *)application { 42 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 43 | } 44 | 45 | 46 | - (void)applicationWillTerminate:(UIApplication *)application { 47 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 48 | } 49 | 50 | 51 | @end 52 | -------------------------------------------------------------------------------- /JFCitySelector/Example/JFCitySelector Demo/JFCitySelector Demo/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "20x20", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "20x20", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "29x29", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "29x29", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "size" : "40x40", 66 | "scale" : "1x" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "size" : "40x40", 71 | "scale" : "2x" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "size" : "76x76", 76 | "scale" : "1x" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "size" : "76x76", 81 | "scale" : "2x" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "size" : "83.5x83.5", 86 | "scale" : "2x" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "size" : "1024x1024", 91 | "scale" : "1x" 92 | } 93 | ], 94 | "info" : { 95 | "version" : 1, 96 | "author" : "xcode" 97 | } 98 | } -------------------------------------------------------------------------------- /JFCitySelector/Example/JFCitySelector Demo/JFCitySelector Demo/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /JFCitySelector/Example/JFCitySelector Demo/JFCitySelector Demo/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /JFCitySelector/Example/JFCitySelector Demo/JFCitySelector Demo/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /JFCitySelector/Example/JFCitySelector Demo/JFCitySelector Demo/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UILaunchStoryboardName 24 | LaunchScreen 25 | UIMainStoryboardFile 26 | Main 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | UISupportedInterfaceOrientations~ipad 38 | 39 | UIInterfaceOrientationPortrait 40 | UIInterfaceOrientationPortraitUpsideDown 41 | UIInterfaceOrientationLandscapeLeft 42 | UIInterfaceOrientationLandscapeRight 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /JFCitySelector/Example/JFCitySelector Demo/JFCitySelector Demo/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // JFCitySelector Demo 4 | // 5 | // Created by zhifenx on 2019/7/31. 6 | // Copyright © 2019 zhifenx. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ViewController : UIViewController 12 | 13 | 14 | @end 15 | 16 | -------------------------------------------------------------------------------- /JFCitySelector/Example/JFCitySelector Demo/JFCitySelector Demo/ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.m 3 | // JFCitySelector Demo 4 | // 5 | // Created by zhifenx on 2019/7/31. 6 | // Copyright © 2019 zhifenx. All rights reserved. 7 | // 8 | 9 | #import "ViewController.h" 10 | 11 | #import "JFCitySelector.h" 12 | 13 | // 自定义Log 14 | #ifdef RELEASE 15 | #define NSLog(...) 16 | #else 17 | #define NSLog(...) NSLog(@"%s 第%d行 \n %@\n\n",__func__,__LINE__,[NSString stringWithFormat:__VA_ARGS__]) 18 | #endif 19 | 20 | #define JFWeakSelf(type) __weak typeof(type) weak##type = type; 21 | #define JFStrongSelf(type) __strong typeof(type) type = weak##type; 22 | 23 | @interface ViewController () 24 | 25 | @property (nonatomic, strong) UITableView *tableView; 26 | @property (nonatomic, strong) NSArray *dataArr; 27 | @property (nonatomic, strong) UITextView *textView; 28 | @property (nonatomic, strong) JFCSConfiguration *config; 29 | @property (nonatomic, strong) JFCSDataOpreation *dataOpreation; 30 | 31 | @end 32 | 33 | @implementation ViewController 34 | 35 | - (void)viewDidLoad { 36 | [super viewDidLoad]; 37 | 38 | self.config = [[JFCSConfiguration alloc] init]; 39 | self.dataOpreation = [[JFCSDataOpreation alloc] initWithConfiguration:self.config]; 40 | 41 | self.title = @"JFCitySelector"; 42 | self.dataArr = @[@"JFCSTableViewController", 43 | @"省份数据", 44 | @"市数据", 45 | @"县数据", 46 | @"省份数据包含的首字母", 47 | @"市数据包含的首字母", 48 | @"县数据包含的首字母", 49 | @"所有名称包含的首字母"]; 50 | [self.view addSubview:self.tableView]; 51 | } 52 | 53 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { 54 | return self.dataArr.count; 55 | } 56 | 57 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { 58 | UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cell"]; 59 | cell.textLabel.text = self.dataArr[indexPath.row]; 60 | cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator; 61 | return cell; 62 | } 63 | 64 | - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { 65 | switch (indexPath.row) { 66 | case 0: { 67 | JFCSTableViewController *vc = [[JFCSTableViewController alloc] initWithConfiguration:self.config delegate:self]; 68 | [self.navigationController pushViewController:vc animated:YES]; 69 | } 70 | 71 | break; 72 | case 1: { 73 | JFWeakSelf(self); 74 | [self.dataOpreation provinces:^(NSArray * _Nonnull provinces) { 75 | JFStrongSelf(self); 76 | self.textView.text = [provinces yy_modelDescription]; 77 | }]; 78 | } 79 | 80 | break; 81 | case 2: { 82 | JFWeakSelf(self); 83 | [self.dataOpreation cities:^(NSArray * _Nonnull cities) { 84 | JFStrongSelf(self); 85 | self.textView.text = [cities yy_modelDescription]; 86 | }]; 87 | } 88 | 89 | break; 90 | case 3: { 91 | JFWeakSelf(self); 92 | [self.dataOpreation areas:^(NSArray * _Nonnull areas) { 93 | JFStrongSelf(self); 94 | self.textView.text = [areas yy_modelDescription]; 95 | }]; 96 | } 97 | 98 | break; 99 | case 4: { 100 | self.textView.text = [self.dataOpreation.firstLetterArraryOfProvince yy_modelDescription]; 101 | } 102 | 103 | break; 104 | case 5: { 105 | self.textView.text = [self.dataOpreation.firstLetterArraryOfCity yy_modelDescription]; 106 | } 107 | 108 | break; 109 | case 6: { 110 | self.textView.text = [self.dataOpreation.firstLetterArraryOfArea yy_modelDescription]; 111 | } 112 | 113 | break; 114 | case 7: { 115 | self.textView.text = [self.dataOpreation.firstLetterArraryOfAllCities yy_modelDescription]; 116 | } 117 | 118 | break; 119 | 120 | default: 121 | break; 122 | } 123 | } 124 | 125 | - (UITableView *)tableView { 126 | if (!_tableView) { 127 | _tableView = [[UITableView alloc] initWithFrame:[UIScreen mainScreen].bounds style:UITableViewStylePlain]; 128 | [_tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:@"cell"]; 129 | _tableView.delegate = self; 130 | _tableView.dataSource = self; 131 | _tableView.tableFooterView = self.textView; 132 | } 133 | return _tableView; 134 | } 135 | 136 | - (UITextView *)textView { 137 | if (!_textView) { 138 | _textView = [[UITextView alloc] initWithFrame:CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width, [UIScreen mainScreen].bounds.size.height - 44 * self.dataArr.count - 44 - 20)]; 139 | _textView.textColor = [UIColor redColor]; 140 | _textView.font = [UIFont systemFontOfSize:14.0]; 141 | _textView.backgroundColor = [UIColor colorWithRed:0.95 green:0.95 blue:0.95 alpha:1]; 142 | } 143 | return _textView; 144 | } 145 | 146 | #pragma mark -- JFCSTableViewControllerDelegate 147 | 148 | - (void)viewController:(JFCSTableViewController *)viewController didSelectCity:(JFCSBaseInfoModel *)model { 149 | self.textView.text = [NSString stringWithFormat:@"%@",[model yy_modelDescription]]; 150 | } 151 | 152 | 153 | @end 154 | -------------------------------------------------------------------------------- /JFCitySelector/Example/JFCitySelector Demo/JFCitySelector Demo/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // JFCitySelector Demo 4 | // 5 | // Created by zhifenx on 2019/7/31. 6 | // Copyright © 2019 zhifenx. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "AppDelegate.h" 11 | 12 | int main(int argc, char * argv[]) { 13 | @autoreleasepool { 14 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /JFCitySelector/Example/JFCitySelector Demo/Podfile: -------------------------------------------------------------------------------- 1 | #阿里source 2 | source 'https://github.com/CocoaPods/Specs.git' 3 | source 'https://github.com/aliyun/aliyun-specs.git' 4 | 5 | # Comment the next line if you're not using Swift and don't want to use dynamic frameworks 6 | platform :ios, '10.0' 7 | inhibit_all_warnings!#去掉pod的工程中的警告 8 | use_frameworks! 9 | 10 | target 'JFCitySelector Demo' do 11 | 12 | pod 'YYModel' 13 | 14 | end 15 | -------------------------------------------------------------------------------- /JFCitySelector/Example/JFCitySelector Demo/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - YYModel (1.0.4) 3 | 4 | DEPENDENCIES: 5 | - YYModel 6 | 7 | SPEC REPOS: 8 | https://github.com/cocoapods/specs.git: 9 | - YYModel 10 | 11 | SPEC CHECKSUMS: 12 | YYModel: 2a7fdd96aaa4b86a824e26d0c517de8928c04b30 13 | 14 | PODFILE CHECKSUM: 263fcaa7a3531e9ff95ac72dfc19191fc44dc052 15 | 16 | COCOAPODS: 1.7.4 17 | -------------------------------------------------------------------------------- /JFCitySelector/Example/JFCitySelector Demo/Pods/Manifest.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - YYModel (1.0.4) 3 | 4 | DEPENDENCIES: 5 | - YYModel 6 | 7 | SPEC REPOS: 8 | https://github.com/cocoapods/specs.git: 9 | - YYModel 10 | 11 | SPEC CHECKSUMS: 12 | YYModel: 2a7fdd96aaa4b86a824e26d0c517de8928c04b30 13 | 14 | PODFILE CHECKSUM: 263fcaa7a3531e9ff95ac72dfc19191fc44dc052 15 | 16 | COCOAPODS: 1.7.4 17 | -------------------------------------------------------------------------------- /JFCitySelector/Example/JFCitySelector Demo/Pods/Target Support Files/Pods-JFCitySelector Demo/Pods-JFCitySelector Demo-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | ${PRODUCT_BUNDLE_IDENTIFIER} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | ${PRODUCT_NAME} 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /JFCitySelector/Example/JFCitySelector Demo/Pods/Target Support Files/Pods-JFCitySelector Demo/Pods-JFCitySelector Demo-acknowledgements.markdown: -------------------------------------------------------------------------------- 1 | # Acknowledgements 2 | This application makes use of the following third party libraries: 3 | 4 | ## YYModel 5 | 6 | The MIT License (MIT) 7 | 8 | Copyright (c) 2015 ibireme 9 | 10 | Permission is hereby granted, free of charge, to any person obtaining a copy 11 | of this software and associated documentation files (the "Software"), to deal 12 | in the Software without restriction, including without limitation the rights 13 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | copies of the Software, and to permit persons to whom the Software is 15 | furnished to do so, subject to the following conditions: 16 | 17 | The above copyright notice and this permission notice shall be included in all 18 | copies or substantial portions of the Software. 19 | 20 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 26 | SOFTWARE. 27 | 28 | 29 | Generated by CocoaPods - https://cocoapods.org 30 | -------------------------------------------------------------------------------- /JFCitySelector/Example/JFCitySelector Demo/Pods/Target Support Files/Pods-JFCitySelector Demo/Pods-JFCitySelector Demo-acknowledgements.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreferenceSpecifiers 6 | 7 | 8 | FooterText 9 | This application makes use of the following third party libraries: 10 | Title 11 | Acknowledgements 12 | Type 13 | PSGroupSpecifier 14 | 15 | 16 | FooterText 17 | The MIT License (MIT) 18 | 19 | Copyright (c) 2015 ibireme <ibireme@gmail.com> 20 | 21 | Permission is hereby granted, free of charge, to any person obtaining a copy 22 | of this software and associated documentation files (the "Software"), to deal 23 | in the Software without restriction, including without limitation the rights 24 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 25 | copies of the Software, and to permit persons to whom the Software is 26 | furnished to do so, subject to the following conditions: 27 | 28 | The above copyright notice and this permission notice shall be included in all 29 | copies or substantial portions of the Software. 30 | 31 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 32 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 33 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 34 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 35 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 36 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 37 | SOFTWARE. 38 | 39 | 40 | License 41 | MIT 42 | Title 43 | YYModel 44 | Type 45 | PSGroupSpecifier 46 | 47 | 48 | FooterText 49 | Generated by CocoaPods - https://cocoapods.org 50 | Title 51 | 52 | Type 53 | PSGroupSpecifier 54 | 55 | 56 | StringsTable 57 | Acknowledgements 58 | Title 59 | Acknowledgements 60 | 61 | 62 | -------------------------------------------------------------------------------- /JFCitySelector/Example/JFCitySelector Demo/Pods/Target Support Files/Pods-JFCitySelector Demo/Pods-JFCitySelector Demo-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_Pods_JFCitySelector_Demo : NSObject 3 | @end 4 | @implementation PodsDummy_Pods_JFCitySelector_Demo 5 | @end 6 | -------------------------------------------------------------------------------- /JFCitySelector/Example/JFCitySelector Demo/Pods/Target Support Files/Pods-JFCitySelector Demo/Pods-JFCitySelector Demo-frameworks-Debug-input-files.xcfilelist: -------------------------------------------------------------------------------- 1 | ${PODS_ROOT}/Target Support Files/Pods-JFCitySelector Demo/Pods-JFCitySelector Demo-frameworks.sh 2 | ${BUILT_PRODUCTS_DIR}/YYModel/YYModel.framework -------------------------------------------------------------------------------- /JFCitySelector/Example/JFCitySelector Demo/Pods/Target Support Files/Pods-JFCitySelector Demo/Pods-JFCitySelector Demo-frameworks-Debug-output-files.xcfilelist: -------------------------------------------------------------------------------- 1 | ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/YYModel.framework -------------------------------------------------------------------------------- /JFCitySelector/Example/JFCitySelector Demo/Pods/Target Support Files/Pods-JFCitySelector Demo/Pods-JFCitySelector Demo-frameworks-Release-input-files.xcfilelist: -------------------------------------------------------------------------------- 1 | ${PODS_ROOT}/Target Support Files/Pods-JFCitySelector Demo/Pods-JFCitySelector Demo-frameworks.sh 2 | ${BUILT_PRODUCTS_DIR}/YYModel/YYModel.framework -------------------------------------------------------------------------------- /JFCitySelector/Example/JFCitySelector Demo/Pods/Target Support Files/Pods-JFCitySelector Demo/Pods-JFCitySelector Demo-frameworks-Release-output-files.xcfilelist: -------------------------------------------------------------------------------- 1 | ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/YYModel.framework -------------------------------------------------------------------------------- /JFCitySelector/Example/JFCitySelector Demo/Pods/Target Support Files/Pods-JFCitySelector Demo/Pods-JFCitySelector Demo-frameworks.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | set -u 4 | set -o pipefail 5 | 6 | function on_error { 7 | echo "$(realpath -mq "${0}"):$1: error: Unexpected failure" 8 | } 9 | trap 'on_error $LINENO' ERR 10 | 11 | if [ -z ${FRAMEWORKS_FOLDER_PATH+x} ]; then 12 | # If FRAMEWORKS_FOLDER_PATH is not set, then there's nowhere for us to copy 13 | # frameworks to, so exit 0 (signalling the script phase was successful). 14 | exit 0 15 | fi 16 | 17 | echo "mkdir -p ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 18 | mkdir -p "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 19 | 20 | COCOAPODS_PARALLEL_CODE_SIGN="${COCOAPODS_PARALLEL_CODE_SIGN:-false}" 21 | SWIFT_STDLIB_PATH="${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" 22 | 23 | # Used as a return value for each invocation of `strip_invalid_archs` function. 24 | STRIP_BINARY_RETVAL=0 25 | 26 | # This protects against multiple targets copying the same framework dependency at the same time. The solution 27 | # was originally proposed here: https://lists.samba.org/archive/rsync/2008-February/020158.html 28 | RSYNC_PROTECT_TMP_FILES=(--filter "P .*.??????") 29 | 30 | # Copies and strips a vendored framework 31 | install_framework() 32 | { 33 | if [ -r "${BUILT_PRODUCTS_DIR}/$1" ]; then 34 | local source="${BUILT_PRODUCTS_DIR}/$1" 35 | elif [ -r "${BUILT_PRODUCTS_DIR}/$(basename "$1")" ]; then 36 | local source="${BUILT_PRODUCTS_DIR}/$(basename "$1")" 37 | elif [ -r "$1" ]; then 38 | local source="$1" 39 | fi 40 | 41 | local destination="${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 42 | 43 | if [ -L "${source}" ]; then 44 | echo "Symlinked..." 45 | source="$(readlink "${source}")" 46 | fi 47 | 48 | # Use filter instead of exclude so missing patterns don't throw errors. 49 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${destination}\"" 50 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${destination}" 51 | 52 | local basename 53 | basename="$(basename -s .framework "$1")" 54 | binary="${destination}/${basename}.framework/${basename}" 55 | 56 | if ! [ -r "$binary" ]; then 57 | binary="${destination}/${basename}" 58 | elif [ -L "${binary}" ]; then 59 | echo "Destination binary is symlinked..." 60 | dirname="$(dirname "${binary}")" 61 | binary="${dirname}/$(readlink "${binary}")" 62 | fi 63 | 64 | # Strip invalid architectures so "fat" simulator / device frameworks work on device 65 | if [[ "$(file "$binary")" == *"dynamically linked shared library"* ]]; then 66 | strip_invalid_archs "$binary" 67 | fi 68 | 69 | # Resign the code if required by the build settings to avoid unstable apps 70 | code_sign_if_enabled "${destination}/$(basename "$1")" 71 | 72 | # Embed linked Swift runtime libraries. No longer necessary as of Xcode 7. 73 | if [ "${XCODE_VERSION_MAJOR}" -lt 7 ]; then 74 | local swift_runtime_libs 75 | swift_runtime_libs=$(xcrun otool -LX "$binary" | grep --color=never @rpath/libswift | sed -E s/@rpath\\/\(.+dylib\).*/\\1/g | uniq -u) 76 | for lib in $swift_runtime_libs; do 77 | echo "rsync -auv \"${SWIFT_STDLIB_PATH}/${lib}\" \"${destination}\"" 78 | rsync -auv "${SWIFT_STDLIB_PATH}/${lib}" "${destination}" 79 | code_sign_if_enabled "${destination}/${lib}" 80 | done 81 | fi 82 | } 83 | 84 | # Copies and strips a vendored dSYM 85 | install_dsym() { 86 | local source="$1" 87 | if [ -r "$source" ]; then 88 | # Copy the dSYM into a the targets temp dir. 89 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${DERIVED_FILES_DIR}\"" 90 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${DERIVED_FILES_DIR}" 91 | 92 | local basename 93 | basename="$(basename -s .framework.dSYM "$source")" 94 | binary="${DERIVED_FILES_DIR}/${basename}.framework.dSYM/Contents/Resources/DWARF/${basename}" 95 | 96 | # Strip invalid architectures so "fat" simulator / device frameworks work on device 97 | if [[ "$(file "$binary")" == *"Mach-O "*"dSYM companion"* ]]; then 98 | strip_invalid_archs "$binary" 99 | fi 100 | 101 | if [[ $STRIP_BINARY_RETVAL == 1 ]]; then 102 | # Move the stripped file into its final destination. 103 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${DERIVED_FILES_DIR}/${basename}.framework.dSYM\" \"${DWARF_DSYM_FOLDER_PATH}\"" 104 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${DERIVED_FILES_DIR}/${basename}.framework.dSYM" "${DWARF_DSYM_FOLDER_PATH}" 105 | else 106 | # The dSYM was not stripped at all, in this case touch a fake folder so the input/output paths from Xcode do not reexecute this script because the file is missing. 107 | touch "${DWARF_DSYM_FOLDER_PATH}/${basename}.framework.dSYM" 108 | fi 109 | fi 110 | } 111 | 112 | # Copies the bcsymbolmap files of a vendored framework 113 | install_bcsymbolmap() { 114 | local bcsymbolmap_path="$1" 115 | local destination="${BUILT_PRODUCTS_DIR}" 116 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${bcsymbolmap_path}" "${destination}"" 117 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${bcsymbolmap_path}" "${destination}" 118 | } 119 | 120 | # Signs a framework with the provided identity 121 | code_sign_if_enabled() { 122 | if [ -n "${EXPANDED_CODE_SIGN_IDENTITY:-}" -a "${CODE_SIGNING_REQUIRED:-}" != "NO" -a "${CODE_SIGNING_ALLOWED}" != "NO" ]; then 123 | # Use the current code_sign_identity 124 | echo "Code Signing $1 with Identity ${EXPANDED_CODE_SIGN_IDENTITY_NAME}" 125 | local code_sign_cmd="/usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} ${OTHER_CODE_SIGN_FLAGS:-} --preserve-metadata=identifier,entitlements '$1'" 126 | 127 | if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then 128 | code_sign_cmd="$code_sign_cmd &" 129 | fi 130 | echo "$code_sign_cmd" 131 | eval "$code_sign_cmd" 132 | fi 133 | } 134 | 135 | # Strip invalid architectures 136 | strip_invalid_archs() { 137 | binary="$1" 138 | # Get architectures for current target binary 139 | binary_archs="$(lipo -info "$binary" | rev | cut -d ':' -f1 | awk '{$1=$1;print}' | rev)" 140 | # Intersect them with the architectures we are building for 141 | intersected_archs="$(echo ${ARCHS[@]} ${binary_archs[@]} | tr ' ' '\n' | sort | uniq -d)" 142 | # If there are no archs supported by this binary then warn the user 143 | if [[ -z "$intersected_archs" ]]; then 144 | echo "warning: [CP] Vendored binary '$binary' contains architectures ($binary_archs) none of which match the current build architectures ($ARCHS)." 145 | STRIP_BINARY_RETVAL=0 146 | return 147 | fi 148 | stripped="" 149 | for arch in $binary_archs; do 150 | if ! [[ "${ARCHS}" == *"$arch"* ]]; then 151 | # Strip non-valid architectures in-place 152 | lipo -remove "$arch" -output "$binary" "$binary" 153 | stripped="$stripped $arch" 154 | fi 155 | done 156 | if [[ "$stripped" ]]; then 157 | echo "Stripped $binary of architectures:$stripped" 158 | fi 159 | STRIP_BINARY_RETVAL=1 160 | } 161 | 162 | 163 | if [[ "$CONFIGURATION" == "Debug" ]]; then 164 | install_framework "${BUILT_PRODUCTS_DIR}/YYModel/YYModel.framework" 165 | fi 166 | if [[ "$CONFIGURATION" == "Release" ]]; then 167 | install_framework "${BUILT_PRODUCTS_DIR}/YYModel/YYModel.framework" 168 | fi 169 | if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then 170 | wait 171 | fi 172 | -------------------------------------------------------------------------------- /JFCitySelector/Example/JFCitySelector Demo/Pods/Target Support Files/Pods-JFCitySelector Demo/Pods-JFCitySelector Demo-umbrella.h: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | 14 | FOUNDATION_EXPORT double Pods_JFCitySelector_DemoVersionNumber; 15 | FOUNDATION_EXPORT const unsigned char Pods_JFCitySelector_DemoVersionString[]; 16 | 17 | -------------------------------------------------------------------------------- /JFCitySelector/Example/JFCitySelector Demo/Pods/Target Support Files/Pods-JFCitySelector Demo/Pods-JFCitySelector Demo.debug.xcconfig: -------------------------------------------------------------------------------- 1 | FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/YYModel" 2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 3 | HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/YYModel/YYModel.framework/Headers" 4 | LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' 5 | OTHER_CFLAGS = $(inherited) -isystem "${PODS_CONFIGURATION_BUILD_DIR}/YYModel/YYModel.framework/Headers" -iframework "${PODS_CONFIGURATION_BUILD_DIR}/YYModel" 6 | OTHER_LDFLAGS = $(inherited) -framework "CoreFoundation" -framework "Foundation" -framework "YYModel" 7 | PODS_BUILD_DIR = ${BUILD_DIR} 8 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 9 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/. 10 | PODS_ROOT = ${SRCROOT}/Pods 11 | -------------------------------------------------------------------------------- /JFCitySelector/Example/JFCitySelector Demo/Pods/Target Support Files/Pods-JFCitySelector Demo/Pods-JFCitySelector Demo.modulemap: -------------------------------------------------------------------------------- 1 | framework module Pods_JFCitySelector_Demo { 2 | umbrella header "Pods-JFCitySelector Demo-umbrella.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /JFCitySelector/Example/JFCitySelector Demo/Pods/Target Support Files/Pods-JFCitySelector Demo/Pods-JFCitySelector Demo.release.xcconfig: -------------------------------------------------------------------------------- 1 | FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/YYModel" 2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 3 | HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/YYModel/YYModel.framework/Headers" 4 | LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' 5 | OTHER_CFLAGS = $(inherited) -isystem "${PODS_CONFIGURATION_BUILD_DIR}/YYModel/YYModel.framework/Headers" -iframework "${PODS_CONFIGURATION_BUILD_DIR}/YYModel" 6 | OTHER_LDFLAGS = $(inherited) -framework "CoreFoundation" -framework "Foundation" -framework "YYModel" 7 | PODS_BUILD_DIR = ${BUILD_DIR} 8 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 9 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/. 10 | PODS_ROOT = ${SRCROOT}/Pods 11 | -------------------------------------------------------------------------------- /JFCitySelector/Example/JFCitySelector Demo/Pods/Target Support Files/YYModel/YYModel-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | ${PRODUCT_BUNDLE_IDENTIFIER} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | ${PRODUCT_NAME} 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0.4 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /JFCitySelector/Example/JFCitySelector Demo/Pods/Target Support Files/YYModel/YYModel-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_YYModel : NSObject 3 | @end 4 | @implementation PodsDummy_YYModel 5 | @end 6 | -------------------------------------------------------------------------------- /JFCitySelector/Example/JFCitySelector Demo/Pods/Target Support Files/YYModel/YYModel-prefix.pch: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | -------------------------------------------------------------------------------- /JFCitySelector/Example/JFCitySelector Demo/Pods/Target Support Files/YYModel/YYModel-umbrella.h: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | #import "NSObject+YYModel.h" 14 | #import "YYClassInfo.h" 15 | #import "YYModel.h" 16 | 17 | FOUNDATION_EXPORT double YYModelVersionNumber; 18 | FOUNDATION_EXPORT const unsigned char YYModelVersionString[]; 19 | 20 | -------------------------------------------------------------------------------- /JFCitySelector/Example/JFCitySelector Demo/Pods/Target Support Files/YYModel/YYModel.modulemap: -------------------------------------------------------------------------------- 1 | framework module YYModel { 2 | umbrella header "YYModel-umbrella.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /JFCitySelector/Example/JFCitySelector Demo/Pods/Target Support Files/YYModel/YYModel.xcconfig: -------------------------------------------------------------------------------- 1 | CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/YYModel 2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 3 | OTHER_LDFLAGS = $(inherited) -framework "CoreFoundation" -framework "Foundation" 4 | PODS_BUILD_DIR = ${BUILD_DIR} 5 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 6 | PODS_ROOT = ${SRCROOT} 7 | PODS_TARGET_SRCROOT = ${PODS_ROOT}/YYModel 8 | PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} 9 | SKIP_INSTALL = YES 10 | -------------------------------------------------------------------------------- /JFCitySelector/Example/JFCitySelector Demo/Pods/YYModel/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 ibireme 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /JFCitySelector/Example/JFCitySelector Demo/Pods/YYModel/README.md: -------------------------------------------------------------------------------- 1 | YYModel 2 | ============== 3 | 4 | [![License MIT](https://img.shields.io/badge/license-MIT-green.svg?style=flat)](https://raw.githubusercontent.com/ibireme/YYModel/master/LICENSE)  5 | [![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage)  6 | [![CocoaPods](http://img.shields.io/cocoapods/v/YYModel.svg?style=flat)](http://cocoapods.org/?q= YYModel)  7 | [![CocoaPods](http://img.shields.io/cocoapods/p/YYModel.svg?style=flat)](http://cocoapods.org/?q= YYModel)  8 | [![Build Status](https://travis-ci.org/ibireme/YYModel.svg?branch=master)](https://travis-ci.org/ibireme/YYModel)  9 | [![codecov.io](https://codecov.io/github/ibireme/YYModel/coverage.svg?branch=master)](https://codecov.io/github/ibireme/YYModel?branch=master) 10 | 11 | High performance model framework for iOS/OSX.
12 | (It's a component of [YYKit](https://github.com/ibireme/YYKit)) 13 | 14 | 15 | Performance 16 | ============== 17 | 18 | Time cost (process GithubUser 10000 times on iPhone 6): 19 | 20 | ![Benchmark result](https://raw.github.com/ibireme/YYModel/master/Benchmark/Result.png 21 | ) 22 | 23 | See `Benchmark/ModelBenchmark.xcodeproj` for more benchmark case. 24 | 25 | 26 | Features 27 | ============== 28 | - **High performance**: The conversion performance is close to handwriting code. 29 | - **Automatic type conversion**: The object types can be automatically converted. 30 | - **Type Safe**: All data types will be verified to ensure type-safe during the conversion process. 31 | - **Non-intrusive**: There is no need to make the model class inherit from other base class. 32 | - **Lightwight**: This library contains only 5 files. 33 | - **Docs and unit testing**: 100% docs coverage, 99.6% code coverage. 34 | 35 | Usage 36 | ============== 37 | 38 | ###Simple model json convert 39 | 40 | // JSON: 41 | { 42 | "uid":123456, 43 | "name":"Harry", 44 | "created":"1965-07-31T00:00:00+0000" 45 | } 46 | 47 | // Model: 48 | @interface User : NSObject 49 | @property UInt64 uid; 50 | @property NSString *name; 51 | @property NSDate *created; 52 | @end 53 | @implementation User 54 | @end 55 | 56 | 57 | // Convert json to model: 58 | User *user = [User yy_modelWithJSON:json]; 59 | 60 | // Convert model to json: 61 | NSDictionary *json = [user yy_modelToJSONObject]; 62 | 63 | 64 | If the type of an object in JSON/Dictionary cannot be matched to the property of the model, the following automatic conversion is performed. If the automatic conversion failed, the value will be ignored. 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 85 | 86 | 87 | 88 | 95 | 96 | 97 | 98 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 |
JSON/DictionaryModel
NSStringNSNumber,NSURL,SEL,Class
NSNumberNSString
NSString/NSNumberC number (BOOL,int,float,NSUInteger,UInt64,...)
84 | NaN and Inf will be ignored
NSStringNSDate parsed with these formats:
89 | yyyy-MM-dd
90 | yyyy-MM-dd HH:mm:ss
91 | yyyy-MM-dd'T'HH:mm:ss
92 | yyyy-MM-dd'T'HH:mm:ssZ
93 | EEE MMM dd HH:mm:ss Z yyyy 94 |
NSDateNSString formatted with ISO8601:
99 | "YYYY-MM-dd'T'HH:mm:ssZ"
NSValuestruct (CGRect,CGSize,...)
NSNullnil,0
"no","false",...@(NO),0
"yes","true",...@(YES),1
119 | 120 | 121 | 122 | ###Match model property to different JSON key 123 | 124 | // JSON: 125 | { 126 | "n":"Harry Pottery", 127 | "p": 256, 128 | "ext" : { 129 | "desc" : "A book written by J.K.Rowing." 130 | }, 131 | "ID" : 100010 132 | } 133 | 134 | // Model: 135 | @interface Book : NSObject 136 | @property NSString *name; 137 | @property NSInteger page; 138 | @property NSString *desc; 139 | @property NSString *bookID; 140 | @end 141 | @implementation Book 142 | + (NSDictionary *)modelCustomPropertyMapper { 143 | return @{@"name" : @"n", 144 | @"page" : @"p", 145 | @"desc" : @"ext.desc", 146 | @"bookID" : @[@"id",@"ID",@"book_id"]}; 147 | } 148 | @end 149 | 150 | You can map a json key (key path) or an array of json key (key path) to one or multiple property name. If there's no mapper for a property, it will use the property's name as default. 151 | 152 | ###Nested model 153 | 154 | // JSON 155 | { 156 | "author":{ 157 | "name":"J.K.Rowling", 158 | "birthday":"1965-07-31T00:00:00+0000" 159 | }, 160 | "name":"Harry Potter", 161 | "pages":256 162 | } 163 | 164 | // Model: (no need to do anything) 165 | @interface Author : NSObject 166 | @property NSString *name; 167 | @property NSDate *birthday; 168 | @end 169 | @implementation Author 170 | @end 171 | 172 | @interface Book : NSObject 173 | @property NSString *name; 174 | @property NSUInteger pages; 175 | @property Author *author; 176 | @end 177 | @implementation Book 178 | @end 179 | 180 | 181 | 182 | ### Container property 183 | 184 | @class Shadow, Border, Attachment; 185 | 186 | @interface Attributes 187 | @property NSString *name; 188 | @property NSArray *shadows; //Array 189 | @property NSSet *borders; //Set 190 | @property NSMutableDictionary *attachments; //Dict 191 | @end 192 | 193 | @implementation Attributes 194 | + (NSDictionary *)modelContainerPropertyGenericClass { 195 | // value should be Class or Class name. 196 | return @{@"shadows" : [Shadow class], 197 | @"borders" : Border.class, 198 | @"attachments" : @"Attachment" }; 199 | } 200 | @end 201 | 202 | 203 | 204 | 205 | ### Whitelist and blacklist 206 | 207 | @interface User 208 | @property NSString *name; 209 | @property NSUInteger age; 210 | @end 211 | 212 | @implementation Attributes 213 | + (NSArray *)modelPropertyBlacklist { 214 | return @[@"test1", @"test2"]; 215 | } 216 | + (NSArray *)modelPropertyWhitelist { 217 | return @[@"name"]; 218 | } 219 | @end 220 | 221 | ###Data validate and custom transform 222 | 223 | // JSON: 224 | { 225 | "name":"Harry", 226 | "timestamp" : 1445534567 227 | } 228 | 229 | // Model: 230 | @interface User 231 | @property NSString *name; 232 | @property NSDate *createdAt; 233 | @end 234 | 235 | @implementation User 236 | - (BOOL)modelCustomTransformFromDictionary:(NSDictionary *)dic { 237 | NSNumber *timestamp = dic[@"timestamp"]; 238 | if (![timestamp isKindOfClass:[NSNumber class]]) return NO; 239 | _createdAt = [NSDate dateWithTimeIntervalSince1970:timestamp.floatValue]; 240 | return YES; 241 | } 242 | - (BOOL)modelCustomTransformToDictionary:(NSMutableDictionary *)dic { 243 | if (!_createdAt) return NO; 244 | dic[@"timestamp"] = @(n.timeIntervalSince1970); 245 | return YES; 246 | } 247 | @end 248 | 249 | ###Coding/Copying/hash/equal/description 250 | 251 | @interface YYShadow :NSObject 252 | @property (nonatomic, copy) NSString *name; 253 | @property (nonatomic, assign) CGSize size; 254 | @end 255 | 256 | @implementation YYShadow 257 | - (void)encodeWithCoder:(NSCoder *)aCoder { [self yy_modelEncodeWithCoder:aCoder]; } 258 | - (id)initWithCoder:(NSCoder *)aDecoder { self = [super init]; return [self yy_modelInitWithCoder:aDecoder]; } 259 | - (id)copyWithZone:(NSZone *)zone { return [self yy_modelCopy]; } 260 | - (NSUInteger)hash { return [self yy_modelHash]; } 261 | - (BOOL)isEqual:(id)object { return [self yy_modelIsEqual:object]; } 262 | - (NSString *)description { return [self yy_modelDescription]; } 263 | @end 264 | 265 | 266 | Installation 267 | ============== 268 | 269 | ### CocoaPods 270 | 271 | 1. Add `pod 'YYModel'` to your Podfile. 272 | 2. Run `pod install` or `pod update`. 273 | 3. Import \. 274 | 275 | 276 | ### Carthage 277 | 278 | 1. Add `github "ibireme/YYModel"` to your Cartfile. 279 | 2. Run `carthage update --platform ios` and add the framework to your project. 280 | 3. Import \. 281 | 282 | 283 | ### Manually 284 | 285 | 1. Download all the files in the YYModel subdirectory. 286 | 2. Add the source files to your Xcode project. 287 | 3. Import `YYModel.h`. 288 | 289 | 290 | Documentation 291 | ============== 292 | Full API documentation is available on [CocoaDocs](http://cocoadocs.org/docsets/YYModel/).
293 | You can also install documentation locally using [appledoc](https://github.com/tomaz/appledoc). 294 | 295 | 296 | Requirements 297 | ============== 298 | This library requires `iOS 6.0+` and `Xcode 7.0+`. 299 | 300 | 301 | License 302 | ============== 303 | YYModel is provided under the MIT license. See LICENSE file for details. 304 | 305 | 306 |

307 | --- 308 | 中文介绍 309 | ============== 310 | 高性能 iOS/OSX 模型转换框架。
311 | (该项目是 [YYKit](https://github.com/ibireme/YYKit) 组件之一) 312 | 313 | 314 | 性能 315 | ============== 316 | 处理 GithubUser 数据 10000 次耗时统计 (iPhone 6): 317 | 318 | ![Benchmark result](https://raw.github.com/ibireme/YYModel/master/Benchmark/Result.png 319 | ) 320 | 321 | 更多测试代码和用例见 `Benchmark/ModelBenchmark.xcodeproj`。 322 | 323 | 324 | 特性 325 | ============== 326 | - **高性能**: 模型转换性能接近手写解析代码。 327 | - **自动类型转换**: 对象类型可以自动转换,详情见下方表格。 328 | - **类型安全**: 转换过程中,所有的数据类型都会被检测一遍,以保证类型安全,避免崩溃问题。 329 | - **无侵入性**: 模型无需继承自其他基类。 330 | - **轻量**: 该框架只有 5 个文件 (包括.h文件)。 331 | - **文档和单元测试**: 文档覆盖率100%, 代码覆盖率99.6%。 332 | 333 | 使用方法 334 | ============== 335 | 336 | ###简单的 Model 与 JSON 相互转换 337 | 338 | // JSON: 339 | { 340 | "uid":123456, 341 | "name":"Harry", 342 | "created":"1965-07-31T00:00:00+0000" 343 | } 344 | 345 | // Model: 346 | @interface User : NSObject 347 | @property UInt64 uid; 348 | @property NSString *name; 349 | @property NSDate *created; 350 | @end 351 | @implementation User 352 | @end 353 | 354 | 355 | // 将 JSON (NSData,NSString,NSDictionary) 转换为 Model: 356 | User *user = [User yy_modelWithJSON:json]; 357 | 358 | // 将 Model 转换为 JSON 对象: 359 | NSDictionary *json = [user yy_modelToJSONObject]; 360 | 361 | 当 JSON/Dictionary 中的对象类型与 Model 属性不一致时,YYModel 将会进行如下自动转换。自动转换不支持的值将会被忽略,以避免各种潜在的崩溃问题。 362 | 363 | 364 | 365 | 366 | 367 | 368 | 369 | 370 | 371 | 372 | 373 | 374 | 375 | 376 | 377 | 378 | 379 | 380 | 382 | 383 | 384 | 385 | 392 | 393 | 394 | 395 | 397 | 398 | 399 | 400 | 401 | 402 | 403 | 404 | 405 | 406 | 407 | 408 | 409 | 410 | 411 | 412 | 413 | 414 | 415 |
JSON/DictionaryModel
NSStringNSNumber,NSURL,SEL,Class
NSNumberNSString
NSString/NSNumber基础类型 (BOOL,int,float,NSUInteger,UInt64,...)
381 | NaN 和 Inf 会被忽略
NSStringNSDate 以下列格式解析:
386 | yyyy-MM-dd
387 | yyyy-MM-dd HH:mm:ss
388 | yyyy-MM-dd'T'HH:mm:ss
389 | yyyy-MM-dd'T'HH:mm:ssZ
390 | EEE MMM dd HH:mm:ss Z yyyy 391 |
NSDateNSString 格式化为 ISO8601:
396 | "YYYY-MM-dd'T'HH:mm:ssZ"
NSValuestruct (CGRect,CGSize,...)
NSNullnil,0
"no","false",...@(NO),0
"yes","true",...@(YES),1
416 | 417 | 418 | ###Model 属性名和 JSON 中的 Key 不相同 419 | 420 | // JSON: 421 | { 422 | "n":"Harry Pottery", 423 | "p": 256, 424 | "ext" : { 425 | "desc" : "A book written by J.K.Rowing." 426 | }, 427 | "ID" : 100010 428 | } 429 | 430 | // Model: 431 | @interface Book : NSObject 432 | @property NSString *name; 433 | @property NSInteger page; 434 | @property NSString *desc; 435 | @property NSString *bookID; 436 | @end 437 | @implementation Book 438 | //返回一个 Dict,将 Model 属性名对映射到 JSON 的 Key。 439 | + (NSDictionary *)modelCustomPropertyMapper { 440 | return @{@"name" : @"n", 441 | @"page" : @"p", 442 | @"desc" : @"ext.desc", 443 | @"bookID" : @[@"id",@"ID",@"book_id"]}; 444 | } 445 | @end 446 | 447 | 你可以把一个或一组 json key (key path) 映射到一个或多个属性。如果一个属性没有映射关系,那默认会使用相同属性名作为映射。 448 | 449 | 在 json->model 的过程中:如果一个属性对应了多个 json key,那么转换过程会按顺序查找,并使用第一个不为空的值。 450 | 451 | 在 model->json 的过程中:如果一个属性对应了多个 json key (key path),那么转换过程仅会处理第一个 json key (key path);如果多个属性对应了同一个 json key,则转换过过程会使用其中任意一个不为空的值。 452 | 453 | ###Model 包含其他 Model 454 | 455 | // JSON 456 | { 457 | "author":{ 458 | "name":"J.K.Rowling", 459 | "birthday":"1965-07-31T00:00:00+0000" 460 | }, 461 | "name":"Harry Potter", 462 | "pages":256 463 | } 464 | 465 | // Model: 什么都不用做,转换会自动完成 466 | @interface Author : NSObject 467 | @property NSString *name; 468 | @property NSDate *birthday; 469 | @end 470 | @implementation Author 471 | @end 472 | 473 | @interface Book : NSObject 474 | @property NSString *name; 475 | @property NSUInteger pages; 476 | @property Author *author; //Book 包含 Author 属性 477 | @end 478 | @implementation Book 479 | @end 480 | 481 | 482 | 483 | ###容器类属性 484 | 485 | @class Shadow, Border, Attachment; 486 | 487 | @interface Attributes 488 | @property NSString *name; 489 | @property NSArray *shadows; //Array 490 | @property NSSet *borders; //Set 491 | @property NSMutableDictionary *attachments; //Dict 492 | @end 493 | 494 | @implementation Attributes 495 | // 返回容器类中的所需要存放的数据类型 (以 Class 或 Class Name 的形式)。 496 | + (NSDictionary *)modelContainerPropertyGenericClass { 497 | return @{@"shadows" : [Shadow class], 498 | @"borders" : Border.class, 499 | @"attachments" : @"Attachment" }; 500 | } 501 | @end 502 | 503 | 504 | 505 | 506 | ###黑名单与白名单 507 | 508 | @interface User 509 | @property NSString *name; 510 | @property NSUInteger age; 511 | @end 512 | 513 | @implementation Attributes 514 | // 如果实现了该方法,则处理过程中会忽略该列表内的所有属性 515 | + (NSArray *)modelPropertyBlacklist { 516 | return @[@"test1", @"test2"]; 517 | } 518 | // 如果实现了该方法,则处理过程中不会处理该列表外的属性。 519 | + (NSArray *)modelPropertyWhitelist { 520 | return @[@"name"]; 521 | } 522 | @end 523 | 524 | ###数据校验与自定义转换 525 | 526 | // JSON: 527 | { 528 | "name":"Harry", 529 | "timestamp" : 1445534567 530 | } 531 | 532 | // Model: 533 | @interface User 534 | @property NSString *name; 535 | @property NSDate *createdAt; 536 | @end 537 | 538 | @implementation User 539 | // 当 JSON 转为 Model 完成后,该方法会被调用。 540 | // 你可以在这里对数据进行校验,如果校验不通过,可以返回 NO,则该 Model 会被忽略。 541 | // 你也可以在这里做一些自动转换不能完成的工作。 542 | - (BOOL)modelCustomTransformFromDictionary:(NSDictionary *)dic { 543 | NSNumber *timestamp = dic[@"timestamp"]; 544 | if (![timestamp isKindOfClass:[NSNumber class]]) return NO; 545 | _createdAt = [NSDate dateWithTimeIntervalSince1970:timestamp.floatValue]; 546 | return YES; 547 | } 548 | 549 | // 当 Model 转为 JSON 完成后,该方法会被调用。 550 | // 你可以在这里对数据进行校验,如果校验不通过,可以返回 NO,则该 Model 会被忽略。 551 | // 你也可以在这里做一些自动转换不能完成的工作。 552 | - (BOOL)modelCustomTransformToDictionary:(NSMutableDictionary *)dic { 553 | if (!_createdAt) return NO; 554 | dic[@"timestamp"] = @(n.timeIntervalSince1970); 555 | return YES; 556 | } 557 | @end 558 | 559 | ###Coding/Copying/hash/equal/description 560 | 561 | @interface YYShadow :NSObject 562 | @property (nonatomic, copy) NSString *name; 563 | @property (nonatomic, assign) CGSize size; 564 | @end 565 | 566 | @implementation YYShadow 567 | // 直接添加以下代码即可自动完成 568 | - (void)encodeWithCoder:(NSCoder *)aCoder { [self yy_modelEncodeWithCoder:aCoder]; } 569 | - (id)initWithCoder:(NSCoder *)aDecoder { self = [super init]; return [self yy_modelInitWithCoder:aDecoder]; } 570 | - (id)copyWithZone:(NSZone *)zone { return [self yy_modelCopy]; } 571 | - (NSUInteger)hash { return [self yy_modelHash]; } 572 | - (BOOL)isEqual:(id)object { return [self yy_modelIsEqual:object]; } 573 | - (NSString *)description { return [self yy_modelDescription]; } 574 | @end 575 | 576 | 577 | 安装 578 | ============== 579 | 580 | ### CocoaPods 581 | 582 | 1. 在 Podfile 中添加 `pod 'YYModel'`。 583 | 2. 执行 `pod install` 或 `pod update`。 584 | 3. 导入 \。 585 | 586 | 587 | ### Carthage 588 | 589 | 1. 在 Cartfile 中添加 `github "ibireme/YYModel"`。 590 | 2. 执行 `carthage update --platform ios` 并将生成的 framework 添加到你的工程。 591 | 3. 导入 \。 592 | 593 | 594 | ### 手动安装 595 | 596 | 1. 下载 YYModel 文件夹内的所有内容。 597 | 2. 将 YYModel 内的源文件添加(拖放)到你的工程。 598 | 3. 导入 `YYModel.h`。 599 | 600 | 601 | 文档 602 | ============== 603 | 你可以在 [CocoaDocs](http://cocoadocs.org/docsets/YYModel/) 查看在线 API 文档,也可以用 [appledoc](https://github.com/tomaz/appledoc) 本地生成文档。 604 | 605 | 606 | 系统要求 607 | ============== 608 | 该项目最低支持 `iOS 6.0` 和 `Xcode 7.0`。 609 | 610 | 611 | 许可证 612 | ============== 613 | YYModel 使用 MIT 许可证,详情见 LICENSE 文件。 614 | 615 | 相关链接 616 | ============== 617 | 618 | [iOS JSON 模型转换库评测](http://blog.ibireme.com/2015/10/23/ios_model_framework_benchmark/) 619 | 620 | -------------------------------------------------------------------------------- /JFCitySelector/Example/JFCitySelector Demo/Pods/YYModel/YYModel/NSObject+YYModel.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSObject+YYModel.h 3 | // YYModel 4 | // 5 | // Created by ibireme on 15/5/10. 6 | // Copyright (c) 2015 ibireme. 7 | // 8 | // This source code is licensed under the MIT-style license found in the 9 | // LICENSE file in the root directory of this source tree. 10 | // 11 | 12 | #import 13 | 14 | NS_ASSUME_NONNULL_BEGIN 15 | 16 | /** 17 | Provide some data-model method: 18 | 19 | * Convert json to any object, or convert any object to json. 20 | * Set object properties with a key-value dictionary (like KVC). 21 | * Implementations of `NSCoding`, `NSCopying`, `-hash` and `-isEqual:`. 22 | 23 | See `YYModel` protocol for custom methods. 24 | 25 | 26 | Sample Code: 27 | 28 | ********************** json convertor ********************* 29 | @interface YYAuthor : NSObject 30 | @property (nonatomic, strong) NSString *name; 31 | @property (nonatomic, assign) NSDate *birthday; 32 | @end 33 | @implementation YYAuthor 34 | @end 35 | 36 | @interface YYBook : NSObject 37 | @property (nonatomic, copy) NSString *name; 38 | @property (nonatomic, assign) NSUInteger pages; 39 | @property (nonatomic, strong) YYAuthor *author; 40 | @end 41 | @implementation YYBook 42 | @end 43 | 44 | int main() { 45 | // create model from json 46 | YYBook *book = [YYBook yy_modelWithJSON:@"{\"name\": \"Harry Potter\", \"pages\": 256, \"author\": {\"name\": \"J.K.Rowling\", \"birthday\": \"1965-07-31\" }}"]; 47 | 48 | // convert model to json 49 | NSString *json = [book yy_modelToJSONString]; 50 | // {"author":{"name":"J.K.Rowling","birthday":"1965-07-31T00:00:00+0000"},"name":"Harry Potter","pages":256} 51 | } 52 | 53 | ********************** Coding/Copying/hash/equal ********************* 54 | @interface YYShadow :NSObject 55 | @property (nonatomic, copy) NSString *name; 56 | @property (nonatomic, assign) CGSize size; 57 | @end 58 | 59 | @implementation YYShadow 60 | - (void)encodeWithCoder:(NSCoder *)aCoder { [self yy_modelEncodeWithCoder:aCoder]; } 61 | - (id)initWithCoder:(NSCoder *)aDecoder { self = [super init]; return [self yy_modelInitWithCoder:aDecoder]; } 62 | - (id)copyWithZone:(NSZone *)zone { return [self yy_modelCopy]; } 63 | - (NSUInteger)hash { return [self yy_modelHash]; } 64 | - (BOOL)isEqual:(id)object { return [self yy_modelIsEqual:object]; } 65 | @end 66 | 67 | */ 68 | @interface NSObject (YYModel) 69 | 70 | /** 71 | Creates and returns a new instance of the receiver from a json. 72 | This method is thread-safe. 73 | 74 | @param json A json object in `NSDictionary`, `NSString` or `NSData`. 75 | 76 | @return A new instance created from the json, or nil if an error occurs. 77 | */ 78 | + (nullable instancetype)yy_modelWithJSON:(id)json; 79 | 80 | /** 81 | Creates and returns a new instance of the receiver from a key-value dictionary. 82 | This method is thread-safe. 83 | 84 | @param dictionary A key-value dictionary mapped to the instance's properties. 85 | Any invalid key-value pair in dictionary will be ignored. 86 | 87 | @return A new instance created from the dictionary, or nil if an error occurs. 88 | 89 | @discussion The key in `dictionary` will mapped to the reciever's property name, 90 | and the value will set to the property. If the value's type does not match the 91 | property, this method will try to convert the value based on these rules: 92 | 93 | `NSString` or `NSNumber` -> c number, such as BOOL, int, long, float, NSUInteger... 94 | `NSString` -> NSDate, parsed with format "yyyy-MM-dd'T'HH:mm:ssZ", "yyyy-MM-dd HH:mm:ss" or "yyyy-MM-dd". 95 | `NSString` -> NSURL. 96 | `NSValue` -> struct or union, such as CGRect, CGSize, ... 97 | `NSString` -> SEL, Class. 98 | */ 99 | + (nullable instancetype)yy_modelWithDictionary:(NSDictionary *)dictionary; 100 | 101 | /** 102 | Set the receiver's properties with a json object. 103 | 104 | @discussion Any invalid data in json will be ignored. 105 | 106 | @param json A json object of `NSDictionary`, `NSString` or `NSData`, mapped to the 107 | receiver's properties. 108 | 109 | @return Whether succeed. 110 | */ 111 | - (BOOL)yy_modelSetWithJSON:(id)json; 112 | 113 | /** 114 | Set the receiver's properties with a key-value dictionary. 115 | 116 | @param dic A key-value dictionary mapped to the receiver's properties. 117 | Any invalid key-value pair in dictionary will be ignored. 118 | 119 | @discussion The key in `dictionary` will mapped to the reciever's property name, 120 | and the value will set to the property. If the value's type doesn't match the 121 | property, this method will try to convert the value based on these rules: 122 | 123 | `NSString`, `NSNumber` -> c number, such as BOOL, int, long, float, NSUInteger... 124 | `NSString` -> NSDate, parsed with format "yyyy-MM-dd'T'HH:mm:ssZ", "yyyy-MM-dd HH:mm:ss" or "yyyy-MM-dd". 125 | `NSString` -> NSURL. 126 | `NSValue` -> struct or union, such as CGRect, CGSize, ... 127 | `NSString` -> SEL, Class. 128 | 129 | @return Whether succeed. 130 | */ 131 | - (BOOL)yy_modelSetWithDictionary:(NSDictionary *)dic; 132 | 133 | /** 134 | Generate a json object from the receiver's properties. 135 | 136 | @return A json object in `NSDictionary` or `NSArray`, or nil if an error occurs. 137 | See [NSJSONSerialization isValidJSONObject] for more information. 138 | 139 | @discussion Any of the invalid property is ignored. 140 | If the reciver is `NSArray`, `NSDictionary` or `NSSet`, it just convert 141 | the inner object to json object. 142 | */ 143 | - (nullable id)yy_modelToJSONObject; 144 | 145 | /** 146 | Generate a json string's data from the receiver's properties. 147 | 148 | @return A json string's data, or nil if an error occurs. 149 | 150 | @discussion Any of the invalid property is ignored. 151 | If the reciver is `NSArray`, `NSDictionary` or `NSSet`, it will also convert the 152 | inner object to json string. 153 | */ 154 | - (nullable NSData *)yy_modelToJSONData; 155 | 156 | /** 157 | Generate a json string from the receiver's properties. 158 | 159 | @return A json string, or nil if an error occurs. 160 | 161 | @discussion Any of the invalid property is ignored. 162 | If the reciver is `NSArray`, `NSDictionary` or `NSSet`, it will also convert the 163 | inner object to json string. 164 | */ 165 | - (nullable NSString *)yy_modelToJSONString; 166 | 167 | /** 168 | Copy a instance with the receiver's properties. 169 | 170 | @return A copied instance, or nil if an error occurs. 171 | */ 172 | - (nullable id)yy_modelCopy; 173 | 174 | /** 175 | Encode the receiver's properties to a coder. 176 | 177 | @param aCoder An archiver object. 178 | */ 179 | - (void)yy_modelEncodeWithCoder:(NSCoder *)aCoder; 180 | 181 | /** 182 | Decode the receiver's properties from a decoder. 183 | 184 | @param aDecoder An archiver object. 185 | 186 | @return self 187 | */ 188 | - (id)yy_modelInitWithCoder:(NSCoder *)aDecoder; 189 | 190 | /** 191 | Get a hash code with the receiver's properties. 192 | 193 | @return Hash code. 194 | */ 195 | - (NSUInteger)yy_modelHash; 196 | 197 | /** 198 | Compares the receiver with another object for equality, based on properties. 199 | 200 | @param model Another object. 201 | 202 | @return `YES` if the reciever is equal to the object, otherwise `NO`. 203 | */ 204 | - (BOOL)yy_modelIsEqual:(id)model; 205 | 206 | /** 207 | Description method for debugging purposes based on properties. 208 | 209 | @return A string that describes the contents of the receiver. 210 | */ 211 | - (NSString *)yy_modelDescription; 212 | 213 | @end 214 | 215 | 216 | 217 | /** 218 | Provide some data-model method for NSArray. 219 | */ 220 | @interface NSArray (YYModel) 221 | 222 | /** 223 | Creates and returns an array from a json-array. 224 | This method is thread-safe. 225 | 226 | @param cls The instance's class in array. 227 | @param json A json array of `NSArray`, `NSString` or `NSData`. 228 | Example: [{"name","Mary"},{name:"Joe"}] 229 | 230 | @return A array, or nil if an error occurs. 231 | */ 232 | + (nullable NSArray *)yy_modelArrayWithClass:(Class)cls json:(id)json; 233 | 234 | @end 235 | 236 | 237 | 238 | /** 239 | Provide some data-model method for NSDictionary. 240 | */ 241 | @interface NSDictionary (YYModel) 242 | 243 | /** 244 | Creates and returns a dictionary from a json. 245 | This method is thread-safe. 246 | 247 | @param cls The value instance's class in dictionary. 248 | @param json A json dictionary of `NSDictionary`, `NSString` or `NSData`. 249 | Example: {"user1":{"name","Mary"}, "user2": {name:"Joe"}} 250 | 251 | @return A dictionary, or nil if an error occurs. 252 | */ 253 | + (nullable NSDictionary *)yy_modelDictionaryWithClass:(Class)cls json:(id)json; 254 | @end 255 | 256 | 257 | 258 | /** 259 | If the default model transform does not fit to your model class, implement one or 260 | more method in this protocol to change the default key-value transform process. 261 | There's no need to add '' to your class header. 262 | */ 263 | @protocol YYModel 264 | @optional 265 | 266 | /** 267 | Custom property mapper. 268 | 269 | @discussion If the key in JSON/Dictionary does not match to the model's property name, 270 | implements this method and returns the additional mapper. 271 | 272 | Example: 273 | 274 | json: 275 | { 276 | "n":"Harry Pottery", 277 | "p": 256, 278 | "ext" : { 279 | "desc" : "A book written by J.K.Rowling." 280 | }, 281 | "ID" : 100010 282 | } 283 | 284 | model: 285 | @interface YYBook : NSObject 286 | @property NSString *name; 287 | @property NSInteger page; 288 | @property NSString *desc; 289 | @property NSString *bookID; 290 | @end 291 | 292 | @implementation YYBook 293 | + (NSDictionary *)modelCustomPropertyMapper { 294 | return @{@"name" : @"n", 295 | @"page" : @"p", 296 | @"desc" : @"ext.desc", 297 | @"bookID": @[@"id", @"ID", @"book_id"]}; 298 | } 299 | @end 300 | 301 | @return A custom mapper for properties. 302 | */ 303 | + (nullable NSDictionary *)modelCustomPropertyMapper; 304 | 305 | /** 306 | The generic class mapper for container properties. 307 | 308 | @discussion If the property is a container object, such as NSArray/NSSet/NSDictionary, 309 | implements this method and returns a property->class mapper, tells which kind of 310 | object will be add to the array/set/dictionary. 311 | 312 | Example: 313 | @class YYShadow, YYBorder, YYAttachment; 314 | 315 | @interface YYAttributes 316 | @property NSString *name; 317 | @property NSArray *shadows; 318 | @property NSSet *borders; 319 | @property NSDictionary *attachments; 320 | @end 321 | 322 | @implementation YYAttributes 323 | + (NSDictionary *)modelContainerPropertyGenericClass { 324 | return @{@"shadows" : [YYShadow class], 325 | @"borders" : YYBorder.class, 326 | @"attachments" : @"YYAttachment" }; 327 | } 328 | @end 329 | 330 | @return A class mapper. 331 | */ 332 | + (nullable NSDictionary *)modelContainerPropertyGenericClass; 333 | 334 | /** 335 | If you need to create instances of different classes during json->object transform, 336 | use the method to choose custom class based on dictionary data. 337 | 338 | @discussion If the model implements this method, it will be called to determine resulting class 339 | during `+modelWithJSON:`, `+modelWithDictionary:`, conveting object of properties of parent objects 340 | (both singular and containers via `+modelContainerPropertyGenericClass`). 341 | 342 | Example: 343 | @class YYCircle, YYRectangle, YYLine; 344 | 345 | @implementation YYShape 346 | 347 | + (Class)modelCustomClassForDictionary:(NSDictionary*)dictionary { 348 | if (dictionary[@"radius"] != nil) { 349 | return [YYCircle class]; 350 | } else if (dictionary[@"width"] != nil) { 351 | return [YYRectangle class]; 352 | } else if (dictionary[@"y2"] != nil) { 353 | return [YYLine class]; 354 | } else { 355 | return [self class]; 356 | } 357 | } 358 | 359 | @end 360 | 361 | @param dictionary The json/kv dictionary. 362 | 363 | @return Class to create from this dictionary, `nil` to use current class. 364 | 365 | */ 366 | + (nullable Class)modelCustomClassForDictionary:(NSDictionary *)dictionary; 367 | 368 | /** 369 | All the properties in blacklist will be ignored in model transform process. 370 | Returns nil to ignore this feature. 371 | 372 | @return An array of property's name. 373 | */ 374 | + (nullable NSArray *)modelPropertyBlacklist; 375 | 376 | /** 377 | If a property is not in the whitelist, it will be ignored in model transform process. 378 | Returns nil to ignore this feature. 379 | 380 | @return An array of property's name. 381 | */ 382 | + (nullable NSArray *)modelPropertyWhitelist; 383 | 384 | /** 385 | This method's behavior is similar to `- (BOOL)modelCustomTransformFromDictionary:(NSDictionary *)dic;`, 386 | but be called before the model transform. 387 | 388 | @discussion If the model implements this method, it will be called before 389 | `+modelWithJSON:`, `+modelWithDictionary:`, `-modelSetWithJSON:` and `-modelSetWithDictionary:`. 390 | If this method returns nil, the transform process will ignore this model. 391 | 392 | @param dic The json/kv dictionary. 393 | 394 | @return Returns the modified dictionary, or nil to ignore this model. 395 | */ 396 | - (NSDictionary *)modelCustomWillTransformFromDictionary:(NSDictionary *)dic; 397 | 398 | /** 399 | If the default json-to-model transform does not fit to your model object, implement 400 | this method to do additional process. You can also use this method to validate the 401 | model's properties. 402 | 403 | @discussion If the model implements this method, it will be called at the end of 404 | `+modelWithJSON:`, `+modelWithDictionary:`, `-modelSetWithJSON:` and `-modelSetWithDictionary:`. 405 | If this method returns NO, the transform process will ignore this model. 406 | 407 | @param dic The json/kv dictionary. 408 | 409 | @return Returns YES if the model is valid, or NO to ignore this model. 410 | */ 411 | - (BOOL)modelCustomTransformFromDictionary:(NSDictionary *)dic; 412 | 413 | /** 414 | If the default model-to-json transform does not fit to your model class, implement 415 | this method to do additional process. You can also use this method to validate the 416 | json dictionary. 417 | 418 | @discussion If the model implements this method, it will be called at the end of 419 | `-modelToJSONObject` and `-modelToJSONString`. 420 | If this method returns NO, the transform process will ignore this json dictionary. 421 | 422 | @param dic The json dictionary. 423 | 424 | @return Returns YES if the model is valid, or NO to ignore this model. 425 | */ 426 | - (BOOL)modelCustomTransformToDictionary:(NSMutableDictionary *)dic; 427 | 428 | @end 429 | 430 | NS_ASSUME_NONNULL_END 431 | -------------------------------------------------------------------------------- /JFCitySelector/Example/JFCitySelector Demo/Pods/YYModel/YYModel/YYClassInfo.h: -------------------------------------------------------------------------------- 1 | // 2 | // YYClassInfo.h 3 | // YYModel 4 | // 5 | // Created by ibireme on 15/5/9. 6 | // Copyright (c) 2015 ibireme. 7 | // 8 | // This source code is licensed under the MIT-style license found in the 9 | // LICENSE file in the root directory of this source tree. 10 | // 11 | 12 | #import 13 | #import 14 | 15 | NS_ASSUME_NONNULL_BEGIN 16 | 17 | /** 18 | Type encoding's type. 19 | */ 20 | typedef NS_OPTIONS(NSUInteger, YYEncodingType) { 21 | YYEncodingTypeMask = 0xFF, ///< mask of type value 22 | YYEncodingTypeUnknown = 0, ///< unknown 23 | YYEncodingTypeVoid = 1, ///< void 24 | YYEncodingTypeBool = 2, ///< bool 25 | YYEncodingTypeInt8 = 3, ///< char / BOOL 26 | YYEncodingTypeUInt8 = 4, ///< unsigned char 27 | YYEncodingTypeInt16 = 5, ///< short 28 | YYEncodingTypeUInt16 = 6, ///< unsigned short 29 | YYEncodingTypeInt32 = 7, ///< int 30 | YYEncodingTypeUInt32 = 8, ///< unsigned int 31 | YYEncodingTypeInt64 = 9, ///< long long 32 | YYEncodingTypeUInt64 = 10, ///< unsigned long long 33 | YYEncodingTypeFloat = 11, ///< float 34 | YYEncodingTypeDouble = 12, ///< double 35 | YYEncodingTypeLongDouble = 13, ///< long double 36 | YYEncodingTypeObject = 14, ///< id 37 | YYEncodingTypeClass = 15, ///< Class 38 | YYEncodingTypeSEL = 16, ///< SEL 39 | YYEncodingTypeBlock = 17, ///< block 40 | YYEncodingTypePointer = 18, ///< void* 41 | YYEncodingTypeStruct = 19, ///< struct 42 | YYEncodingTypeUnion = 20, ///< union 43 | YYEncodingTypeCString = 21, ///< char* 44 | YYEncodingTypeCArray = 22, ///< char[10] (for example) 45 | 46 | YYEncodingTypeQualifierMask = 0xFF00, ///< mask of qualifier 47 | YYEncodingTypeQualifierConst = 1 << 8, ///< const 48 | YYEncodingTypeQualifierIn = 1 << 9, ///< in 49 | YYEncodingTypeQualifierInout = 1 << 10, ///< inout 50 | YYEncodingTypeQualifierOut = 1 << 11, ///< out 51 | YYEncodingTypeQualifierBycopy = 1 << 12, ///< bycopy 52 | YYEncodingTypeQualifierByref = 1 << 13, ///< byref 53 | YYEncodingTypeQualifierOneway = 1 << 14, ///< oneway 54 | 55 | YYEncodingTypePropertyMask = 0xFF0000, ///< mask of property 56 | YYEncodingTypePropertyReadonly = 1 << 16, ///< readonly 57 | YYEncodingTypePropertyCopy = 1 << 17, ///< copy 58 | YYEncodingTypePropertyRetain = 1 << 18, ///< retain 59 | YYEncodingTypePropertyNonatomic = 1 << 19, ///< nonatomic 60 | YYEncodingTypePropertyWeak = 1 << 20, ///< weak 61 | YYEncodingTypePropertyCustomGetter = 1 << 21, ///< getter= 62 | YYEncodingTypePropertyCustomSetter = 1 << 22, ///< setter= 63 | YYEncodingTypePropertyDynamic = 1 << 23, ///< @dynamic 64 | }; 65 | 66 | /** 67 | Get the type from a Type-Encoding string. 68 | 69 | @discussion See also: 70 | https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtTypeEncodings.html 71 | https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtPropertyIntrospection.html 72 | 73 | @param typeEncoding A Type-Encoding string. 74 | @return The encoding type. 75 | */ 76 | YYEncodingType YYEncodingGetType(const char *typeEncoding); 77 | 78 | 79 | /** 80 | Instance variable information. 81 | */ 82 | @interface YYClassIvarInfo : NSObject 83 | @property (nonatomic, assign, readonly) Ivar ivar; ///< ivar opaque struct 84 | @property (nonatomic, strong, readonly) NSString *name; ///< Ivar's name 85 | @property (nonatomic, assign, readonly) ptrdiff_t offset; ///< Ivar's offset 86 | @property (nonatomic, strong, readonly) NSString *typeEncoding; ///< Ivar's type encoding 87 | @property (nonatomic, assign, readonly) YYEncodingType type; ///< Ivar's type 88 | 89 | /** 90 | Creates and returns an ivar info object. 91 | 92 | @param ivar ivar opaque struct 93 | @return A new object, or nil if an error occurs. 94 | */ 95 | - (instancetype)initWithIvar:(Ivar)ivar; 96 | @end 97 | 98 | 99 | /** 100 | Method information. 101 | */ 102 | @interface YYClassMethodInfo : NSObject 103 | @property (nonatomic, assign, readonly) Method method; ///< method opaque struct 104 | @property (nonatomic, strong, readonly) NSString *name; ///< method name 105 | @property (nonatomic, assign, readonly) SEL sel; ///< method's selector 106 | @property (nonatomic, assign, readonly) IMP imp; ///< method's implementation 107 | @property (nonatomic, strong, readonly) NSString *typeEncoding; ///< method's parameter and return types 108 | @property (nonatomic, strong, readonly) NSString *returnTypeEncoding; ///< return value's type 109 | @property (nullable, nonatomic, strong, readonly) NSArray *argumentTypeEncodings; ///< array of arguments' type 110 | 111 | /** 112 | Creates and returns a method info object. 113 | 114 | @param method method opaque struct 115 | @return A new object, or nil if an error occurs. 116 | */ 117 | - (instancetype)initWithMethod:(Method)method; 118 | @end 119 | 120 | 121 | /** 122 | Property information. 123 | */ 124 | @interface YYClassPropertyInfo : NSObject 125 | @property (nonatomic, assign, readonly) objc_property_t property; ///< property's opaque struct 126 | @property (nonatomic, strong, readonly) NSString *name; ///< property's name 127 | @property (nonatomic, assign, readonly) YYEncodingType type; ///< property's type 128 | @property (nonatomic, strong, readonly) NSString *typeEncoding; ///< property's encoding value 129 | @property (nonatomic, strong, readonly) NSString *ivarName; ///< property's ivar name 130 | @property (nullable, nonatomic, assign, readonly) Class cls; ///< may be nil 131 | @property (nullable, nonatomic, strong, readonly) NSArray *protocols; ///< may nil 132 | @property (nonatomic, assign, readonly) SEL getter; ///< getter (nonnull) 133 | @property (nonatomic, assign, readonly) SEL setter; ///< setter (nonnull) 134 | 135 | /** 136 | Creates and returns a property info object. 137 | 138 | @param property property opaque struct 139 | @return A new object, or nil if an error occurs. 140 | */ 141 | - (instancetype)initWithProperty:(objc_property_t)property; 142 | @end 143 | 144 | 145 | /** 146 | Class information for a class. 147 | */ 148 | @interface YYClassInfo : NSObject 149 | @property (nonatomic, assign, readonly) Class cls; ///< class object 150 | @property (nullable, nonatomic, assign, readonly) Class superCls; ///< super class object 151 | @property (nullable, nonatomic, assign, readonly) Class metaCls; ///< class's meta class object 152 | @property (nonatomic, readonly) BOOL isMeta; ///< whether this class is meta class 153 | @property (nonatomic, strong, readonly) NSString *name; ///< class name 154 | @property (nullable, nonatomic, strong, readonly) YYClassInfo *superClassInfo; ///< super class's class info 155 | @property (nullable, nonatomic, strong, readonly) NSDictionary *ivarInfos; ///< ivars 156 | @property (nullable, nonatomic, strong, readonly) NSDictionary *methodInfos; ///< methods 157 | @property (nullable, nonatomic, strong, readonly) NSDictionary *propertyInfos; ///< properties 158 | 159 | /** 160 | If the class is changed (for example: you add a method to this class with 161 | 'class_addMethod()'), you should call this method to refresh the class info cache. 162 | 163 | After called this method, `needUpdate` will returns `YES`, and you should call 164 | 'classInfoWithClass' or 'classInfoWithClassName' to get the updated class info. 165 | */ 166 | - (void)setNeedUpdate; 167 | 168 | /** 169 | If this method returns `YES`, you should stop using this instance and call 170 | `classInfoWithClass` or `classInfoWithClassName` to get the updated class info. 171 | 172 | @return Whether this class info need update. 173 | */ 174 | - (BOOL)needUpdate; 175 | 176 | /** 177 | Get the class info of a specified Class. 178 | 179 | @discussion This method will cache the class info and super-class info 180 | at the first access to the Class. This method is thread-safe. 181 | 182 | @param cls A class. 183 | @return A class info, or nil if an error occurs. 184 | */ 185 | + (nullable instancetype)classInfoWithClass:(Class)cls; 186 | 187 | /** 188 | Get the class info of a specified Class. 189 | 190 | @discussion This method will cache the class info and super-class info 191 | at the first access to the Class. This method is thread-safe. 192 | 193 | @param className A class name. 194 | @return A class info, or nil if an error occurs. 195 | */ 196 | + (nullable instancetype)classInfoWithClassName:(NSString *)className; 197 | 198 | @end 199 | 200 | NS_ASSUME_NONNULL_END 201 | -------------------------------------------------------------------------------- /JFCitySelector/Example/JFCitySelector Demo/Pods/YYModel/YYModel/YYClassInfo.m: -------------------------------------------------------------------------------- 1 | // 2 | // YYClassInfo.m 3 | // YYModel 4 | // 5 | // Created by ibireme on 15/5/9. 6 | // Copyright (c) 2015 ibireme. 7 | // 8 | // This source code is licensed under the MIT-style license found in the 9 | // LICENSE file in the root directory of this source tree. 10 | // 11 | 12 | #import "YYClassInfo.h" 13 | #import 14 | 15 | YYEncodingType YYEncodingGetType(const char *typeEncoding) { 16 | char *type = (char *)typeEncoding; 17 | if (!type) return YYEncodingTypeUnknown; 18 | size_t len = strlen(type); 19 | if (len == 0) return YYEncodingTypeUnknown; 20 | 21 | YYEncodingType qualifier = 0; 22 | bool prefix = true; 23 | while (prefix) { 24 | switch (*type) { 25 | case 'r': { 26 | qualifier |= YYEncodingTypeQualifierConst; 27 | type++; 28 | } break; 29 | case 'n': { 30 | qualifier |= YYEncodingTypeQualifierIn; 31 | type++; 32 | } break; 33 | case 'N': { 34 | qualifier |= YYEncodingTypeQualifierInout; 35 | type++; 36 | } break; 37 | case 'o': { 38 | qualifier |= YYEncodingTypeQualifierOut; 39 | type++; 40 | } break; 41 | case 'O': { 42 | qualifier |= YYEncodingTypeQualifierBycopy; 43 | type++; 44 | } break; 45 | case 'R': { 46 | qualifier |= YYEncodingTypeQualifierByref; 47 | type++; 48 | } break; 49 | case 'V': { 50 | qualifier |= YYEncodingTypeQualifierOneway; 51 | type++; 52 | } break; 53 | default: { prefix = false; } break; 54 | } 55 | } 56 | 57 | len = strlen(type); 58 | if (len == 0) return YYEncodingTypeUnknown | qualifier; 59 | 60 | switch (*type) { 61 | case 'v': return YYEncodingTypeVoid | qualifier; 62 | case 'B': return YYEncodingTypeBool | qualifier; 63 | case 'c': return YYEncodingTypeInt8 | qualifier; 64 | case 'C': return YYEncodingTypeUInt8 | qualifier; 65 | case 's': return YYEncodingTypeInt16 | qualifier; 66 | case 'S': return YYEncodingTypeUInt16 | qualifier; 67 | case 'i': return YYEncodingTypeInt32 | qualifier; 68 | case 'I': return YYEncodingTypeUInt32 | qualifier; 69 | case 'l': return YYEncodingTypeInt32 | qualifier; 70 | case 'L': return YYEncodingTypeUInt32 | qualifier; 71 | case 'q': return YYEncodingTypeInt64 | qualifier; 72 | case 'Q': return YYEncodingTypeUInt64 | qualifier; 73 | case 'f': return YYEncodingTypeFloat | qualifier; 74 | case 'd': return YYEncodingTypeDouble | qualifier; 75 | case 'D': return YYEncodingTypeLongDouble | qualifier; 76 | case '#': return YYEncodingTypeClass | qualifier; 77 | case ':': return YYEncodingTypeSEL | qualifier; 78 | case '*': return YYEncodingTypeCString | qualifier; 79 | case '^': return YYEncodingTypePointer | qualifier; 80 | case '[': return YYEncodingTypeCArray | qualifier; 81 | case '(': return YYEncodingTypeUnion | qualifier; 82 | case '{': return YYEncodingTypeStruct | qualifier; 83 | case '@': { 84 | if (len == 2 && *(type + 1) == '?') 85 | return YYEncodingTypeBlock | qualifier; 86 | else 87 | return YYEncodingTypeObject | qualifier; 88 | } 89 | default: return YYEncodingTypeUnknown | qualifier; 90 | } 91 | } 92 | 93 | @implementation YYClassIvarInfo 94 | 95 | - (instancetype)initWithIvar:(Ivar)ivar { 96 | if (!ivar) return nil; 97 | self = [super init]; 98 | _ivar = ivar; 99 | const char *name = ivar_getName(ivar); 100 | if (name) { 101 | _name = [NSString stringWithUTF8String:name]; 102 | } 103 | _offset = ivar_getOffset(ivar); 104 | const char *typeEncoding = ivar_getTypeEncoding(ivar); 105 | if (typeEncoding) { 106 | _typeEncoding = [NSString stringWithUTF8String:typeEncoding]; 107 | _type = YYEncodingGetType(typeEncoding); 108 | } 109 | return self; 110 | } 111 | 112 | @end 113 | 114 | @implementation YYClassMethodInfo 115 | 116 | - (instancetype)initWithMethod:(Method)method { 117 | if (!method) return nil; 118 | self = [super init]; 119 | _method = method; 120 | _sel = method_getName(method); 121 | _imp = method_getImplementation(method); 122 | const char *name = sel_getName(_sel); 123 | if (name) { 124 | _name = [NSString stringWithUTF8String:name]; 125 | } 126 | const char *typeEncoding = method_getTypeEncoding(method); 127 | if (typeEncoding) { 128 | _typeEncoding = [NSString stringWithUTF8String:typeEncoding]; 129 | } 130 | char *returnType = method_copyReturnType(method); 131 | if (returnType) { 132 | _returnTypeEncoding = [NSString stringWithUTF8String:returnType]; 133 | free(returnType); 134 | } 135 | unsigned int argumentCount = method_getNumberOfArguments(method); 136 | if (argumentCount > 0) { 137 | NSMutableArray *argumentTypes = [NSMutableArray new]; 138 | for (unsigned int i = 0; i < argumentCount; i++) { 139 | char *argumentType = method_copyArgumentType(method, i); 140 | NSString *type = argumentType ? [NSString stringWithUTF8String:argumentType] : nil; 141 | [argumentTypes addObject:type ? type : @""]; 142 | if (argumentType) free(argumentType); 143 | } 144 | _argumentTypeEncodings = argumentTypes; 145 | } 146 | return self; 147 | } 148 | 149 | @end 150 | 151 | @implementation YYClassPropertyInfo 152 | 153 | - (instancetype)initWithProperty:(objc_property_t)property { 154 | if (!property) return nil; 155 | self = [super init]; 156 | _property = property; 157 | const char *name = property_getName(property); 158 | if (name) { 159 | _name = [NSString stringWithUTF8String:name]; 160 | } 161 | 162 | YYEncodingType type = 0; 163 | unsigned int attrCount; 164 | objc_property_attribute_t *attrs = property_copyAttributeList(property, &attrCount); 165 | for (unsigned int i = 0; i < attrCount; i++) { 166 | switch (attrs[i].name[0]) { 167 | case 'T': { // Type encoding 168 | if (attrs[i].value) { 169 | _typeEncoding = [NSString stringWithUTF8String:attrs[i].value]; 170 | type = YYEncodingGetType(attrs[i].value); 171 | 172 | if ((type & YYEncodingTypeMask) == YYEncodingTypeObject && _typeEncoding.length) { 173 | NSScanner *scanner = [NSScanner scannerWithString:_typeEncoding]; 174 | if (![scanner scanString:@"@\"" intoString:NULL]) continue; 175 | 176 | NSString *clsName = nil; 177 | if ([scanner scanUpToCharactersFromSet: [NSCharacterSet characterSetWithCharactersInString:@"\"<"] intoString:&clsName]) { 178 | if (clsName.length) _cls = objc_getClass(clsName.UTF8String); 179 | } 180 | 181 | NSMutableArray *protocols = nil; 182 | while ([scanner scanString:@"<" intoString:NULL]) { 183 | NSString* protocol = nil; 184 | if ([scanner scanUpToString:@">" intoString: &protocol]) { 185 | if (protocol.length) { 186 | if (!protocols) protocols = [NSMutableArray new]; 187 | [protocols addObject:protocol]; 188 | } 189 | } 190 | [scanner scanString:@">" intoString:NULL]; 191 | } 192 | _protocols = protocols; 193 | } 194 | } 195 | } break; 196 | case 'V': { // Instance variable 197 | if (attrs[i].value) { 198 | _ivarName = [NSString stringWithUTF8String:attrs[i].value]; 199 | } 200 | } break; 201 | case 'R': { 202 | type |= YYEncodingTypePropertyReadonly; 203 | } break; 204 | case 'C': { 205 | type |= YYEncodingTypePropertyCopy; 206 | } break; 207 | case '&': { 208 | type |= YYEncodingTypePropertyRetain; 209 | } break; 210 | case 'N': { 211 | type |= YYEncodingTypePropertyNonatomic; 212 | } break; 213 | case 'D': { 214 | type |= YYEncodingTypePropertyDynamic; 215 | } break; 216 | case 'W': { 217 | type |= YYEncodingTypePropertyWeak; 218 | } break; 219 | case 'G': { 220 | type |= YYEncodingTypePropertyCustomGetter; 221 | if (attrs[i].value) { 222 | _getter = NSSelectorFromString([NSString stringWithUTF8String:attrs[i].value]); 223 | } 224 | } break; 225 | case 'S': { 226 | type |= YYEncodingTypePropertyCustomSetter; 227 | if (attrs[i].value) { 228 | _setter = NSSelectorFromString([NSString stringWithUTF8String:attrs[i].value]); 229 | } 230 | } // break; commented for code coverage in next line 231 | default: break; 232 | } 233 | } 234 | if (attrs) { 235 | free(attrs); 236 | attrs = NULL; 237 | } 238 | 239 | _type = type; 240 | if (_name.length) { 241 | if (!_getter) { 242 | _getter = NSSelectorFromString(_name); 243 | } 244 | if (!_setter) { 245 | _setter = NSSelectorFromString([NSString stringWithFormat:@"set%@%@:", [_name substringToIndex:1].uppercaseString, [_name substringFromIndex:1]]); 246 | } 247 | } 248 | return self; 249 | } 250 | 251 | @end 252 | 253 | @implementation YYClassInfo { 254 | BOOL _needUpdate; 255 | } 256 | 257 | - (instancetype)initWithClass:(Class)cls { 258 | if (!cls) return nil; 259 | self = [super init]; 260 | _cls = cls; 261 | _superCls = class_getSuperclass(cls); 262 | _isMeta = class_isMetaClass(cls); 263 | if (!_isMeta) { 264 | _metaCls = objc_getMetaClass(class_getName(cls)); 265 | } 266 | _name = NSStringFromClass(cls); 267 | [self _update]; 268 | 269 | _superClassInfo = [self.class classInfoWithClass:_superCls]; 270 | return self; 271 | } 272 | 273 | - (void)_update { 274 | _ivarInfos = nil; 275 | _methodInfos = nil; 276 | _propertyInfos = nil; 277 | 278 | Class cls = self.cls; 279 | unsigned int methodCount = 0; 280 | Method *methods = class_copyMethodList(cls, &methodCount); 281 | if (methods) { 282 | NSMutableDictionary *methodInfos = [NSMutableDictionary new]; 283 | _methodInfos = methodInfos; 284 | for (unsigned int i = 0; i < methodCount; i++) { 285 | YYClassMethodInfo *info = [[YYClassMethodInfo alloc] initWithMethod:methods[i]]; 286 | if (info.name) methodInfos[info.name] = info; 287 | } 288 | free(methods); 289 | } 290 | unsigned int propertyCount = 0; 291 | objc_property_t *properties = class_copyPropertyList(cls, &propertyCount); 292 | if (properties) { 293 | NSMutableDictionary *propertyInfos = [NSMutableDictionary new]; 294 | _propertyInfos = propertyInfos; 295 | for (unsigned int i = 0; i < propertyCount; i++) { 296 | YYClassPropertyInfo *info = [[YYClassPropertyInfo alloc] initWithProperty:properties[i]]; 297 | if (info.name) propertyInfos[info.name] = info; 298 | } 299 | free(properties); 300 | } 301 | 302 | unsigned int ivarCount = 0; 303 | Ivar *ivars = class_copyIvarList(cls, &ivarCount); 304 | if (ivars) { 305 | NSMutableDictionary *ivarInfos = [NSMutableDictionary new]; 306 | _ivarInfos = ivarInfos; 307 | for (unsigned int i = 0; i < ivarCount; i++) { 308 | YYClassIvarInfo *info = [[YYClassIvarInfo alloc] initWithIvar:ivars[i]]; 309 | if (info.name) ivarInfos[info.name] = info; 310 | } 311 | free(ivars); 312 | } 313 | 314 | if (!_ivarInfos) _ivarInfos = @{}; 315 | if (!_methodInfos) _methodInfos = @{}; 316 | if (!_propertyInfos) _propertyInfos = @{}; 317 | 318 | _needUpdate = NO; 319 | } 320 | 321 | - (void)setNeedUpdate { 322 | _needUpdate = YES; 323 | } 324 | 325 | - (BOOL)needUpdate { 326 | return _needUpdate; 327 | } 328 | 329 | + (instancetype)classInfoWithClass:(Class)cls { 330 | if (!cls) return nil; 331 | static CFMutableDictionaryRef classCache; 332 | static CFMutableDictionaryRef metaCache; 333 | static dispatch_once_t onceToken; 334 | static dispatch_semaphore_t lock; 335 | dispatch_once(&onceToken, ^{ 336 | classCache = CFDictionaryCreateMutable(CFAllocatorGetDefault(), 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 337 | metaCache = CFDictionaryCreateMutable(CFAllocatorGetDefault(), 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 338 | lock = dispatch_semaphore_create(1); 339 | }); 340 | dispatch_semaphore_wait(lock, DISPATCH_TIME_FOREVER); 341 | YYClassInfo *info = CFDictionaryGetValue(class_isMetaClass(cls) ? metaCache : classCache, (__bridge const void *)(cls)); 342 | if (info && info->_needUpdate) { 343 | [info _update]; 344 | } 345 | dispatch_semaphore_signal(lock); 346 | if (!info) { 347 | info = [[YYClassInfo alloc] initWithClass:cls]; 348 | if (info) { 349 | dispatch_semaphore_wait(lock, DISPATCH_TIME_FOREVER); 350 | CFDictionarySetValue(info.isMeta ? metaCache : classCache, (__bridge const void *)(cls), (__bridge const void *)(info)); 351 | dispatch_semaphore_signal(lock); 352 | } 353 | } 354 | return info; 355 | } 356 | 357 | + (instancetype)classInfoWithClassName:(NSString *)className { 358 | Class cls = NSClassFromString(className); 359 | return [self classInfoWithClass:cls]; 360 | } 361 | 362 | @end 363 | -------------------------------------------------------------------------------- /JFCitySelector/Example/JFCitySelector Demo/Pods/YYModel/YYModel/YYModel.h: -------------------------------------------------------------------------------- 1 | // 2 | // YYModel.h 3 | // YYModel 4 | // 5 | // Created by ibireme on 15/5/10. 6 | // Copyright (c) 2015 ibireme. 7 | // 8 | // This source code is licensed under the MIT-style license found in the 9 | // LICENSE file in the root directory of this source tree. 10 | // 11 | 12 | #import 13 | 14 | #if __has_include() 15 | FOUNDATION_EXPORT double YYModelVersionNumber; 16 | FOUNDATION_EXPORT const unsigned char YYModelVersionString[]; 17 | #import 18 | #import 19 | #else 20 | #import "NSObject+YYModel.h" 21 | #import "YYClassInfo.h" 22 | #endif 23 | -------------------------------------------------------------------------------- /JFCitySelector/Resources/JFCitySelector.bundle/jf_icon_down@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhifenx/JFCitySelector/ccc1bb113959df9066d5f639a1ed807323f62d74/JFCitySelector/Resources/JFCitySelector.bundle/jf_icon_down@3x.png -------------------------------------------------------------------------------- /JFCitySelector/Resources/JFCitySelector.bundle/jf_icon_navi_return@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhifenx/JFCitySelector/ccc1bb113959df9066d5f639a1ed807323f62d74/JFCitySelector/Resources/JFCitySelector.bundle/jf_icon_navi_return@2x.png -------------------------------------------------------------------------------- /JFCitySelector/Resources/JFCitySelector.bundle/jf_icon_navi_return@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhifenx/JFCitySelector/ccc1bb113959df9066d5f639a1ed807323f62d74/JFCitySelector/Resources/JFCitySelector.bundle/jf_icon_navi_return@3x.png -------------------------------------------------------------------------------- /JFCitySelector/Resources/JFCitySelector.bundle/jf_icon_search_button@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhifenx/JFCitySelector/ccc1bb113959df9066d5f639a1ed807323f62d74/JFCitySelector/Resources/JFCitySelector.bundle/jf_icon_search_button@3x.png -------------------------------------------------------------------------------- /JFCitySelector/Resources/JFCitySelector.bundle/jf_icon_up@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhifenx/JFCitySelector/ccc1bb113959df9066d5f639a1ed807323f62d74/JFCitySelector/Resources/JFCitySelector.bundle/jf_icon_up@3x.png -------------------------------------------------------------------------------- /JFCitySelector/Resources/JFCitySelector.bundle/provinces.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | provinces 6 | 7 | 8 | code 9 | 11 10 | firstLetter 11 | B 12 | name 13 | 北京市 14 | pinyin 15 | bei jing shi 16 | 17 | 18 | code 19 | 12 20 | firstLetter 21 | T 22 | name 23 | 天津市 24 | pinyin 25 | tian jin shi 26 | 27 | 28 | code 29 | 13 30 | firstLetter 31 | H 32 | name 33 | 河北省 34 | pinyin 35 | he bei sheng 36 | 37 | 38 | code 39 | 14 40 | firstLetter 41 | S 42 | name 43 | 山西省 44 | pinyin 45 | shan xi sheng 46 | 47 | 48 | code 49 | 15 50 | firstLetter 51 | N 52 | name 53 | 内蒙古自治区 54 | pinyin 55 | nei meng gu zi zhi qu 56 | 57 | 58 | code 59 | 21 60 | firstLetter 61 | L 62 | name 63 | 辽宁省 64 | pinyin 65 | liao ning sheng 66 | 67 | 68 | code 69 | 22 70 | firstLetter 71 | J 72 | name 73 | 吉林省 74 | pinyin 75 | ji lin sheng 76 | 77 | 78 | code 79 | 23 80 | firstLetter 81 | H 82 | name 83 | 黑龙江省 84 | pinyin 85 | hei long jiang sheng 86 | 87 | 88 | code 89 | 31 90 | firstLetter 91 | S 92 | name 93 | 上海市 94 | pinyin 95 | shang hai shi 96 | 97 | 98 | code 99 | 32 100 | firstLetter 101 | J 102 | name 103 | 江苏省 104 | pinyin 105 | jiang su sheng 106 | 107 | 108 | code 109 | 33 110 | firstLetter 111 | Z 112 | name 113 | 浙江省 114 | pinyin 115 | zhe jiang sheng 116 | 117 | 118 | code 119 | 34 120 | firstLetter 121 | A 122 | name 123 | 安徽省 124 | pinyin 125 | an hui sheng 126 | 127 | 128 | code 129 | 35 130 | firstLetter 131 | F 132 | name 133 | 福建省 134 | pinyin 135 | fu jian sheng 136 | 137 | 138 | code 139 | 36 140 | firstLetter 141 | J 142 | name 143 | 江西省 144 | pinyin 145 | jiang xi sheng 146 | 147 | 148 | code 149 | 37 150 | firstLetter 151 | S 152 | name 153 | 山东省 154 | pinyin 155 | shan dong sheng 156 | 157 | 158 | code 159 | 41 160 | firstLetter 161 | H 162 | name 163 | 河南省 164 | pinyin 165 | he nan sheng 166 | 167 | 168 | code 169 | 42 170 | firstLetter 171 | H 172 | name 173 | 湖北省 174 | pinyin 175 | hu bei sheng 176 | 177 | 178 | code 179 | 43 180 | firstLetter 181 | H 182 | name 183 | 湖南省 184 | pinyin 185 | hu nan sheng 186 | 187 | 188 | code 189 | 44 190 | firstLetter 191 | G 192 | name 193 | 广东省 194 | pinyin 195 | guang dong sheng 196 | 197 | 198 | code 199 | 45 200 | firstLetter 201 | G 202 | name 203 | 广西壮族自治区 204 | pinyin 205 | guang xi zhuang zu zi zhi qu 206 | 207 | 208 | code 209 | 46 210 | firstLetter 211 | H 212 | name 213 | 海南省 214 | pinyin 215 | hai nan sheng 216 | 217 | 218 | code 219 | 50 220 | firstLetter 221 | C 222 | name 223 | 重庆市 224 | pinyin 225 | chong qing shi 226 | 227 | 228 | code 229 | 51 230 | firstLetter 231 | S 232 | name 233 | 四川省 234 | pinyin 235 | si chuan sheng 236 | 237 | 238 | code 239 | 52 240 | firstLetter 241 | G 242 | name 243 | 贵州省 244 | pinyin 245 | gui zhou sheng 246 | 247 | 248 | code 249 | 53 250 | firstLetter 251 | Y 252 | name 253 | 云南省 254 | pinyin 255 | yun nan sheng 256 | 257 | 258 | code 259 | 54 260 | firstLetter 261 | X 262 | name 263 | 西藏自治区 264 | pinyin 265 | xi cang zi zhi qu 266 | 267 | 268 | code 269 | 61 270 | firstLetter 271 | S 272 | name 273 | 陕西省 274 | pinyin 275 | shan xi sheng 276 | 277 | 278 | code 279 | 62 280 | firstLetter 281 | G 282 | name 283 | 甘肃省 284 | pinyin 285 | gan su sheng 286 | 287 | 288 | code 289 | 63 290 | firstLetter 291 | Q 292 | name 293 | 青海省 294 | pinyin 295 | qing hai sheng 296 | 297 | 298 | code 299 | 64 300 | firstLetter 301 | N 302 | name 303 | 宁夏回族自治区 304 | pinyin 305 | ning xia hui zu zi zhi qu 306 | 307 | 308 | code 309 | 65 310 | firstLetter 311 | X 312 | name 313 | 新疆维吾尔自治区(省级) 314 | pinyin 315 | xin jiang wei wu er zi zhi qu 316 | 317 | 318 | 319 | 320 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2019 837856614@qq.com 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # JFCitySelector 2 | 3 | 4 | [![Version](https://img.shields.io/cocoapods/v/JFCitySelector.svg?style=flat)](https://cocoapods.org/pods/JFCitySelector) 5 | [![License](https://img.shields.io/cocoapods/l/JFCitySelector.svg?style=flat)](https://cocoapods.org/pods/JFCitySelector) 6 | [![Platform](https://img.shields.io/cocoapods/p/JFCitySelector.svg?style=flat)](https://cocoapods.org/pods/JFCitySelector) 7 | 8 | # 简介 9 | [JFCitySelector](https://github.com/zhifenx/JFCitySelector) 10 | 是一个轻量、灵活、可自定义的三级城市选择器(City selector、City picker)。 11 | # 特点 12 | * 汉字、拼音搜索 13 | * 首字母索引 14 | * 可自定义热门城市 15 | * 可自定义最近访问 16 | * 可使用提供的UI界面,亦可用提供的数据接口自己实现UI界面。 17 | 18 | # 页面展示 19 | ![屏幕快照 2019-08-01 下午2.20.22.png](https://upload-images.jianshu.io/upload_images/1707533-c326e7a4e8726dc1.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 20 | 21 | # 项目结构 22 | ![项目结构.png](https://upload-images.jianshu.io/upload_images/1707533-0cf40b179c67442b.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 23 | 24 | 25 | # 安装 26 | ##### CocoaPods安装 27 | * 在 Podfile 中添加 pod 'JFCitySelector'。 28 | * 执行 pod install 或 pod update。 29 | * 导入 JFCitySelector.h。 30 | 31 | ##### 手动安装 32 | * 下载 JFCitySelector 文件夹内的所有内容。 33 | * 将 JFCitySelector 内的Classes、Resources文件夹添加(拖放)到你的工程。 34 | * 导入 JFCitySelector.h。 35 | 36 | ##### 系统要求 37 | * 该项目最低支持iOS 8.0 38 | 39 | # 具体使用 40 | ##### 1、 使用已有的UI `JFCSTableViewController ` 41 | * 遵循`JFCSTableViewControllerDelegate`, 42 | * 使用`initWithConfiguration: delegate:`初始化`JFCSTableViewController`;若直接使用 `init`初始化`JFCSTableViewController`,`JFCSConfiguration`则为默认配置。 43 | 44 | ``` 45 | //自定义配置... 46 | JFCSConfiguration *config = [[JFCSConfiguration alloc] init]; 47 | //关闭拼音搜索 48 | config.isPinyinSearch = NO; 49 | //配置热门城市 50 | config.popularCitiesMutableArray = [self defealtPopularCities]; 51 | 52 | JFCSTableViewController *vc = [[JFCSTableViewController alloc] initWithConfiguration:config delegate:self]; 53 | [self.navigationController pushViewController:vc animated:YES]; 54 | 55 | #pragma mark -- JFCSTableViewControllerDelegate 56 | 57 | - (void)viewController:(JFCSTableViewController *)viewController didSelectCity:(JFCSBaseInfoModel *)model { 58 | //选择城市后... 59 | NSLog(@"name %@ code %zd pinyin %@ alias %@ firstLetter %@",model.name, model.code, model.pinyin, model.alias, model.firstLetter); 60 | } 61 | 62 | //自定义热门城市 63 | - (NSMutableArray *)defealtPopularCities { 64 | JFCSPopularCitiesModel *bjModel = [[JFCSPopularCitiesModel alloc] initWithName:@"北京" type:JFCSPopularCitiesTypeCity]; 65 | JFCSPopularCitiesModel *shModel = [[JFCSPopularCitiesModel alloc] initWithName:@"上海" type:JFCSPopularCitiesTypeCity]; 66 | JFCSPopularCitiesModel *gzModel = [[JFCSPopularCitiesModel alloc] initWithName:@"广州" type:JFCSPopularCitiesTypeCity]; 67 | JFCSPopularCitiesModel *szModel = [[JFCSPopularCitiesModel alloc] initWithName:@"深圳" type:JFCSPopularCitiesTypeCity]; 68 | JFCSPopularCitiesModel *hzModel = [[JFCSPopularCitiesModel alloc] initWithName:@"杭州" type:JFCSPopularCitiesTypeCity]; 69 | return [NSMutableArray arrayWithObjects:bjModel, shModel, gzModel, szModel, hzModel, nil]; 70 | } 71 | ``` 72 | 73 | ##### 2、 使用`JFCSDataOpreation`提供的数据接口,自建UI。 74 | * 使用`initWithConfiguration:`,传入`JFCSConfiguration`实例,初始化`JFCSDataOpreation` 75 | 76 | ``` 77 | JFCSConfiguration *config = [[JFCSConfiguration alloc] init]; 78 | config.isPinyinSearch = NO; 79 | //自定义配置... 80 | JFCSDataOpreation *dataOpreation = [[JFCSDataOpreation alloc] initWithConfiguration:config]; 81 | 82 | [dataOpreation provinces:^(NSArray * _Nonnull provinces) { 83 | //数据源... 84 | }]; 85 | 86 | //code... 87 | ``` 88 | 89 | ##### 3、注意 90 | * `JFCSConfiguration `的属性`popularCitiesMutableArray `数组,元素必须是`JFCSPopularCitiesModel `类型; 91 | * `JFCSPopularCitiesModel `初始化方法:`initWithName: type:`,传入的城市名称必须要和数据源内的name对应上,因为牵扯到城市名称所对应的code,所以在自定义城市前可以先打印所需城市的`JFCSBaseInfoModel `数据;type也必须是`JFCSPopularCitiesType`,分别为`JFCSPopularCitiesTypeProvince`(省级)、`JFCSPopularCitiesTypeCity`(市级)和`JFCSPopularCitiesTypeArea`(县级)。 92 | 93 | ## 简书 94 | [JFCitySelector轻量、灵活、可自定义的三级城市选择器 95 | ](https://www.jianshu.com/p/413db5c2480b) 96 | 97 | ## 许可证 98 | 99 | JFCitySelector 使用 MIT 许可证,详情见 LICENSE 文件。 100 | --------------------------------------------------------------------------------