├── .DS_Store ├── UITableViewLInkageDemo ├── Assets.xcassets │ ├── Contents.json │ ├── icon_add_n.imageset │ │ ├── icon_add_n@2x.png │ │ ├── icon_add_n@3x.png │ │ └── Contents.json │ ├── icon_delete.imageset │ │ ├── icon_delete@2x.png │ │ ├── icon_delete@3x.png │ │ └── Contents.json │ └── AppIcon.appiconset │ │ └── Contents.json ├── Class │ ├── Controller │ │ ├── ViewController.h │ │ └── ViewController.m │ ├── Other │ │ ├── CompareTableView.h │ │ ├── EScrollDelegate.h │ │ ├── AppDelegate.h │ │ ├── CompareTableView.m │ │ ├── AppDelegate.m │ │ └── Header.h │ ├── View │ │ ├── Configuration │ │ │ ├── BackgroundView.h │ │ │ ├── ConfigSectionHeaderView.h │ │ │ ├── ConfigCell.h │ │ │ ├── ConfigurationView.h │ │ │ ├── BackgroundView.m │ │ │ ├── ConfigSectionHeaderView.m │ │ │ ├── ConfigCell.m │ │ │ ├── ConfigurationView.m │ │ │ ├── ConfigCell.xib │ │ │ └── ConfigSectionHeaderView.xib │ │ ├── CompareDetail │ │ │ ├── CompareTableHeader.h │ │ │ ├── CompareCell.h │ │ │ ├── CompareDetailView.h │ │ │ ├── CompareTableHeader.m │ │ │ ├── CompareCell.m │ │ │ ├── CompareDetailView.m │ │ │ └── CompareItem.xib │ │ ├── CompareHeaderView.h │ │ ├── CompareHeaderView.m │ │ └── CompareHeaderView.xib │ ├── YYModel │ │ ├── YYModel │ │ │ ├── YYModel.h │ │ │ ├── YYClassInfo.h │ │ │ ├── YYClassInfo.m │ │ │ └── NSObject+YYModel.h │ │ ├── LICENSE │ │ └── README.md │ └── Model │ │ ├── CarModel.m │ │ └── CarModel.h ├── main.m ├── Info.plist └── Base.lproj │ ├── Main.storyboard │ └── LaunchScreen.storyboard ├── UITableViewLInkageDemo.xcodeproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ ├── xcuserdata │ │ └── eleven.xcuserdatad │ │ │ └── UserInterfaceState.xcuserstate │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist ├── xcuserdata │ ├── apple.xcuserdatad │ │ ├── xcdebugger │ │ │ └── Breakpoints_v2.xcbkptlist │ │ └── xcschemes │ │ │ ├── xcschememanagement.plist │ │ │ └── UITableViewLInkageDemo.xcscheme │ └── eleven.xcuserdatad │ │ ├── xcdebugger │ │ └── Breakpoints_v2.xcbkptlist │ │ └── xcschemes │ │ └── xcschememanagement.plist └── project.pbxproj ├── .gitignore ├── UITableViewLInkageDemoUITests ├── Info.plist └── UITableViewLInkageDemoUITests.m └── README.md /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HawkEleven/UITableViewLinkageDemo/HEAD/.DS_Store -------------------------------------------------------------------------------- /UITableViewLInkageDemo/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /UITableViewLInkageDemo/Assets.xcassets/icon_add_n.imageset/icon_add_n@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HawkEleven/UITableViewLinkageDemo/HEAD/UITableViewLInkageDemo/Assets.xcassets/icon_add_n.imageset/icon_add_n@2x.png -------------------------------------------------------------------------------- /UITableViewLInkageDemo/Assets.xcassets/icon_add_n.imageset/icon_add_n@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HawkEleven/UITableViewLinkageDemo/HEAD/UITableViewLInkageDemo/Assets.xcassets/icon_add_n.imageset/icon_add_n@3x.png -------------------------------------------------------------------------------- /UITableViewLInkageDemo/Assets.xcassets/icon_delete.imageset/icon_delete@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HawkEleven/UITableViewLinkageDemo/HEAD/UITableViewLInkageDemo/Assets.xcassets/icon_delete.imageset/icon_delete@2x.png -------------------------------------------------------------------------------- /UITableViewLInkageDemo/Assets.xcassets/icon_delete.imageset/icon_delete@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HawkEleven/UITableViewLinkageDemo/HEAD/UITableViewLInkageDemo/Assets.xcassets/icon_delete.imageset/icon_delete@3x.png -------------------------------------------------------------------------------- /UITableViewLInkageDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /UITableViewLInkageDemo.xcodeproj/project.xcworkspace/xcuserdata/eleven.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HawkEleven/UITableViewLinkageDemo/HEAD/UITableViewLInkageDemo.xcodeproj/project.xcworkspace/xcuserdata/eleven.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | .DS_Store 3 | */build/* 4 | *.pbxuser 5 | !default.pbxuser 6 | *.mode1v3 7 | !default.mode1v3 8 | *.mode2v3 9 | !default.mode2v3 10 | *.perspectivev3 11 | !default.perspectivev3 12 | xcuserdata 13 | profile 14 | *.moved-aside 15 | DerivedData 16 | .idea/ 17 | *.hmap 18 | -------------------------------------------------------------------------------- /UITableViewLInkageDemo/Class/Controller/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // UITableViewLInkageDemo 4 | // 5 | // Created by Eleven on 2017/7/3. 6 | // Copyright © 2017年 Hawk. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ViewController : UIViewController 12 | 13 | 14 | @end 15 | 16 | -------------------------------------------------------------------------------- /UITableViewLInkageDemo.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /UITableViewLInkageDemo/Class/Other/CompareTableView.h: -------------------------------------------------------------------------------- 1 | // 2 | // CompareTableView.h 3 | // UITableViewLInkageDemo 4 | // 5 | // Created by Eleven on 2017/8/13. 6 | // Copyright © 2017年 Hawk. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface CompareTableView : UITableView 12 | 13 | @property (nonatomic, strong) UIBezierPath *path; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /UITableViewLInkageDemo/Class/View/Configuration/BackgroundView.h: -------------------------------------------------------------------------------- 1 | // 2 | // BackgroundView.h 3 | // UITableViewLInkageDemo 4 | // 5 | // Created by Eleven on 2018/8/14. 6 | // Copyright © 2018年 Hawk. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface BackgroundView : UIView 12 | 13 | @property (nonatomic, strong) UIBezierPath *path; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /UITableViewLInkageDemo/Class/Other/EScrollDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // EScrollDelegate.h 3 | // UITableViewLInkageDemo 4 | // 5 | // Created by Eleven on 2018/8/14. 6 | // Copyright © 2018年 Hawk. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @protocol EScrollDelegate 12 | 13 | - (void)e_scrollViewDidScroll:(UIScrollView *)scrollView; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /UITableViewLInkageDemo/Class/Other/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // UITableViewLInkageDemo 4 | // 5 | // Created by Eleven on 2017/7/3. 6 | // Copyright © 2017年 Hawk. 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 | -------------------------------------------------------------------------------- /UITableViewLInkageDemo/Class/View/CompareDetail/CompareTableHeader.h: -------------------------------------------------------------------------------- 1 | // 2 | // CompareTableHeader.h 3 | // UITableViewLInkageDemo 4 | // 5 | // Created by Eleven on 2018/8/20. 6 | // Copyright © 2018年 Hawk. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @class CarModel; 12 | @interface CompareTableHeader : UIView 13 | 14 | @property (nonatomic, copy) NSArray *datas; 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /UITableViewLInkageDemo/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // UITableViewLInkageDemo 4 | // 5 | // Created by Eleven on 2017/7/3. 6 | // Copyright © 2017年 Hawk. 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 | -------------------------------------------------------------------------------- /UITableViewLInkageDemo/Class/View/Configuration/ConfigSectionHeaderView.h: -------------------------------------------------------------------------------- 1 | // 2 | // SectionHeaderView.h 3 | // UITableViewLInkageDemo 4 | // 5 | // Created by Eleven on 2017/7/3. 6 | // Copyright © 2016年 Hawk. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ConfigSectionHeaderView : UIView 12 | 13 | @property (weak, nonatomic) IBOutlet UILabel *titleLabel; 14 | 15 | + (instancetype)creatView; 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /UITableViewLInkageDemo/Class/View/Configuration/ConfigCell.h: -------------------------------------------------------------------------------- 1 | // 2 | // ConfigCell.h 3 | // UITableViewLInkageDemo 4 | // 5 | // Created by Eleven on 2018/8/20. 6 | // Copyright © 2018年 Hawk. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ConfigCell : UITableViewCell 12 | 13 | @property (weak, nonatomic) IBOutlet UILabel *titleLabel; 14 | 15 | 16 | + (instancetype)cellWithTableView:(UITableView *)tableView; 17 | 18 | @end 19 | -------------------------------------------------------------------------------- /UITableViewLInkageDemo/Assets.xcassets/icon_add_n.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "icon_add_n@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "filename" : "icon_add_n@3x.png", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /UITableViewLInkageDemo/Class/View/CompareDetail/CompareCell.h: -------------------------------------------------------------------------------- 1 | // 2 | // CompareCell.h 3 | // UITableViewLInkageDemo 4 | // 5 | // Created by Eleven on 2017/7/3. 6 | // Copyright © 2016年 Hawk. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @class CarModel; 12 | @interface CompareCell : UITableViewCell 13 | 14 | + (instancetype)cellWithTableView:(UITableView *)tableView; 15 | 16 | - (void)setDatas:(NSArray *)datas withIndex:(NSIndexPath *)indexPath; 17 | 18 | @end 19 | -------------------------------------------------------------------------------- /UITableViewLInkageDemo/Assets.xcassets/icon_delete.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "icon_delete@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "filename" : "icon_delete@3x.png", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /UITableViewLInkageDemo/Class/View/CompareDetail/CompareDetailView.h: -------------------------------------------------------------------------------- 1 | // 2 | // CompareDetailView.h 3 | // UITableViewLInkageDemo 4 | // 5 | // Created by Eleven on 2018/8/13. 6 | // Copyright © 2018年 Hawk. All rights reserved. 7 | // 右侧对比项 8 | 9 | #import 10 | #import "EScrollDelegate.h" 11 | 12 | @class CarModel; 13 | @interface CompareDetailView : UIView 14 | 15 | @property (nonatomic, strong) UITableView *tableView; 16 | @property (nonatomic, copy) NSArray *datas; 17 | @property (nonatomic, weak) id delegate; 18 | 19 | 20 | @end 21 | -------------------------------------------------------------------------------- /UITableViewLInkageDemo/Class/View/Configuration/ConfigurationView.h: -------------------------------------------------------------------------------- 1 | // 2 | // ConfigurationView.h 3 | // UITableViewLInkageDemo 4 | // 5 | // Created by Eleven on 2018/8/13. 6 | // Copyright © 2018年 Hawk. All rights reserved. 7 | // 左侧配置项 8 | 9 | #import "BackgroundView.h" 10 | #import "EScrollDelegate.h" 11 | 12 | @class CarModel; 13 | @interface ConfigurationView : BackgroundView 14 | 15 | @property (nonatomic, strong) UITableView *tableView; 16 | @property (nonatomic, copy) NSArray *datas; 17 | @property (nonatomic, weak) id delegate; 18 | 19 | @end 20 | -------------------------------------------------------------------------------- /UITableViewLInkageDemo/Class/Other/CompareTableView.m: -------------------------------------------------------------------------------- 1 | // 2 | // CompareTableView.m 3 | // UITableViewLInkageDemo 4 | // 5 | // Created by Eleven on 2017/8/13. 6 | // Copyright © 2017年 Hawk. All rights reserved. 7 | // 8 | 9 | #import "CompareTableView.h" 10 | 11 | @implementation CompareTableView 12 | 13 | - (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event { 14 | if (CGPathIsEmpty(self.path.CGPath)) { 15 | return YES; 16 | } else if (CGPathContainsPoint(self.path.CGPath, nil, point, nil)) { 17 | return YES; 18 | } else { 19 | return NO; 20 | } 21 | } 22 | 23 | @end 24 | -------------------------------------------------------------------------------- /UITableViewLInkageDemo/Class/View/Configuration/BackgroundView.m: -------------------------------------------------------------------------------- 1 | // 2 | // BackgroundView.m 3 | // UITableViewLInkageDemo 4 | // 5 | // Created by Eleven on 2018/8/14. 6 | // Copyright © 2018年 Hawk. All rights reserved. 7 | // 8 | 9 | #import "BackgroundView.h" 10 | 11 | @implementation BackgroundView 12 | 13 | - (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event { 14 | if (CGPathIsEmpty(self.path.CGPath)) { 15 | return YES; 16 | } else if (CGPathContainsPoint(self.path.CGPath, nil, point, nil)) { 17 | return YES; 18 | } else { 19 | return NO; 20 | } 21 | } 22 | 23 | @end 24 | -------------------------------------------------------------------------------- /UITableViewLInkageDemo.xcodeproj/xcuserdata/apple.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 8 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /UITableViewLInkageDemo.xcodeproj/xcuserdata/eleven.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 8 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /UITableViewLInkageDemo.xcodeproj/xcuserdata/eleven.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | UITableViewLInkageDemo.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | UITableViewLInkageDemo.xcscheme_^#shared#^_ 13 | 14 | orderHint 15 | 0 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /UITableViewLInkageDemo/Class/View/Configuration/ConfigSectionHeaderView.m: -------------------------------------------------------------------------------- 1 | // 2 | // ParameterSectionHeaderView.m 3 | // UITableViewLInkageDemo 4 | // 5 | // Created by Eleven on 2017/7/3. 6 | // Copyright © 2016年 Hawk. All rights reserved. 7 | // 8 | 9 | #import "ConfigSectionHeaderView.h" 10 | 11 | @implementation ConfigSectionHeaderView 12 | { 13 | /// 标题 14 | __weak IBOutlet UILabel *_title; 15 | /// 向下箭头 16 | __weak IBOutlet UIImageView *_arrow; 17 | } 18 | 19 | -(void)awakeFromNib { 20 | [super awakeFromNib]; 21 | } 22 | 23 | + (instancetype)creatView { 24 | return [[[NSBundle mainBundle] loadNibNamed:@"ConfigSectionHeaderView" owner:self options:nil] objectAtIndex:0]; 25 | } 26 | 27 | 28 | @end 29 | -------------------------------------------------------------------------------- /UITableViewLInkageDemo/Class/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 | -------------------------------------------------------------------------------- /UITableViewLInkageDemo.xcodeproj/xcuserdata/apple.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | UITableViewLInkageDemo.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | 18700D571F09ED4100F51FB4 16 | 17 | primary 18 | 19 | 20 | 18700D701F09ED4100F51FB4 21 | 22 | primary 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /UITableViewLInkageDemoUITests/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 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /UITableViewLInkageDemo/Class/View/Configuration/ConfigCell.m: -------------------------------------------------------------------------------- 1 | // 2 | // ConfigCell.m 3 | // UITableViewLInkageDemo 4 | // 5 | // Created by Eleven on 2018/8/20. 6 | // Copyright © 2018年 Hawk. All rights reserved. 7 | // 8 | 9 | #import "ConfigCell.h" 10 | 11 | @implementation ConfigCell 12 | 13 | - (void)awakeFromNib { 14 | [super awakeFromNib]; 15 | // Initialization code 16 | } 17 | 18 | + (instancetype)cellWithTableView:(UITableView *)tableView { 19 | static NSString * ID = @"ConfigCell"; 20 | ConfigCell * cell = [tableView dequeueReusableCellWithIdentifier:ID]; 21 | if (!cell) { 22 | cell = [[[NSBundle mainBundle] loadNibNamed:@"ConfigCell" owner:nil options:nil] objectAtIndex:0]; 23 | } 24 | cell.selectionStyle = UITableViewCellSelectionStyleNone; 25 | return cell; 26 | } 27 | 28 | @end 29 | -------------------------------------------------------------------------------- /UITableViewLInkageDemo/Class/Model/CarModel.m: -------------------------------------------------------------------------------- 1 | // 2 | // CarModel.m 3 | // UITableViewLInkageDemo 4 | // 5 | // Created by Eleven on 2018/8/13. 6 | // Copyright © 2018年 Hawk. All rights reserved. 7 | // 8 | 9 | #import "CarModel.h" 10 | 11 | @implementation CarModel 12 | 13 | + (NSDictionary *)modelContainerPropertyGenericClass{ 14 | return @{@"specColorList" : [CarSpeccolorlistModel class], @"groupParamsViewModelList" : [GroupParamsModel class]}; 15 | } 16 | 17 | 18 | @end 19 | 20 | @implementation CarSpeccolorlistModel 21 | 22 | 23 | + (NSDictionary *)modelCustomPropertyMapper{ 24 | return @{@"ID" : @"id"}; 25 | } 26 | 27 | @end 28 | 29 | 30 | @implementation GroupParamsModel 31 | 32 | + (NSDictionary *)modelContainerPropertyGenericClass{ 33 | return @{@"paramList" : [ParamlistModel class]}; 34 | } 35 | 36 | 37 | @end 38 | 39 | 40 | @implementation ParamlistModel 41 | 42 | 43 | @end 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /UITableViewLInkageDemo/Class/View/CompareHeaderView.h: -------------------------------------------------------------------------------- 1 | // 2 | // ParameterCompareHeaderView.h 3 | // UITableViewLInkageDemo 4 | // 5 | // Created by Eleven on 2017/7/3. 6 | // Copyright © 2016年 Hawk. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | typedef void(^ParameterClickBlock)(); 12 | 13 | @interface CompareHiddenHeader : UIView 14 | 15 | @property (nonatomic, copy) ParameterClickBlock hiddenBlock; 16 | 17 | 18 | + (instancetype)creatView; 19 | 20 | @end 21 | 22 | @interface CompareRightHeader : UIView 23 | 24 | @property (weak, nonatomic) IBOutlet UILabel *modelNameLabel; 25 | @property (weak, nonatomic) IBOutlet UIButton *deleteBtn; 26 | @property (nonatomic, copy) ParameterClickBlock deleteBlock; 27 | 28 | + (instancetype)creatView; 29 | 30 | @end 31 | 32 | @interface CompareAddHeader : UIView 33 | 34 | @property (weak, nonatomic) IBOutlet UIButton *addButton; 35 | @property (nonatomic, copy) ParameterClickBlock addBlock; 36 | 37 | + (instancetype)creatView; 38 | 39 | @end 40 | -------------------------------------------------------------------------------- /UITableViewLInkageDemo/Class/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 | -------------------------------------------------------------------------------- /UITableViewLInkageDemo/Class/View/CompareHeaderView.m: -------------------------------------------------------------------------------- 1 | // 2 | // ParameterCompareHeaderView.m 3 | // UITableViewLInkageDemo 4 | // 5 | // Created by Eleven on 2017/7/3. 6 | // Copyright © 2016年 Hawk. All rights reserved. 7 | // 8 | 9 | #import "CompareHeaderView.h" 10 | 11 | @implementation CompareHiddenHeader 12 | { 13 | __weak IBOutlet UIImageView *_imgView; 14 | __weak IBOutlet UILabel *_promptLabel; 15 | } 16 | 17 | + (instancetype)creatView { 18 | return [[[NSBundle mainBundle] loadNibNamed:@"CompareHeaderView" owner:self options:nil] objectAtIndex:0]; 19 | } 20 | 21 | - (IBAction)hidden:(UIButton *)sender { 22 | 23 | } 24 | 25 | @end 26 | 27 | @implementation CompareRightHeader 28 | 29 | - (void)awakeFromNib { 30 | [super awakeFromNib]; 31 | 32 | } 33 | 34 | + (instancetype)creatView { 35 | return [[[NSBundle mainBundle] loadNibNamed:@"CompareHeaderView" owner:self options:nil] objectAtIndex:1]; 36 | } 37 | 38 | - (IBAction)delete:(UIButton *)sender { 39 | if (self.deleteBlock) { 40 | self.deleteBlock(); 41 | } 42 | } 43 | 44 | @end 45 | 46 | @implementation CompareAddHeader 47 | 48 | + (instancetype)creatView { 49 | return [[[NSBundle mainBundle] loadNibNamed:@"CompareHeaderView" owner:self options:nil] objectAtIndex:2]; 50 | } 51 | 52 | - (IBAction)add:(UIButton *)sender { 53 | if (self.addBlock) { 54 | self.addBlock(); 55 | } 56 | } 57 | 58 | @end 59 | -------------------------------------------------------------------------------- /UITableViewLInkageDemoUITests/UITableViewLInkageDemoUITests.m: -------------------------------------------------------------------------------- 1 | // 2 | // UITableViewLInkageDemoUITests.m 3 | // UITableViewLInkageDemoUITests 4 | // 5 | // Created by Eleven on 2017/7/3. 6 | // Copyright © 2017年 Hawk. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface UITableViewLInkageDemoUITests : XCTestCase 12 | 13 | @end 14 | 15 | @implementation UITableViewLInkageDemoUITests 16 | 17 | - (void)setUp { 18 | [super setUp]; 19 | 20 | // Put setup code here. This method is called before the invocation of each test method in the class. 21 | 22 | // In UI tests it is usually best to stop immediately when a failure occurs. 23 | self.continueAfterFailure = NO; 24 | // UI tests must launch the application that they test. Doing this in setup will make sure it happens for each test method. 25 | [[[XCUIApplication alloc] init] launch]; 26 | 27 | // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this. 28 | } 29 | 30 | - (void)tearDown { 31 | // Put teardown code here. This method is called after the invocation of each test method in the class. 32 | [super tearDown]; 33 | } 34 | 35 | - (void)testExample { 36 | // Use recording to get started writing UI tests. 37 | // Use XCTAssert and related functions to verify your tests produce the correct results. 38 | } 39 | 40 | @end 41 | -------------------------------------------------------------------------------- /UITableViewLInkageDemo/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 | 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 | 35 | UISupportedInterfaceOrientations~ipad 36 | 37 | UIInterfaceOrientationPortrait 38 | UIInterfaceOrientationPortraitUpsideDown 39 | UIInterfaceOrientationLandscapeLeft 40 | UIInterfaceOrientationLandscapeRight 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /UITableViewLInkageDemo/Class/View/CompareDetail/CompareTableHeader.m: -------------------------------------------------------------------------------- 1 | // 2 | // CompareTableHeader.m 3 | // UITableViewLInkageDemo 4 | // 5 | // Created by Eleven on 2018/8/20. 6 | // Copyright © 2018年 Hawk. All rights reserved. 7 | // 8 | 9 | #import "CompareTableHeader.h" 10 | #import "CarModel.h" 11 | #import "CompareHeaderView.h" 12 | #import "Header.h" 13 | 14 | @implementation CompareTableHeader 15 | 16 | - (void)setDatas:(NSArray *)datas { 17 | for (UIView *view in self.subviews) { 18 | if ([view isKindOfClass:[CompareRightHeader class]] || [view isKindOfClass:[CompareAddHeader class]]) { 19 | [view removeFromSuperview]; 20 | } 21 | } 22 | 23 | for (NSInteger i = 0; i < datas.count; i ++) { 24 | if (i == datas.count - 1) { 25 | CompareAddHeader *header = [CompareAddHeader creatView]; 26 | header.frame = CGRectMake(ITEM_WIDTH * i, 0, ITEM_WIDTH, 66); 27 | header.addBlock = ^{ 28 | [[NSNotificationCenter defaultCenter] postNotificationName:@"addNotification" object:nil]; 29 | }; 30 | [self addSubview:header]; 31 | } else { 32 | CompareRightHeader *header = [CompareRightHeader creatView]; 33 | header.frame = CGRectMake(ITEM_WIDTH * i, 0, ITEM_WIDTH, 66); 34 | header.modelNameLabel.text = datas[i].specName; 35 | header.deleteBlock = ^{ 36 | [[NSNotificationCenter defaultCenter] postNotificationName:@"deleteNotification" object:nil userInfo:@{@"index" : @(i)}]; 37 | }; 38 | [self addSubview:header]; 39 | } 40 | } 41 | } 42 | 43 | @end 44 | -------------------------------------------------------------------------------- /UITableViewLInkageDemo/Class/Model/CarModel.h: -------------------------------------------------------------------------------- 1 | // 2 | // CarModel.h 3 | // UITableViewLInkageDemo 4 | // 5 | // Created by Eleven on 2018/8/13. 6 | // Copyright © 2018年 Hawk. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @class CarSpeccolorlistModel, GroupParamsModel, ParamlistModel; 13 | @interface CarModel : NSObject 14 | 15 | @property (nonatomic, assign) NSInteger specId; 16 | 17 | @property (nonatomic, strong) NSArray *groupParamsViewModelList; 18 | 19 | @property (nonatomic, strong) NSArray *specColorList; 20 | 21 | @property (nonatomic, copy) NSString *specName; 22 | 23 | @property (nonatomic, assign) NSInteger seriesId; 24 | 25 | @end 26 | 27 | 28 | @interface CarSpeccolorlistModel : NSObject 29 | 30 | @property (nonatomic, assign) NSInteger specId; 31 | 32 | @property (nonatomic, assign) NSInteger ID; 33 | 34 | @property (nonatomic, assign) NSInteger bodyId; 35 | 36 | @property (nonatomic, copy) NSString *colorValue; 37 | 38 | @property (nonatomic, copy) NSString *colorName; 39 | 40 | @property (nonatomic, assign) NSInteger colorId; 41 | 42 | @end 43 | 44 | 45 | @interface GroupParamsModel : NSObject 46 | 47 | @property (nonatomic, assign) NSInteger groupId; 48 | 49 | @property (nonatomic, copy) NSString *groupName; 50 | 51 | @property (nonatomic, strong) NSArray *paramList; 52 | 53 | @end 54 | 55 | 56 | @interface ParamlistModel : NSObject 57 | 58 | @property (nonatomic, assign) NSInteger paramId; 59 | 60 | @property (nonatomic, copy) NSString *paramValue; 61 | 62 | @property (nonatomic, copy) NSString *paramName; 63 | 64 | @end 65 | 66 | 67 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # UITableViewLinkageDemo 2 | 3 | iOS采用UITableView来实现Excel、课程表、汽车之家车辆参数对比的上下左右联动效果。 4 | 5 | ### 引言 6 | 7 | 2016年12月份在项目中遇到了这种需求,当时能参照的效果只有汽车之家,且没有开源的项目供参考,因此自己摸索了这种实现方式。早想将本方案呈现给大伙以供参考,无奈项目一直赶进度,因此拖到了现在。提笔之时发现其他网友已经贡献了类似项目的实现思路,那么,大家可以综合对比,撷取精华部分为自己所用。 8 | 9 | (备注:感谢flyOfYW和YearRen的促使,使我下定决心重构,摒弃之前复杂的设计) 10 | 11 | ### 需求描述 12 | 13 | > 列表可以左右滑动,上下滚动,且能动态增加删除列表个数。 14 | 15 | ![列表.jpeg](http://upload-images.jianshu.io/upload_images/1338824-b1913d0aa37ca25a.jpeg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 16 | 17 | #### 效果如下 18 | 19 | ![列表左右上下滑动.gif](http://upload-images.jianshu.io/upload_images/1338824-46230582665664d3.gif?imageMogr2/auto-orient/strip) 20 | 21 | ### 方案实现(2018-08-14重构) 22 | 23 | 1)视图有两部分组成:左侧**ConfigurationView**和右侧**CompareDetailView**; 24 | 25 | 2)CompareDetailView在视图底层,宽度为绿色区域宽,核心为UITableView,添加到UIScrollview上面,这样就可实现上下和左右滑动; 26 | 27 | 3)ConfigurationView在视图顶层,宽度为屏幕宽,核心为UITableview,且tableviewCell宽度为红色区域宽度。 28 | 29 | #### 注意点 30 | ConfigurationView在CompareDetailView顶部是因为要显示图中灰色sectionHeader, 31 | 此时CompareDetailView被遮盖且不能交互。因此将ConfigurationView里面的视图设置为clearColor,还要重写ConfigurationView的 - (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event 方法,来设置交互区域:图中红色区域。 32 | 33 | ```objc 34 | - (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event { 35 | if (CGPathIsEmpty(self.path.CGPath)) { 36 | return YES; 37 | } else if (CGPathContainsPoint(self.path.CGPath, nil, point, nil)) { 38 | return YES; 39 | } else { 40 | return NO; 41 | } 42 | } 43 | ``` 44 | 45 | 46 | ```objc 47 | // 设置tableView的交互区域 48 | UIBezierPath *path = [UIBezierPath bezierPathWithRect:CGRectMake(0, 0, ITEM_WIDTH, self.bounds.size.height)]; 49 | self.path = path; 50 | ``` 51 | 52 | 53 | ### 结尾 54 | 55 | 本Demo是从项目中拆分整理而来,如有问题欢迎指正,若对你有所帮助,还望star支持下~ 56 | -------------------------------------------------------------------------------- /UITableViewLInkageDemo/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 | -------------------------------------------------------------------------------- /UITableViewLInkageDemo/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 | -------------------------------------------------------------------------------- /UITableViewLInkageDemo/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 | "info" : { 90 | "version" : 1, 91 | "author" : "xcode" 92 | } 93 | } -------------------------------------------------------------------------------- /UITableViewLInkageDemo/Class/Other/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // UITableViewLInkageDemo 4 | // 5 | // Created by Eleven on 2017/7/3. 6 | // Copyright © 2017年 Hawk. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | #import "ViewController.h" 11 | 12 | @interface AppDelegate () 13 | 14 | @end 15 | 16 | @implementation AppDelegate 17 | 18 | 19 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 20 | // Override point for customization after application launch. 21 | self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; 22 | self.window.backgroundColor = [UIColor whiteColor]; 23 | [self.window makeKeyAndVisible]; 24 | 25 | if (@available(iOS 11.0, *)){ 26 | [[UIScrollView appearance] setContentInsetAdjustmentBehavior:UIScrollViewContentInsetAdjustmentNever]; 27 | } 28 | 29 | UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:[ViewController new]]; 30 | self.window.rootViewController = nav; 31 | 32 | return YES; 33 | } 34 | 35 | 36 | - (void)applicationWillResignActive:(UIApplication *)application { 37 | // 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. 38 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. 39 | } 40 | 41 | 42 | - (void)applicationDidEnterBackground:(UIApplication *)application { 43 | // 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. 44 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 45 | } 46 | 47 | 48 | - (void)applicationWillEnterForeground:(UIApplication *)application { 49 | // 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. 50 | } 51 | 52 | 53 | - (void)applicationDidBecomeActive:(UIApplication *)application { 54 | // 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. 55 | } 56 | 57 | 58 | - (void)applicationWillTerminate:(UIApplication *)application { 59 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 60 | } 61 | 62 | 63 | @end 64 | -------------------------------------------------------------------------------- /UITableViewLInkageDemo/Class/View/CompareDetail/CompareCell.m: -------------------------------------------------------------------------------- 1 | // 2 | // CompareCell.m 3 | // UITableViewLInkageDemo 4 | // 5 | // Created by Eleven on 2017/7/3. 6 | // Copyright © 2016年 Hawk. All rights reserved. 7 | // 8 | 9 | #import "CompareCell.h" 10 | #import "CarModel.h" 11 | #import "Header.h" 12 | 13 | @interface CompareItem : UIView 14 | 15 | @property (weak, nonatomic) IBOutlet UILabel *titleLabel; 16 | 17 | + (instancetype)creatView; 18 | 19 | @end 20 | 21 | @implementation CompareItem 22 | 23 | + (instancetype)creatView { 24 | return [[[NSBundle mainBundle] loadNibNamed:@"CompareItem" owner:nil options:nil] objectAtIndex:0]; 25 | } 26 | 27 | @end 28 | 29 | 30 | @interface CompareCell () 31 | 32 | @property (nonatomic, strong) NSMutableArray *compareItems; 33 | 34 | @end 35 | 36 | @implementation CompareCell 37 | 38 | - (void)awakeFromNib { 39 | [super awakeFromNib]; 40 | } 41 | 42 | + (instancetype)cellWithTableView:(UITableView *)tableView { 43 | static NSString * ID = @"CompareCell"; 44 | CompareCell *cell = [tableView dequeueReusableCellWithIdentifier:ID]; 45 | if (!cell) { 46 | cell = [[CompareCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:ID]; 47 | } 48 | cell.selectionStyle = UITableViewCellSelectionStyleNone; 49 | return cell; 50 | } 51 | 52 | - (void)setDatas:(NSArray *)datas withIndex:(NSIndexPath *)indexPath { 53 | if (!self.compareItems.count) { 54 | for (NSInteger i = 0; i < datas.count; i ++) { 55 | CompareItem *item = [CompareItem creatView]; 56 | [self.contentView addSubview:item]; 57 | [self.compareItems addObject:item]; 58 | item.frame = CGRectMake(ITEM_WIDTH * i, 0, ITEM_WIDTH, 40); 59 | } 60 | } else { 61 | if (self.compareItems.count > datas.count) { 62 | for (NSInteger i = self.compareItems.count - 1; i > datas.count - 1; i --) { 63 | [(CompareItem *)self.compareItems[i] removeFromSuperview]; 64 | [self.compareItems removeObject:self.compareItems[i]]; 65 | } 66 | } else if (self.compareItems.count < datas.count) { 67 | for (NSInteger i = self.compareItems.count; i < datas.count; i ++) { 68 | CompareItem *item = [CompareItem creatView]; 69 | [self.contentView addSubview:item]; 70 | [self.compareItems addObject:item]; 71 | item.frame = CGRectMake(ITEM_WIDTH * i, 0, ITEM_WIDTH, 40); 72 | } 73 | } 74 | } 75 | for (NSInteger i = 0; i < datas.count; i ++) { 76 | self.compareItems[i].titleLabel.text = datas[i].groupParamsViewModelList[indexPath.section].paramList[indexPath.row].paramValue; 77 | } 78 | } 79 | 80 | - (NSMutableArray *)compareItems { 81 | if (!_compareItems) { 82 | _compareItems = [NSMutableArray array]; 83 | } 84 | return _compareItems; 85 | } 86 | 87 | @end 88 | -------------------------------------------------------------------------------- /UITableViewLInkageDemo/Class/Other/Header.h: -------------------------------------------------------------------------------- 1 | // 2 | // Header.h 3 | // UITableViewLInkageDemo 4 | // 5 | // Created by Eleven on 2017/7/3. 6 | // Copyright © 2017年 Hawk. All rights reserved. 7 | // 8 | 9 | #ifndef Header_h 10 | #define Header_h 11 | 12 | #define RandColor RGBColor(arc4random_uniform(255), arc4random_uniform(255), arc4random_uniform(255)) 13 | #define SCREEN_WIDTH [UIScreen mainScreen].bounds.size.width 14 | #define SCREEN_HEIGHT [UIScreen mainScreen].bounds.size.height 15 | #define HexColorInt32_t(rgbValue) \ 16 | [UIColor colorWithRed:((float)((0x##rgbValue & 0xFF0000) >> 16))/255.0 green:((float)((0x##rgbValue & 0x00FF00) >> 8))/255.0 blue:((float)(0x##rgbValue & 0x0000FF))/255.0 alpha:1] 17 | 18 | /////////////////////////// 懒加载 ///////////////////////////// 19 | #define ELazyMutableArray(_array) \ 20 | return !(_array) ? (_array) = [NSMutableArray array] : (_array); 21 | 22 | ////////////////////////////////////////////////////////////// 23 | 24 | // 判断是否iPhone X 25 | #define IS_iPhoneX (CGSizeEqualToSize(CGSizeMake(375.f, 812.f), [UIScreen mainScreen].bounds.size) || CGSizeEqualToSize(CGSizeMake(812.f, 375.f), [UIScreen mainScreen].bounds.size) || CGSizeEqualToSize(CGSizeMake(414.f, 896.f), [UIScreen mainScreen].bounds.size) || CGSizeEqualToSize(CGSizeMake(896.f, 414.f), [UIScreen mainScreen].bounds.size)) 26 | 27 | 28 | // status bar height. 29 | #define STATUS_BAR_HEIGHT (IS_iPhoneX ? 44.f : 20.f) 30 | 31 | // Navigation bar height. 32 | #define NAVIGATION_BAR_HEIGHT 44.f 33 | 34 | // Status bar & navigation bar height. 35 | #define STATUS_AND_NAVIGATION_HEIGHT (IS_iPhoneX ? 88.f : 64.f) 36 | 37 | // Tabbar height. 38 | #define TAB_BAR_HEIGHT (IS_iPhoneX ? (49.f + 34.f) : 49.f) 39 | 40 | // Tabbar safe bottom margin. 41 | #define TAB_BAR_SAFE_BOTTOM_MARGIN (IS_iPhoneX ? 34.f : 0.f) 42 | 43 | ///////////////////////////////////////////////////////////////////////////////// 44 | /// 安全运行block 45 | #define BLOCK_SAFE_RUN(block, ...) block ? block(__VA_ARGS__) : nil; 46 | 47 | #ifndef weakify 48 | 49 | #if DEBUG 50 | 51 | #if __has_feature(objc_arc) 52 | 53 | #define weakify(object) autoreleasepool{} __weak __typeof__(object) weak##_##object = object; 54 | 55 | #else 56 | 57 | #define weakify(object) autoreleasepool{} __block __typeof__(object) block##_##object = object; 58 | 59 | #endif 60 | 61 | #else 62 | 63 | #if __has_feature(objc_arc) 64 | 65 | #define weakify(object) try{} @finally{} {} __weak __typeof__(object) weak##_##object = object; 66 | 67 | #else 68 | 69 | #define weakify(object) try{} @finally{} {} __block __typeof__(object) block##_##object = object; 70 | 71 | #endif 72 | 73 | #endif 74 | 75 | #endif 76 | 77 | 78 | 79 | #ifndef strongify 80 | 81 | #if DEBUG 82 | 83 | #if __has_feature(objc_arc) 84 | 85 | #define strongify(object) autoreleasepool{} __typeof__(object) object = weak##_##object; 86 | 87 | #else 88 | 89 | #define strongify(object) autoreleasepool{} __typeof__(object) object = block##_##object; 90 | 91 | #endif 92 | 93 | #else 94 | 95 | #if __has_feature(objc_arc) 96 | 97 | #define strongify(object) try{} @finally{} __typeof__(object) object = weak##_##object; 98 | 99 | #else 100 | 101 | #define strongify(object) try{} @finally{} __typeof__(object) object = block##_##object; 102 | 103 | #endif 104 | 105 | #endif 106 | 107 | #endif 108 | 109 | ////////////////////////////////////////////////////////////// 110 | #define ITEM_WIDTH 94 111 | 112 | #endif /* Header_h */ 113 | -------------------------------------------------------------------------------- /UITableViewLInkageDemo/Class/View/CompareDetail/CompareDetailView.m: -------------------------------------------------------------------------------- 1 | // 2 | // CompareDetailView.m 3 | // UITableViewLInkageDemo 4 | // 5 | // Created by Eleven on 2018/8/13. 6 | // Copyright © 2018年 Hawk. All rights reserved. 7 | // 8 | 9 | #import "CompareDetailView.h" 10 | #import "Header.h" 11 | #import "CarModel.h" 12 | #import "CompareTableHeader.h" 13 | #import "CompareCell.h" 14 | 15 | static CGFloat const kHeaderHeight = 66; 16 | @interface CompareDetailView () 17 | 18 | @property (nonatomic, strong) UIScrollView *scrollVeiw; 19 | @property (nonatomic, copy) NSArray *dataArr; 20 | @property (nonatomic, strong) CompareTableHeader *tableHeader; 21 | @property (nonatomic, assign) NSInteger index; 22 | 23 | @end 24 | 25 | @implementation CompareDetailView 26 | 27 | #pragma mark - 28 | - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { 29 | return self.dataArr.count; 30 | } 31 | 32 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { 33 | return self.dataArr[section].paramList.count; 34 | } 35 | 36 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { 37 | CompareCell *cell = [CompareCell cellWithTableView:tableView]; 38 | [cell setDatas:self.datas withIndex:indexPath]; 39 | return cell; 40 | } 41 | 42 | - (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section { 43 | UIView *sectionHeader = [[UIView alloc] init]; 44 | return sectionHeader; 45 | } 46 | 47 | #pragma mark - UIScrollViewDelegate 48 | - (void)scrollViewDidScroll:(UIScrollView *)scrollView { 49 | if (self.delegate && [self.delegate respondsToSelector:@selector(e_scrollViewDidScroll:)]) { 50 | [self.delegate e_scrollViewDidScroll:scrollView]; 51 | } 52 | } 53 | 54 | #pragma mark - setter/getter 55 | - (void)setDatas:(NSArray *)datas { 56 | _datas = datas; 57 | 58 | self.scrollVeiw.contentSize = CGSizeMake(datas.count * ITEM_WIDTH, 0); 59 | self.tableView.frame = CGRectMake(0, kHeaderHeight, datas.count * ITEM_WIDTH, self.scrollVeiw.bounds.size.height); 60 | self.tableHeader.frame = CGRectMake(0, 0, datas.count * ITEM_WIDTH, kHeaderHeight); 61 | 62 | _dataArr = datas[0].groupParamsViewModelList; 63 | [self.tableHeader setDatas:datas]; 64 | [self.tableView reloadData]; 65 | 66 | } 67 | 68 | - (UITableView *)tableView { 69 | if (!_tableView) { 70 | _tableView = [[UITableView alloc] init]; 71 | _tableView.delegate = self; 72 | _tableView.dataSource = self; 73 | _tableView.rowHeight = 40; 74 | _tableView.sectionHeaderHeight = 30; 75 | _tableView.bounces = NO; 76 | _tableView.showsVerticalScrollIndicator = NO; 77 | _tableView.separatorStyle = UITableViewCellSeparatorStyleNone; 78 | _tableView.backgroundColor = self.backgroundColor; 79 | 80 | [self.scrollVeiw addSubview:_tableView]; 81 | } 82 | return _tableView; 83 | } 84 | 85 | - (CompareTableHeader *)tableHeader { 86 | if (!_tableHeader) { 87 | _tableHeader = [[CompareTableHeader alloc] init]; 88 | [self.scrollVeiw addSubview:_tableHeader]; 89 | } 90 | return _tableHeader; 91 | } 92 | 93 | - (UIScrollView *)scrollVeiw { 94 | if (!_scrollVeiw) { 95 | _scrollVeiw = [[UIScrollView alloc] initWithFrame:self.bounds]; 96 | _scrollVeiw.bounces = NO; 97 | [self addSubview:_scrollVeiw]; 98 | } 99 | return _scrollVeiw; 100 | } 101 | 102 | @end 103 | -------------------------------------------------------------------------------- /UITableViewLInkageDemo/Class/View/Configuration/ConfigurationView.m: -------------------------------------------------------------------------------- 1 | // 2 | // ConfigurationView.m 3 | // UITableViewLInkageDemo 4 | // 5 | // Created by Eleven on 2018/8/13. 6 | // Copyright © 2018年 Hawk. All rights reserved. 7 | // 8 | 9 | #import "ConfigurationView.h" 10 | #import "Header.h" 11 | #import "CompareHeaderView.h" 12 | #import "ConfigCell.h" 13 | #import "ConfigSectionHeaderView.h" 14 | #import "CarModel.h" 15 | 16 | static CGFloat const kHeaderHeight = 66; 17 | 18 | @interface ConfigurationView () 19 | 20 | @property (nonatomic, copy) NSArray *dataArr; 21 | @property (nonatomic, strong) CompareHiddenHeader *hiddenHeader; 22 | 23 | 24 | @end 25 | 26 | @implementation ConfigurationView 27 | 28 | - (instancetype)initWithFrame:(CGRect)frame 29 | { 30 | self = [super initWithFrame:frame]; 31 | if (self) { 32 | self.backgroundColor = [UIColor clearColor]; 33 | 34 | // 设置tableView的交互区域 35 | UIBezierPath *path = [UIBezierPath bezierPathWithRect:CGRectMake(0, 0, ITEM_WIDTH, self.bounds.size.height)]; 36 | self.path = path; 37 | } 38 | return self; 39 | } 40 | 41 | #pragma mark - 42 | - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { 43 | return self.dataArr.count; 44 | } 45 | 46 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { 47 | return self.dataArr[section].paramList.count; 48 | } 49 | 50 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { 51 | ConfigCell *cell = [ConfigCell cellWithTableView:tableView]; 52 | cell.titleLabel.text = self.dataArr[indexPath.section].paramList[indexPath.row].paramName; 53 | cell.backgroundColor = self.backgroundColor; 54 | return cell; 55 | } 56 | 57 | - (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section { 58 | ConfigSectionHeaderView *sectionHeader = [ConfigSectionHeaderView creatView]; 59 | sectionHeader.titleLabel.text = self.dataArr[section].groupName; 60 | return sectionHeader; 61 | } 62 | 63 | - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { 64 | [tableView deselectRowAtIndexPath:indexPath animated:NO]; 65 | 66 | } 67 | 68 | #pragma mark - UIScrollViewDelegate 69 | - (void)scrollViewDidScroll:(UIScrollView *)scrollView { 70 | if (self.delegate && [self.delegate respondsToSelector:@selector(e_scrollViewDidScroll:)]) { 71 | [self.delegate e_scrollViewDidScroll:scrollView]; 72 | } 73 | } 74 | 75 | #pragma mark - setter/getter 76 | - (void)setDatas:(NSArray *)datas { 77 | _datas = datas; 78 | 79 | _dataArr = datas[0].groupParamsViewModelList; 80 | [self.tableView reloadData]; 81 | self.hiddenHeader.backgroundColor = self.backgroundColor; 82 | } 83 | 84 | - (UITableView *)tableView { 85 | if (!_tableView) { 86 | _tableView = [[UITableView alloc] initWithFrame:CGRectMake(0, kHeaderHeight, self.bounds.size.width, self.bounds.size.height - kHeaderHeight)]; 87 | _tableView.delegate = self; 88 | _tableView.dataSource = self; 89 | _tableView.rowHeight = 40; 90 | _tableView.sectionHeaderHeight = 30; 91 | _tableView.bounces = NO; 92 | _tableView.showsVerticalScrollIndicator = NO; 93 | _tableView.separatorStyle = UITableViewCellSeparatorStyleNone; 94 | _tableView.backgroundColor = self.backgroundColor; 95 | 96 | [self addSubview:_tableView]; 97 | } 98 | return _tableView; 99 | } 100 | 101 | - (CompareHiddenHeader *)hiddenHeader { 102 | if (!_hiddenHeader) { 103 | _hiddenHeader = [CompareHiddenHeader creatView]; 104 | _hiddenHeader.frame = CGRectMake(0, 0, ITEM_WIDTH, kHeaderHeight); 105 | _hiddenHeader.hiddenBlock = ^(UIButton *button) { 106 | 107 | }; 108 | [self addSubview:_hiddenHeader]; 109 | } 110 | return _hiddenHeader; 111 | } 112 | 113 | 114 | 115 | @end 116 | -------------------------------------------------------------------------------- /UITableViewLInkageDemo.xcodeproj/xcuserdata/apple.xcuserdatad/xcschemes/UITableViewLInkageDemo.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 42 | 43 | 49 | 50 | 51 | 52 | 53 | 54 | 64 | 66 | 72 | 73 | 74 | 75 | 76 | 77 | 83 | 85 | 91 | 92 | 93 | 94 | 96 | 97 | 100 | 101 | 102 | -------------------------------------------------------------------------------- /UITableViewLInkageDemo/Class/Controller/ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.m 3 | // UITableViewLInkageDemo 4 | // 5 | // Created by Eleven on 2017/7/3. 6 | // Copyright © 2017年 Hawk. All rights reserved. 7 | // 8 | 9 | #import "ViewController.h" 10 | #import "Header.h" 11 | #import "ConfigurationView.h" 12 | #import "CompareDetailView.h" 13 | #import "CarModel.h" 14 | #import "YYModel.h" 15 | 16 | @interface ViewController () 17 | 18 | @property (nonatomic, strong) ConfigurationView *configurationView; 19 | @property (nonatomic, strong) CompareDetailView *compareDetailView; 20 | @property (nonatomic, strong) NSMutableArray *dataArr; 21 | @property (nonatomic, copy) NSArray *sourceArr; 22 | 23 | @end 24 | 25 | @implementation ViewController 26 | 27 | #pragma mark - life cycle 28 | - (void)viewDidLoad { 29 | [super viewDidLoad]; 30 | // Do any additional setup after loading the view. 31 | 32 | self.title = @"参数对比"; 33 | 34 | self.view.backgroundColor = [UIColor whiteColor]; 35 | [self.view addSubview:self.compareDetailView]; 36 | [self.view addSubview:self.configurationView]; 37 | 38 | [self loadData]; 39 | 40 | self.automaticallyAdjustsScrollViewInsets = NO; 41 | } 42 | 43 | - (void)viewDidAppear:(BOOL)animated { 44 | [super viewDidAppear:animated]; 45 | 46 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(addEvent) name:@"addNotification" object:nil]; 47 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(deleteEvent:) name:@"deleteNotification" object:nil]; 48 | } 49 | 50 | - (void)dealloc { 51 | [[NSNotificationCenter defaultCenter] removeObserver:self name:@"addNotification" object:nil]; 52 | [[NSNotificationCenter defaultCenter] removeObserver:self name:@"deleteNotification" object:nil]; 53 | } 54 | 55 | - (void)loadData { 56 | NSData *jsonData = [NSData dataWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"comparedata" ofType:@"json"]]; 57 | NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:jsonData options:0 error:nil]; 58 | self.dataArr = [NSArray yy_modelArrayWithClass:[CarModel class] json:dict[@"data"]].mutableCopy; 59 | 60 | self.sourceArr = [self.dataArr copy]; 61 | 62 | // 加一组空数据 63 | CarModel *model = [[CarModel alloc] init]; 64 | [self.dataArr addObject:model]; 65 | 66 | [self.configurationView setDatas:self.dataArr]; 67 | [self.compareDetailView setDatas:self.dataArr]; 68 | } 69 | 70 | #pragma mark - notification 71 | #pragma mark - 模拟添加与删除数据 72 | - (void)addEvent { 73 | CarModel *model = self.sourceArr[arc4random() % self.sourceArr.count]; 74 | [self.dataArr insertObject:model atIndex:self.dataArr.count - 1]; 75 | [self.configurationView setDatas:self.dataArr]; 76 | [self.compareDetailView setDatas:self.dataArr]; 77 | } 78 | 79 | - (void)deleteEvent:(NSNotification *)notificaiton { 80 | NSInteger index = [notificaiton.userInfo[@"index"] integerValue]; 81 | [self.dataArr removeObjectAtIndex:index]; 82 | [self.configurationView setDatas:self.dataArr]; 83 | [self.compareDetailView setDatas:self.dataArr]; 84 | } 85 | 86 | #pragma mark - EScrollDelegate 87 | #pragma mark - tableveiw联动设置 88 | - (void)e_scrollViewDidScroll:(UIScrollView *)scrollView { 89 | if (scrollView == self.configurationView.tableView) { 90 | self.compareDetailView.tableView.contentOffset = scrollView.contentOffset; 91 | } else { 92 | self.configurationView.tableView.contentOffset = scrollView.contentOffset; 93 | } 94 | } 95 | 96 | #pragma mark - getter 97 | - (ConfigurationView *)configurationView { 98 | if (!_configurationView) { 99 | _configurationView = [[ConfigurationView alloc] initWithFrame:CGRectMake(0, STATUS_AND_NAVIGATION_HEIGHT, SCREEN_WIDTH, SCREEN_HEIGHT - STATUS_AND_NAVIGATION_HEIGHT)]; 100 | _configurationView.delegate = self; 101 | } 102 | return _configurationView; 103 | } 104 | 105 | - (CompareDetailView *)compareDetailView { 106 | if (!_compareDetailView) { 107 | _compareDetailView = [[CompareDetailView alloc] initWithFrame:CGRectMake(ITEM_WIDTH, STATUS_AND_NAVIGATION_HEIGHT, SCREEN_WIDTH - ITEM_WIDTH, SCREEN_HEIGHT - STATUS_AND_NAVIGATION_HEIGHT)]; 108 | _compareDetailView.delegate = self; 109 | } 110 | return _compareDetailView; 111 | } 112 | 113 | - (NSMutableArray *)dataArr { 114 | if (!_dataArr) { 115 | _dataArr = [NSMutableArray array]; 116 | } 117 | return _dataArr; 118 | } 119 | 120 | @end 121 | -------------------------------------------------------------------------------- /UITableViewLInkageDemo/Class/View/CompareDetail/CompareItem.xib: -------------------------------------------------------------------------------- 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 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /UITableViewLInkageDemo/Class/View/Configuration/ConfigCell.xib: -------------------------------------------------------------------------------- 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 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /UITableViewLInkageDemo/Class/View/Configuration/ConfigSectionHeaderView.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | -------------------------------------------------------------------------------- /UITableViewLInkageDemo/Class/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 | -------------------------------------------------------------------------------- /UITableViewLInkageDemo/Class/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 | -------------------------------------------------------------------------------- /UITableViewLInkageDemo/Class/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 | -------------------------------------------------------------------------------- /UITableViewLInkageDemo/Class/View/CompareHeaderView.xib: -------------------------------------------------------------------------------- 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 | 45 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 103 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | -------------------------------------------------------------------------------- /UITableViewLInkageDemo/Class/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 | -------------------------------------------------------------------------------- /UITableViewLInkageDemo.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 18700D5D1F09ED4100F51FB4 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 18700D5C1F09ED4100F51FB4 /* main.m */; }; 11 | 18700D661F09ED4100F51FB4 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 18700D641F09ED4100F51FB4 /* Main.storyboard */; }; 12 | 18700D681F09ED4100F51FB4 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 18700D671F09ED4100F51FB4 /* Assets.xcassets */; }; 13 | 18700D6B1F09ED4100F51FB4 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 18700D691F09ED4100F51FB4 /* LaunchScreen.storyboard */; }; 14 | 18700D761F09ED4100F51FB4 /* UITableViewLInkageDemoUITests.m in Sources */ = {isa = PBXBuildFile; fileRef = 18700D751F09ED4100F51FB4 /* UITableViewLInkageDemoUITests.m */; }; 15 | 18700D941F0A18A300F51FB4 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 18700D841F0A18A300F51FB4 /* ViewController.m */; }; 16 | 18700D951F0A18A300F51FB4 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 18700D881F0A18A300F51FB4 /* AppDelegate.m */; }; 17 | 18700D961F0A18A300F51FB4 /* CompareCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 18700D8C1F0A18A300F51FB4 /* CompareCell.m */; }; 18 | 18700D981F0A18A300F51FB4 /* CompareHeaderView.m in Sources */ = {isa = PBXBuildFile; fileRef = 18700D8F1F0A18A300F51FB4 /* CompareHeaderView.m */; }; 19 | 18700D991F0A18A300F51FB4 /* CompareHeaderView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 18700D901F0A18A300F51FB4 /* CompareHeaderView.xib */; }; 20 | 18700D9A1F0A18A300F51FB4 /* ConfigSectionHeaderView.m in Sources */ = {isa = PBXBuildFile; fileRef = 18700D921F0A18A300F51FB4 /* ConfigSectionHeaderView.m */; }; 21 | 18700D9B1F0A18A300F51FB4 /* ConfigSectionHeaderView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 18700D931F0A18A300F51FB4 /* ConfigSectionHeaderView.xib */; }; 22 | A56862E02122743F00F4D56E /* LICENSE in Resources */ = {isa = PBXBuildFile; fileRef = A56862D82122743F00F4D56E /* LICENSE */; }; 23 | A56862E12122743F00F4D56E /* README.md in Resources */ = {isa = PBXBuildFile; fileRef = A56862D92122743F00F4D56E /* README.md */; }; 24 | A56862E22122743F00F4D56E /* NSObject+YYModel.m in Sources */ = {isa = PBXBuildFile; fileRef = A56862DD2122743F00F4D56E /* NSObject+YYModel.m */; }; 25 | A56862E32122743F00F4D56E /* YYClassInfo.m in Sources */ = {isa = PBXBuildFile; fileRef = A56862DE2122743F00F4D56E /* YYClassInfo.m */; }; 26 | A56862E62122806C00F4D56E /* BackgroundView.m in Sources */ = {isa = PBXBuildFile; fileRef = A56862E52122806C00F4D56E /* BackgroundView.m */; }; 27 | A56862EC2122B8F800F4D56E /* CompareTableView.m in Sources */ = {isa = PBXBuildFile; fileRef = A56862EA2122B8F800F4D56E /* CompareTableView.m */; }; 28 | A58069E2212AA1A6003F79CB /* ConfigCell.m in Sources */ = {isa = PBXBuildFile; fileRef = A58069E1212AA1A6003F79CB /* ConfigCell.m */; }; 29 | A58069E4212AA1BC003F79CB /* ConfigCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = A58069E3212AA1BC003F79CB /* ConfigCell.xib */; }; 30 | A58069E8212AA52D003F79CB /* CompareItem.xib in Resources */ = {isa = PBXBuildFile; fileRef = A58069E7212AA52D003F79CB /* CompareItem.xib */; }; 31 | A58069EB212AB232003F79CB /* CompareTableHeader.m in Sources */ = {isa = PBXBuildFile; fileRef = A58069EA212AB232003F79CB /* CompareTableHeader.m */; }; 32 | A59237F021217ED40055A5B4 /* ConfigurationView.m in Sources */ = {isa = PBXBuildFile; fileRef = A59237EF21217ED40055A5B4 /* ConfigurationView.m */; }; 33 | A59237F321217EEA0055A5B4 /* CompareDetailView.m in Sources */ = {isa = PBXBuildFile; fileRef = A59237F221217EEA0055A5B4 /* CompareDetailView.m */; }; 34 | A59237FA21218BCD0055A5B4 /* CarModel.m in Sources */ = {isa = PBXBuildFile; fileRef = A59237F921218BCD0055A5B4 /* CarModel.m */; }; 35 | A59237FC21218CFE0055A5B4 /* comparedata.json in Resources */ = {isa = PBXBuildFile; fileRef = A59237FB21218CFE0055A5B4 /* comparedata.json */; }; 36 | /* End PBXBuildFile section */ 37 | 38 | /* Begin PBXContainerItemProxy section */ 39 | 18700D721F09ED4100F51FB4 /* PBXContainerItemProxy */ = { 40 | isa = PBXContainerItemProxy; 41 | containerPortal = 18700D501F09ED4100F51FB4 /* Project object */; 42 | proxyType = 1; 43 | remoteGlobalIDString = 18700D571F09ED4100F51FB4; 44 | remoteInfo = UITableViewLInkageDemo; 45 | }; 46 | /* End PBXContainerItemProxy section */ 47 | 48 | /* Begin PBXFileReference section */ 49 | 18700D581F09ED4100F51FB4 /* UITableViewLInkageDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = UITableViewLInkageDemo.app; sourceTree = BUILT_PRODUCTS_DIR; }; 50 | 18700D5C1F09ED4100F51FB4 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 51 | 18700D651F09ED4100F51FB4 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 52 | 18700D671F09ED4100F51FB4 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = ../../Assets.xcassets; sourceTree = ""; }; 53 | 18700D6A1F09ED4100F51FB4 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 54 | 18700D6C1F09ED4100F51FB4 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = Info.plist; path = ../../Info.plist; sourceTree = ""; }; 55 | 18700D711F09ED4100F51FB4 /* UITableViewLInkageDemoUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = UITableViewLInkageDemoUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 56 | 18700D751F09ED4100F51FB4 /* UITableViewLInkageDemoUITests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = UITableViewLInkageDemoUITests.m; sourceTree = ""; }; 57 | 18700D771F09ED4100F51FB4 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 58 | 18700D831F0A18A300F51FB4 /* ViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; 59 | 18700D841F0A18A300F51FB4 /* ViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; 60 | 18700D871F0A18A300F51FB4 /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 61 | 18700D881F0A18A300F51FB4 /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 62 | 18700D891F0A18A300F51FB4 /* Header.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Header.h; sourceTree = ""; }; 63 | 18700D8B1F0A18A300F51FB4 /* CompareCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CompareCell.h; sourceTree = ""; }; 64 | 18700D8C1F0A18A300F51FB4 /* CompareCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CompareCell.m; sourceTree = ""; }; 65 | 18700D8E1F0A18A300F51FB4 /* CompareHeaderView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CompareHeaderView.h; sourceTree = ""; }; 66 | 18700D8F1F0A18A300F51FB4 /* CompareHeaderView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CompareHeaderView.m; sourceTree = ""; }; 67 | 18700D901F0A18A300F51FB4 /* CompareHeaderView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = CompareHeaderView.xib; sourceTree = ""; }; 68 | 18700D911F0A18A300F51FB4 /* ConfigSectionHeaderView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ConfigSectionHeaderView.h; sourceTree = ""; }; 69 | 18700D921F0A18A300F51FB4 /* ConfigSectionHeaderView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ConfigSectionHeaderView.m; sourceTree = ""; }; 70 | 18700D931F0A18A300F51FB4 /* ConfigSectionHeaderView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = ConfigSectionHeaderView.xib; sourceTree = ""; }; 71 | A56862D82122743F00F4D56E /* LICENSE */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = LICENSE; sourceTree = ""; }; 72 | A56862D92122743F00F4D56E /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; 73 | A56862DB2122743F00F4D56E /* YYClassInfo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = YYClassInfo.h; sourceTree = ""; }; 74 | A56862DC2122743F00F4D56E /* YYModel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = YYModel.h; sourceTree = ""; }; 75 | A56862DD2122743F00F4D56E /* NSObject+YYModel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSObject+YYModel.m"; sourceTree = ""; }; 76 | A56862DE2122743F00F4D56E /* YYClassInfo.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = YYClassInfo.m; sourceTree = ""; }; 77 | A56862DF2122743F00F4D56E /* NSObject+YYModel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSObject+YYModel.h"; sourceTree = ""; }; 78 | A56862E42122806C00F4D56E /* BackgroundView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BackgroundView.h; sourceTree = ""; }; 79 | A56862E52122806C00F4D56E /* BackgroundView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BackgroundView.m; sourceTree = ""; }; 80 | A56862E72122834D00F4D56E /* EScrollDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = EScrollDelegate.h; sourceTree = ""; }; 81 | A56862EA2122B8F800F4D56E /* CompareTableView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CompareTableView.m; sourceTree = ""; }; 82 | A56862EB2122B8F800F4D56E /* CompareTableView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CompareTableView.h; sourceTree = ""; }; 83 | A58069E0212AA1A6003F79CB /* ConfigCell.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ConfigCell.h; sourceTree = ""; }; 84 | A58069E1212AA1A6003F79CB /* ConfigCell.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ConfigCell.m; sourceTree = ""; }; 85 | A58069E3212AA1BC003F79CB /* ConfigCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = ConfigCell.xib; sourceTree = ""; }; 86 | A58069E7212AA52D003F79CB /* CompareItem.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = CompareItem.xib; sourceTree = ""; }; 87 | A58069E9212AB232003F79CB /* CompareTableHeader.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CompareTableHeader.h; sourceTree = ""; }; 88 | A58069EA212AB232003F79CB /* CompareTableHeader.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CompareTableHeader.m; sourceTree = ""; }; 89 | A59237EE21217ED40055A5B4 /* ConfigurationView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ConfigurationView.h; sourceTree = ""; }; 90 | A59237EF21217ED40055A5B4 /* ConfigurationView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ConfigurationView.m; sourceTree = ""; }; 91 | A59237F121217EEA0055A5B4 /* CompareDetailView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CompareDetailView.h; sourceTree = ""; }; 92 | A59237F221217EEA0055A5B4 /* CompareDetailView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CompareDetailView.m; sourceTree = ""; }; 93 | A59237F821218BCD0055A5B4 /* CarModel.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CarModel.h; sourceTree = ""; }; 94 | A59237F921218BCD0055A5B4 /* CarModel.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CarModel.m; sourceTree = ""; }; 95 | A59237FB21218CFE0055A5B4 /* comparedata.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = comparedata.json; sourceTree = ""; }; 96 | /* End PBXFileReference section */ 97 | 98 | /* Begin PBXFrameworksBuildPhase section */ 99 | 18700D551F09ED4100F51FB4 /* Frameworks */ = { 100 | isa = PBXFrameworksBuildPhase; 101 | buildActionMask = 2147483647; 102 | files = ( 103 | ); 104 | runOnlyForDeploymentPostprocessing = 0; 105 | }; 106 | 18700D6E1F09ED4100F51FB4 /* Frameworks */ = { 107 | isa = PBXFrameworksBuildPhase; 108 | buildActionMask = 2147483647; 109 | files = ( 110 | ); 111 | runOnlyForDeploymentPostprocessing = 0; 112 | }; 113 | /* End PBXFrameworksBuildPhase section */ 114 | 115 | /* Begin PBXGroup section */ 116 | 18700D4F1F09ED4100F51FB4 = { 117 | isa = PBXGroup; 118 | children = ( 119 | 18700D5A1F09ED4100F51FB4 /* UITableViewLInkageDemo */, 120 | 18700D741F09ED4100F51FB4 /* UITableViewLInkageDemoUITests */, 121 | 18700D591F09ED4100F51FB4 /* Products */, 122 | ); 123 | sourceTree = ""; 124 | }; 125 | 18700D591F09ED4100F51FB4 /* Products */ = { 126 | isa = PBXGroup; 127 | children = ( 128 | 18700D581F09ED4100F51FB4 /* UITableViewLInkageDemo.app */, 129 | 18700D711F09ED4100F51FB4 /* UITableViewLInkageDemoUITests.xctest */, 130 | ); 131 | name = Products; 132 | sourceTree = ""; 133 | }; 134 | 18700D5A1F09ED4100F51FB4 /* UITableViewLInkageDemo */ = { 135 | isa = PBXGroup; 136 | children = ( 137 | 18700D811F0A18A300F51FB4 /* Class */, 138 | 18700D5B1F09ED4100F51FB4 /* Supporting Files */, 139 | ); 140 | path = UITableViewLInkageDemo; 141 | sourceTree = ""; 142 | }; 143 | 18700D5B1F09ED4100F51FB4 /* Supporting Files */ = { 144 | isa = PBXGroup; 145 | children = ( 146 | 18700D5C1F09ED4100F51FB4 /* main.m */, 147 | ); 148 | name = "Supporting Files"; 149 | sourceTree = ""; 150 | }; 151 | 18700D741F09ED4100F51FB4 /* UITableViewLInkageDemoUITests */ = { 152 | isa = PBXGroup; 153 | children = ( 154 | 18700D751F09ED4100F51FB4 /* UITableViewLInkageDemoUITests.m */, 155 | 18700D771F09ED4100F51FB4 /* Info.plist */, 156 | ); 157 | path = UITableViewLInkageDemoUITests; 158 | sourceTree = ""; 159 | }; 160 | 18700D811F0A18A300F51FB4 /* Class */ = { 161 | isa = PBXGroup; 162 | children = ( 163 | A56862D72122743F00F4D56E /* YYModel */, 164 | 18700D821F0A18A300F51FB4 /* Controller */, 165 | A59237F721218BC10055A5B4 /* Model */, 166 | 18700D8A1F0A18A300F51FB4 /* View */, 167 | 18700D861F0A18A300F51FB4 /* Other */, 168 | ); 169 | path = Class; 170 | sourceTree = ""; 171 | }; 172 | 18700D821F0A18A300F51FB4 /* Controller */ = { 173 | isa = PBXGroup; 174 | children = ( 175 | 18700D831F0A18A300F51FB4 /* ViewController.h */, 176 | 18700D841F0A18A300F51FB4 /* ViewController.m */, 177 | ); 178 | path = Controller; 179 | sourceTree = ""; 180 | }; 181 | 18700D861F0A18A300F51FB4 /* Other */ = { 182 | isa = PBXGroup; 183 | children = ( 184 | A56862EB2122B8F800F4D56E /* CompareTableView.h */, 185 | A56862EA2122B8F800F4D56E /* CompareTableView.m */, 186 | A56862E72122834D00F4D56E /* EScrollDelegate.h */, 187 | A59237FB21218CFE0055A5B4 /* comparedata.json */, 188 | 18700D641F09ED4100F51FB4 /* Main.storyboard */, 189 | 18700D671F09ED4100F51FB4 /* Assets.xcassets */, 190 | 18700D691F09ED4100F51FB4 /* LaunchScreen.storyboard */, 191 | 18700D6C1F09ED4100F51FB4 /* Info.plist */, 192 | 18700D871F0A18A300F51FB4 /* AppDelegate.h */, 193 | 18700D881F0A18A300F51FB4 /* AppDelegate.m */, 194 | 18700D891F0A18A300F51FB4 /* Header.h */, 195 | ); 196 | path = Other; 197 | sourceTree = ""; 198 | }; 199 | 18700D8A1F0A18A300F51FB4 /* View */ = { 200 | isa = PBXGroup; 201 | children = ( 202 | 18700D8E1F0A18A300F51FB4 /* CompareHeaderView.h */, 203 | 18700D8F1F0A18A300F51FB4 /* CompareHeaderView.m */, 204 | 18700D901F0A18A300F51FB4 /* CompareHeaderView.xib */, 205 | A56862E82122875E00F4D56E /* Configuration */, 206 | A56862E92122876E00F4D56E /* CompareDetail */, 207 | ); 208 | path = View; 209 | sourceTree = ""; 210 | }; 211 | A56862D72122743F00F4D56E /* YYModel */ = { 212 | isa = PBXGroup; 213 | children = ( 214 | A56862D82122743F00F4D56E /* LICENSE */, 215 | A56862D92122743F00F4D56E /* README.md */, 216 | A56862DA2122743F00F4D56E /* YYModel */, 217 | ); 218 | path = YYModel; 219 | sourceTree = ""; 220 | }; 221 | A56862DA2122743F00F4D56E /* YYModel */ = { 222 | isa = PBXGroup; 223 | children = ( 224 | A56862DB2122743F00F4D56E /* YYClassInfo.h */, 225 | A56862DC2122743F00F4D56E /* YYModel.h */, 226 | A56862DD2122743F00F4D56E /* NSObject+YYModel.m */, 227 | A56862DE2122743F00F4D56E /* YYClassInfo.m */, 228 | A56862DF2122743F00F4D56E /* NSObject+YYModel.h */, 229 | ); 230 | path = YYModel; 231 | sourceTree = ""; 232 | }; 233 | A56862E82122875E00F4D56E /* Configuration */ = { 234 | isa = PBXGroup; 235 | children = ( 236 | A56862E42122806C00F4D56E /* BackgroundView.h */, 237 | A56862E52122806C00F4D56E /* BackgroundView.m */, 238 | A59237EE21217ED40055A5B4 /* ConfigurationView.h */, 239 | A59237EF21217ED40055A5B4 /* ConfigurationView.m */, 240 | A58069E0212AA1A6003F79CB /* ConfigCell.h */, 241 | A58069E1212AA1A6003F79CB /* ConfigCell.m */, 242 | A58069E3212AA1BC003F79CB /* ConfigCell.xib */, 243 | 18700D911F0A18A300F51FB4 /* ConfigSectionHeaderView.h */, 244 | 18700D921F0A18A300F51FB4 /* ConfigSectionHeaderView.m */, 245 | 18700D931F0A18A300F51FB4 /* ConfigSectionHeaderView.xib */, 246 | ); 247 | path = Configuration; 248 | sourceTree = ""; 249 | }; 250 | A56862E92122876E00F4D56E /* CompareDetail */ = { 251 | isa = PBXGroup; 252 | children = ( 253 | A59237F121217EEA0055A5B4 /* CompareDetailView.h */, 254 | A59237F221217EEA0055A5B4 /* CompareDetailView.m */, 255 | A58069E9212AB232003F79CB /* CompareTableHeader.h */, 256 | A58069EA212AB232003F79CB /* CompareTableHeader.m */, 257 | 18700D8B1F0A18A300F51FB4 /* CompareCell.h */, 258 | 18700D8C1F0A18A300F51FB4 /* CompareCell.m */, 259 | A58069E7212AA52D003F79CB /* CompareItem.xib */, 260 | ); 261 | path = CompareDetail; 262 | sourceTree = ""; 263 | }; 264 | A59237F721218BC10055A5B4 /* Model */ = { 265 | isa = PBXGroup; 266 | children = ( 267 | A59237F821218BCD0055A5B4 /* CarModel.h */, 268 | A59237F921218BCD0055A5B4 /* CarModel.m */, 269 | ); 270 | path = Model; 271 | sourceTree = ""; 272 | }; 273 | /* End PBXGroup section */ 274 | 275 | /* Begin PBXNativeTarget section */ 276 | 18700D571F09ED4100F51FB4 /* UITableViewLInkageDemo */ = { 277 | isa = PBXNativeTarget; 278 | buildConfigurationList = 18700D7A1F09ED4100F51FB4 /* Build configuration list for PBXNativeTarget "UITableViewLInkageDemo" */; 279 | buildPhases = ( 280 | 18700D541F09ED4100F51FB4 /* Sources */, 281 | 18700D551F09ED4100F51FB4 /* Frameworks */, 282 | 18700D561F09ED4100F51FB4 /* Resources */, 283 | ); 284 | buildRules = ( 285 | ); 286 | dependencies = ( 287 | ); 288 | name = UITableViewLInkageDemo; 289 | productName = UITableViewLInkageDemo; 290 | productReference = 18700D581F09ED4100F51FB4 /* UITableViewLInkageDemo.app */; 291 | productType = "com.apple.product-type.application"; 292 | }; 293 | 18700D701F09ED4100F51FB4 /* UITableViewLInkageDemoUITests */ = { 294 | isa = PBXNativeTarget; 295 | buildConfigurationList = 18700D7D1F09ED4100F51FB4 /* Build configuration list for PBXNativeTarget "UITableViewLInkageDemoUITests" */; 296 | buildPhases = ( 297 | 18700D6D1F09ED4100F51FB4 /* Sources */, 298 | 18700D6E1F09ED4100F51FB4 /* Frameworks */, 299 | 18700D6F1F09ED4100F51FB4 /* Resources */, 300 | ); 301 | buildRules = ( 302 | ); 303 | dependencies = ( 304 | 18700D731F09ED4100F51FB4 /* PBXTargetDependency */, 305 | ); 306 | name = UITableViewLInkageDemoUITests; 307 | productName = UITableViewLInkageDemoUITests; 308 | productReference = 18700D711F09ED4100F51FB4 /* UITableViewLInkageDemoUITests.xctest */; 309 | productType = "com.apple.product-type.bundle.ui-testing"; 310 | }; 311 | /* End PBXNativeTarget section */ 312 | 313 | /* Begin PBXProject section */ 314 | 18700D501F09ED4100F51FB4 /* Project object */ = { 315 | isa = PBXProject; 316 | attributes = { 317 | LastUpgradeCheck = 0830; 318 | ORGANIZATIONNAME = Hawk; 319 | TargetAttributes = { 320 | 18700D571F09ED4100F51FB4 = { 321 | CreatedOnToolsVersion = 8.3.2; 322 | DevelopmentTeam = Y7UYL5JAB6; 323 | ProvisioningStyle = Automatic; 324 | }; 325 | 18700D701F09ED4100F51FB4 = { 326 | CreatedOnToolsVersion = 8.3.2; 327 | ProvisioningStyle = Automatic; 328 | TestTargetID = 18700D571F09ED4100F51FB4; 329 | }; 330 | }; 331 | }; 332 | buildConfigurationList = 18700D531F09ED4100F51FB4 /* Build configuration list for PBXProject "UITableViewLInkageDemo" */; 333 | compatibilityVersion = "Xcode 3.2"; 334 | developmentRegion = English; 335 | hasScannedForEncodings = 0; 336 | knownRegions = ( 337 | English, 338 | en, 339 | Base, 340 | ); 341 | mainGroup = 18700D4F1F09ED4100F51FB4; 342 | productRefGroup = 18700D591F09ED4100F51FB4 /* Products */; 343 | projectDirPath = ""; 344 | projectRoot = ""; 345 | targets = ( 346 | 18700D571F09ED4100F51FB4 /* UITableViewLInkageDemo */, 347 | 18700D701F09ED4100F51FB4 /* UITableViewLInkageDemoUITests */, 348 | ); 349 | }; 350 | /* End PBXProject section */ 351 | 352 | /* Begin PBXResourcesBuildPhase section */ 353 | 18700D561F09ED4100F51FB4 /* Resources */ = { 354 | isa = PBXResourcesBuildPhase; 355 | buildActionMask = 2147483647; 356 | files = ( 357 | 18700D991F0A18A300F51FB4 /* CompareHeaderView.xib in Resources */, 358 | 18700D9B1F0A18A300F51FB4 /* ConfigSectionHeaderView.xib in Resources */, 359 | A56862E02122743F00F4D56E /* LICENSE in Resources */, 360 | A58069E8212AA52D003F79CB /* CompareItem.xib in Resources */, 361 | 18700D6B1F09ED4100F51FB4 /* LaunchScreen.storyboard in Resources */, 362 | 18700D681F09ED4100F51FB4 /* Assets.xcassets in Resources */, 363 | A59237FC21218CFE0055A5B4 /* comparedata.json in Resources */, 364 | A56862E12122743F00F4D56E /* README.md in Resources */, 365 | 18700D661F09ED4100F51FB4 /* Main.storyboard in Resources */, 366 | A58069E4212AA1BC003F79CB /* ConfigCell.xib in Resources */, 367 | ); 368 | runOnlyForDeploymentPostprocessing = 0; 369 | }; 370 | 18700D6F1F09ED4100F51FB4 /* Resources */ = { 371 | isa = PBXResourcesBuildPhase; 372 | buildActionMask = 2147483647; 373 | files = ( 374 | ); 375 | runOnlyForDeploymentPostprocessing = 0; 376 | }; 377 | /* End PBXResourcesBuildPhase section */ 378 | 379 | /* Begin PBXSourcesBuildPhase section */ 380 | 18700D541F09ED4100F51FB4 /* Sources */ = { 381 | isa = PBXSourcesBuildPhase; 382 | buildActionMask = 2147483647; 383 | files = ( 384 | A58069EB212AB232003F79CB /* CompareTableHeader.m in Sources */, 385 | A56862E62122806C00F4D56E /* BackgroundView.m in Sources */, 386 | 18700D941F0A18A300F51FB4 /* ViewController.m in Sources */, 387 | A58069E2212AA1A6003F79CB /* ConfigCell.m in Sources */, 388 | A59237F021217ED40055A5B4 /* ConfigurationView.m in Sources */, 389 | 18700D9A1F0A18A300F51FB4 /* ConfigSectionHeaderView.m in Sources */, 390 | A56862E22122743F00F4D56E /* NSObject+YYModel.m in Sources */, 391 | A56862EC2122B8F800F4D56E /* CompareTableView.m in Sources */, 392 | A59237F321217EEA0055A5B4 /* CompareDetailView.m in Sources */, 393 | 18700D5D1F09ED4100F51FB4 /* main.m in Sources */, 394 | 18700D981F0A18A300F51FB4 /* CompareHeaderView.m in Sources */, 395 | 18700D951F0A18A300F51FB4 /* AppDelegate.m in Sources */, 396 | A56862E32122743F00F4D56E /* YYClassInfo.m in Sources */, 397 | A59237FA21218BCD0055A5B4 /* CarModel.m in Sources */, 398 | 18700D961F0A18A300F51FB4 /* CompareCell.m in Sources */, 399 | ); 400 | runOnlyForDeploymentPostprocessing = 0; 401 | }; 402 | 18700D6D1F09ED4100F51FB4 /* Sources */ = { 403 | isa = PBXSourcesBuildPhase; 404 | buildActionMask = 2147483647; 405 | files = ( 406 | 18700D761F09ED4100F51FB4 /* UITableViewLInkageDemoUITests.m in Sources */, 407 | ); 408 | runOnlyForDeploymentPostprocessing = 0; 409 | }; 410 | /* End PBXSourcesBuildPhase section */ 411 | 412 | /* Begin PBXTargetDependency section */ 413 | 18700D731F09ED4100F51FB4 /* PBXTargetDependency */ = { 414 | isa = PBXTargetDependency; 415 | target = 18700D571F09ED4100F51FB4 /* UITableViewLInkageDemo */; 416 | targetProxy = 18700D721F09ED4100F51FB4 /* PBXContainerItemProxy */; 417 | }; 418 | /* End PBXTargetDependency section */ 419 | 420 | /* Begin PBXVariantGroup section */ 421 | 18700D641F09ED4100F51FB4 /* Main.storyboard */ = { 422 | isa = PBXVariantGroup; 423 | children = ( 424 | 18700D651F09ED4100F51FB4 /* Base */, 425 | ); 426 | name = Main.storyboard; 427 | path = ../..; 428 | sourceTree = ""; 429 | }; 430 | 18700D691F09ED4100F51FB4 /* LaunchScreen.storyboard */ = { 431 | isa = PBXVariantGroup; 432 | children = ( 433 | 18700D6A1F09ED4100F51FB4 /* Base */, 434 | ); 435 | name = LaunchScreen.storyboard; 436 | path = ../..; 437 | sourceTree = ""; 438 | }; 439 | /* End PBXVariantGroup section */ 440 | 441 | /* Begin XCBuildConfiguration section */ 442 | 18700D781F09ED4100F51FB4 /* Debug */ = { 443 | isa = XCBuildConfiguration; 444 | buildSettings = { 445 | ALWAYS_SEARCH_USER_PATHS = NO; 446 | CLANG_ANALYZER_NONNULL = YES; 447 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 448 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 449 | CLANG_CXX_LIBRARY = "libc++"; 450 | CLANG_ENABLE_MODULES = YES; 451 | CLANG_ENABLE_OBJC_ARC = YES; 452 | CLANG_WARN_BOOL_CONVERSION = YES; 453 | CLANG_WARN_CONSTANT_CONVERSION = YES; 454 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 455 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 456 | CLANG_WARN_EMPTY_BODY = YES; 457 | CLANG_WARN_ENUM_CONVERSION = YES; 458 | CLANG_WARN_INFINITE_RECURSION = YES; 459 | CLANG_WARN_INT_CONVERSION = YES; 460 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 461 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 462 | CLANG_WARN_UNREACHABLE_CODE = YES; 463 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 464 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 465 | COPY_PHASE_STRIP = NO; 466 | DEBUG_INFORMATION_FORMAT = dwarf; 467 | ENABLE_STRICT_OBJC_MSGSEND = YES; 468 | ENABLE_TESTABILITY = YES; 469 | GCC_C_LANGUAGE_STANDARD = gnu99; 470 | GCC_DYNAMIC_NO_PIC = NO; 471 | GCC_NO_COMMON_BLOCKS = YES; 472 | GCC_OPTIMIZATION_LEVEL = 0; 473 | GCC_PREPROCESSOR_DEFINITIONS = ( 474 | "DEBUG=1", 475 | "$(inherited)", 476 | ); 477 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 478 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 479 | GCC_WARN_UNDECLARED_SELECTOR = YES; 480 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 481 | GCC_WARN_UNUSED_FUNCTION = YES; 482 | GCC_WARN_UNUSED_VARIABLE = YES; 483 | IPHONEOS_DEPLOYMENT_TARGET = 9.2; 484 | MTL_ENABLE_DEBUG_INFO = YES; 485 | ONLY_ACTIVE_ARCH = YES; 486 | SDKROOT = iphoneos; 487 | TARGETED_DEVICE_FAMILY = "1,2"; 488 | }; 489 | name = Debug; 490 | }; 491 | 18700D791F09ED4100F51FB4 /* Release */ = { 492 | isa = XCBuildConfiguration; 493 | buildSettings = { 494 | ALWAYS_SEARCH_USER_PATHS = NO; 495 | CLANG_ANALYZER_NONNULL = YES; 496 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 497 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 498 | CLANG_CXX_LIBRARY = "libc++"; 499 | CLANG_ENABLE_MODULES = YES; 500 | CLANG_ENABLE_OBJC_ARC = YES; 501 | CLANG_WARN_BOOL_CONVERSION = YES; 502 | CLANG_WARN_CONSTANT_CONVERSION = YES; 503 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 504 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 505 | CLANG_WARN_EMPTY_BODY = YES; 506 | CLANG_WARN_ENUM_CONVERSION = YES; 507 | CLANG_WARN_INFINITE_RECURSION = YES; 508 | CLANG_WARN_INT_CONVERSION = YES; 509 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 510 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 511 | CLANG_WARN_UNREACHABLE_CODE = YES; 512 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 513 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 514 | COPY_PHASE_STRIP = NO; 515 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 516 | ENABLE_NS_ASSERTIONS = NO; 517 | ENABLE_STRICT_OBJC_MSGSEND = YES; 518 | GCC_C_LANGUAGE_STANDARD = gnu99; 519 | GCC_NO_COMMON_BLOCKS = YES; 520 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 521 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 522 | GCC_WARN_UNDECLARED_SELECTOR = YES; 523 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 524 | GCC_WARN_UNUSED_FUNCTION = YES; 525 | GCC_WARN_UNUSED_VARIABLE = YES; 526 | IPHONEOS_DEPLOYMENT_TARGET = 9.2; 527 | MTL_ENABLE_DEBUG_INFO = NO; 528 | SDKROOT = iphoneos; 529 | TARGETED_DEVICE_FAMILY = "1,2"; 530 | VALIDATE_PRODUCT = YES; 531 | }; 532 | name = Release; 533 | }; 534 | 18700D7B1F09ED4100F51FB4 /* Debug */ = { 535 | isa = XCBuildConfiguration; 536 | buildSettings = { 537 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 538 | DEVELOPMENT_TEAM = Y7UYL5JAB6; 539 | GCC_PREFIX_HEADER = ""; 540 | INFOPLIST_FILE = UITableViewLInkageDemo/Info.plist; 541 | IPHONEOS_DEPLOYMENT_TARGET = 9.2; 542 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 543 | PRODUCT_BUNDLE_IDENTIFIER = com.eleven.UITableViewLInkage; 544 | PRODUCT_NAME = "$(TARGET_NAME)"; 545 | }; 546 | name = Debug; 547 | }; 548 | 18700D7C1F09ED4100F51FB4 /* Release */ = { 549 | isa = XCBuildConfiguration; 550 | buildSettings = { 551 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 552 | DEVELOPMENT_TEAM = Y7UYL5JAB6; 553 | GCC_PREFIX_HEADER = ""; 554 | INFOPLIST_FILE = UITableViewLInkageDemo/Info.plist; 555 | IPHONEOS_DEPLOYMENT_TARGET = 9.2; 556 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 557 | PRODUCT_BUNDLE_IDENTIFIER = com.eleven.UITableViewLInkage; 558 | PRODUCT_NAME = "$(TARGET_NAME)"; 559 | }; 560 | name = Release; 561 | }; 562 | 18700D7E1F09ED4100F51FB4 /* Debug */ = { 563 | isa = XCBuildConfiguration; 564 | buildSettings = { 565 | INFOPLIST_FILE = UITableViewLInkageDemoUITests/Info.plist; 566 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 567 | PRODUCT_BUNDLE_IDENTIFIER = com.eleven.UITableViewLInkageDemoUITests; 568 | PRODUCT_NAME = "$(TARGET_NAME)"; 569 | TEST_TARGET_NAME = UITableViewLInkageDemo; 570 | }; 571 | name = Debug; 572 | }; 573 | 18700D7F1F09ED4100F51FB4 /* Release */ = { 574 | isa = XCBuildConfiguration; 575 | buildSettings = { 576 | INFOPLIST_FILE = UITableViewLInkageDemoUITests/Info.plist; 577 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 578 | PRODUCT_BUNDLE_IDENTIFIER = com.eleven.UITableViewLInkageDemoUITests; 579 | PRODUCT_NAME = "$(TARGET_NAME)"; 580 | TEST_TARGET_NAME = UITableViewLInkageDemo; 581 | }; 582 | name = Release; 583 | }; 584 | /* End XCBuildConfiguration section */ 585 | 586 | /* Begin XCConfigurationList section */ 587 | 18700D531F09ED4100F51FB4 /* Build configuration list for PBXProject "UITableViewLInkageDemo" */ = { 588 | isa = XCConfigurationList; 589 | buildConfigurations = ( 590 | 18700D781F09ED4100F51FB4 /* Debug */, 591 | 18700D791F09ED4100F51FB4 /* Release */, 592 | ); 593 | defaultConfigurationIsVisible = 0; 594 | defaultConfigurationName = Release; 595 | }; 596 | 18700D7A1F09ED4100F51FB4 /* Build configuration list for PBXNativeTarget "UITableViewLInkageDemo" */ = { 597 | isa = XCConfigurationList; 598 | buildConfigurations = ( 599 | 18700D7B1F09ED4100F51FB4 /* Debug */, 600 | 18700D7C1F09ED4100F51FB4 /* Release */, 601 | ); 602 | defaultConfigurationIsVisible = 0; 603 | defaultConfigurationName = Release; 604 | }; 605 | 18700D7D1F09ED4100F51FB4 /* Build configuration list for PBXNativeTarget "UITableViewLInkageDemoUITests" */ = { 606 | isa = XCConfigurationList; 607 | buildConfigurations = ( 608 | 18700D7E1F09ED4100F51FB4 /* Debug */, 609 | 18700D7F1F09ED4100F51FB4 /* Release */, 610 | ); 611 | defaultConfigurationIsVisible = 0; 612 | defaultConfigurationName = Release; 613 | }; 614 | /* End XCConfigurationList section */ 615 | }; 616 | rootObject = 18700D501F09ED4100F51FB4 /* Project object */; 617 | } 618 | --------------------------------------------------------------------------------