├── CCLKLineChartView ├── Tool │ ├── CCLFrameTool.h │ └── CCLFrameTool.m ├── kLineChart │ ├── CCLKLineAboveCell.h │ ├── CCLKLineAboveCell.m │ ├── CCLKLineAboveView.h │ ├── CCLKLineAboveView.m │ ├── CCLKLineBelowCell.h │ ├── CCLKLineBelowCell.m │ ├── CCLKLineBelowView.h │ ├── CCLKLineBelowView.m │ ├── CCLKLineChartView.h │ ├── CCLKLineChartView.m │ ├── CCLKLineData.h │ ├── CCLKLineData.m │ ├── CCLKLineShareData.h │ └── CCLKLineShareData.m └── timeChart │ ├── CCLTimeAboveView.h │ ├── CCLTimeAboveView.m │ ├── CCLTimeBelowView.h │ ├── CCLTimeBelowView.m │ ├── CCLTimeModel.h │ ├── CCLTimeModel.m │ ├── CCLTimeShareData.h │ ├── CCLTimeShareData.m │ ├── CCLTimeView.h │ └── CCLTimeView.m ├── CCLKLineChartViewDemo ├── CCLKLineChartViewDemo.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcuserdata │ │ │ └── Crisps_13.xcuserdatad │ │ │ └── UserInterfaceState.xcuserstate │ └── xcuserdata │ │ └── Crisps_13.xcuserdatad │ │ └── xcschemes │ │ ├── CCLKLineChartViewDemo.xcscheme │ │ └── xcschememanagement.plist ├── CCLKLineChartViewDemo │ ├── AppDelegate.h │ ├── AppDelegate.m │ ├── Assets.xcassets │ │ └── AppIcon.appiconset │ │ │ └── Contents.json │ ├── Base.lproj │ │ ├── LaunchScreen.storyboard │ │ └── Main.storyboard │ ├── Info.plist │ ├── MJExtension │ │ ├── MJExtension.h │ │ ├── MJExtensionConst.h │ │ ├── MJExtensionConst.m │ │ ├── MJFoundation.h │ │ ├── MJFoundation.m │ │ ├── MJProperty.h │ │ ├── MJProperty.m │ │ ├── MJPropertyKey.h │ │ ├── MJPropertyKey.m │ │ ├── MJPropertyType.h │ │ ├── MJPropertyType.m │ │ ├── NSObject+MJClass.h │ │ ├── NSObject+MJClass.m │ │ ├── NSObject+MJCoding.h │ │ ├── NSObject+MJCoding.m │ │ ├── NSObject+MJKeyValue.h │ │ ├── NSObject+MJKeyValue.m │ │ ├── NSObject+MJProperty.h │ │ ├── NSObject+MJProperty.m │ │ ├── NSString+MJExtension.h │ │ └── NSString+MJExtension.m │ ├── ViewController.h │ ├── ViewController.m │ ├── data.plist │ ├── main.m │ ├── stockData.plist │ └── timeData.plist ├── CCLKLineChartViewDemoTests │ ├── CCLKLineChartViewDemoTests.m │ └── Info.plist └── CCLKLineChartViewDemoUITests │ ├── CCLKLineChartViewDemoUITests.m │ └── Info.plist └── README.md /CCLKLineChartView/Tool/CCLFrameTool.h: -------------------------------------------------------------------------------- 1 | // 2 | // CCLFrameTool.h 3 | // KlineChart 4 | // 5 | // Created by Crisps on 16/8/29. 6 | // Copyright © 2016年 cclion.cc. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @interface CCLFrameTool : NSObject 13 | 14 | /** 15 | * 计算位置坐标(居左边距) 16 | * 17 | * @param value 数值 18 | * @param rangeB 范围(极大) 19 | * @param rangeSma 范围(极小) 20 | * @param length 实际屏幕显示宽度 21 | * 22 | * @return 位置坐标(居左边距) 23 | */ 24 | + (CGFloat)getLeftForValue:(CGFloat)value rangeBig:(CGFloat)rangeBig rangeSma:(CGFloat)rangeSma andLength:(CGFloat)length; 25 | 26 | 27 | /** 28 | * 计算位置坐标(居右边距) 29 | * 30 | * @param value 数值 31 | * @param rangeB 范围(极大) 32 | * @param rangeSma 范围(极小) 33 | * @param length 实际屏幕显示宽度 34 | * 35 | * @return 位置坐标(居左边距) 36 | */ 37 | + (CGFloat)getRightForValue:(CGFloat)value rangeBig:(CGFloat)rangeBig rangeSma:(CGFloat)rangeSma andLength:(CGFloat)length; 38 | 39 | 40 | /** 41 | * 计算位置坐标(宽度) 42 | * 43 | * @param value 数值 44 | * @param rangeBig 范围(极大) 45 | * @param rangeSma 范围(极小) 46 | * @param length 实际屏幕显示宽度 47 | * 48 | * @return 位置宽度 49 | */ 50 | + (CGFloat)getWidthForValue:(CGFloat)value rangeBig:(CGFloat)rangeBig rangeSma:(CGFloat)rangeSma andLength:(CGFloat)length; 51 | 52 | 53 | 54 | 55 | /** 56 | * 计算位置坐标(居左边距)有边界线的情况 57 | * 58 | * @param value 数值 59 | * @param rangeBig 范围(极大) 60 | * @param rangeSma 范围(极小) 61 | * @param length 实际屏幕显示宽度 62 | * @param length 边界线 63 | * 64 | * @return 位置宽度 65 | */ 66 | + (CGFloat)getLeftForValue:(CGFloat)value rangeBig:(CGFloat)rangeBig rangeSma:(CGFloat)rangeSma length:(CGFloat)length andGap:(CGFloat)gap; 67 | 68 | 69 | /** 70 | * 计算位置坐标(居右边距)有边界线的情况 71 | * 72 | * @param value 数值 73 | * @param rangeBig 范围(极大) 74 | * @param rangeSma 范围(极小) 75 | * @param length 实际屏幕显示宽度 76 | * @param length 边界线 77 | * 78 | * @return 位置宽度 79 | */ 80 | + (CGFloat)getRightForValue:(CGFloat)value rangeBig:(CGFloat)rangeBig rangeSma:(CGFloat)rangeSma length:(CGFloat)length andGap:(CGFloat)gap; 81 | 82 | 83 | /** 84 | * 计算位置坐标(宽度))有边界线的情况 85 | * 86 | * @param value 数值 87 | * @param rangeBig 范围(极大) 88 | * @param rangeSma 范围(极小) 89 | * @param length 实际屏幕显示宽度 90 | * @param length 边界线 91 | * 92 | * @return 位置宽度 93 | */ 94 | + (CGFloat)getWidthForValue:(CGFloat)value rangeBig:(CGFloat)rangeBig rangeSma:(CGFloat)rangeSma length:(CGFloat)length andGap:(CGFloat)gap; 95 | 96 | /** 97 | * 获取-90旋转前的坐标 98 | * 99 | * @param rectNew 现在的坐标 100 | * 101 | * @return 本质坐标 102 | */ 103 | + (CGRect)getTransBefore:(CGRect)rectNew; 104 | 105 | /** 106 | * 获取-90旋转前的坐标 107 | * 108 | * @param rectNew 现在的坐标 109 | * 110 | * @return 本质坐标 111 | */ 112 | + (CGPoint)getOriginBefore:(CGRect)rectNew; 113 | 114 | /** 115 | * 计算位置坐标(时间线)有边界线的情况 116 | * 117 | * @param value 数值 118 | * @param index 位置 119 | * @param width 父视图宽度 120 | * @param rangeBig 范围(极大) 121 | * @param rangeSma 范围(极小) 122 | * @param length 实际屏幕显示宽度 123 | * @param length 边界线 124 | * 125 | * @return 位置宽度 126 | */ 127 | + (CGPoint)getPointForValue:(CGFloat)value index:(NSUInteger)index width:(CGFloat)width rangeBig:(CGFloat)rangeBig rangeSma:(CGFloat)rangeSma length:(CGFloat)length andGap:(CGFloat)gap; 128 | 129 | /** 130 | * 计算位置坐标(时间线下方交易量)无边界线 131 | * 132 | * @param value 数值 133 | * @param index 位置 134 | * @param width 父视图宽度 135 | * @param rangeBig 范围(极大) 136 | * @param rangeSma 范围(极小) 137 | * @param length 实际屏幕显示宽度 138 | * 139 | * @return 位置宽度 140 | */ 141 | + (CGRect)getRectForValue:(CGFloat)value index:(NSUInteger)index width:(CGFloat)width rangeBig:(CGFloat)rangeBig rangeSma:(CGFloat)rangeSma andLength:(CGFloat)length; 142 | 143 | 144 | @end 145 | -------------------------------------------------------------------------------- /CCLKLineChartView/Tool/CCLFrameTool.m: -------------------------------------------------------------------------------- 1 | // 2 | // CCLFrameTool.m 3 | // KlineChart 4 | // 5 | // Created by Crisps on 16/8/29. 6 | // Copyright © 2016年 cclion.cc. All rights reserved. 7 | // 8 | 9 | #import "CCLFrameTool.h" 10 | 11 | @implementation CCLFrameTool 12 | 13 | + (CGFloat)getLeftForValue:(CGFloat)value rangeBig:(CGFloat)rangeBig rangeSma:(CGFloat)rangeSma andLength:(CGFloat)length{ 14 | 15 | CGFloat left = (rangeBig - value) / (rangeBig - rangeSma) * length; 16 | return left; 17 | } 18 | 19 | + (CGFloat)getRightForValue:(CGFloat)value rangeBig:(CGFloat)rangeBig rangeSma:(CGFloat)rangeSma andLength:(CGFloat)length{ 20 | CGFloat right = (value - rangeSma) / (rangeBig - rangeSma) * length; 21 | return right; 22 | } 23 | 24 | + (CGFloat)getWidthForValue:(CGFloat)value rangeBig:(CGFloat)rangeBig rangeSma:(CGFloat)rangeSma andLength:(CGFloat)length{ 25 | 26 | CGFloat width = value / (rangeBig - rangeSma) * length; 27 | return width; 28 | } 29 | 30 | + (CGFloat)getLeftForValue:(CGFloat)value rangeBig:(CGFloat)rangeBig rangeSma:(CGFloat)rangeSma length:(CGFloat)length andGap:(CGFloat)gap{ 31 | CGFloat left = (rangeBig - value) / (rangeBig - rangeSma) * (length - 2 * gap) + gap; 32 | return left; 33 | } 34 | 35 | + (CGFloat)getRightForValue:(CGFloat)value rangeBig:(CGFloat)rangeBig rangeSma:(CGFloat)rangeSma length:(CGFloat)length andGap:(CGFloat)gap{ 36 | CGFloat left = (rangeBig - value) / (rangeBig - rangeSma) * (length - 2 * gap) + gap; 37 | return left; 38 | } 39 | 40 | + (CGFloat)getWidthForValue:(CGFloat)value rangeBig:(CGFloat)rangeBig rangeSma:(CGFloat)rangeSma length:(CGFloat)length andGap:(CGFloat)gap{ 41 | CGFloat width = value / (rangeBig - rangeSma) * (length - 2 * gap); 42 | return width; 43 | } 44 | 45 | + (CGRect)getTransBefore:(CGRect)rectNew{ 46 | 47 | CGFloat x = rectNew.origin.x; 48 | CGFloat y = rectNew.origin.y; 49 | CGFloat w= rectNew.size.width; 50 | CGFloat h = rectNew.size.height; 51 | 52 | return CGRectMake(x + w * 0.5 - h * 0.5, y + h * 0.5 - w * 0.5, w, h); 53 | 54 | } 55 | 56 | + (CGPoint)getOriginBefore:(CGRect)rectNew{ 57 | CGFloat x = rectNew.origin.x; 58 | CGFloat y = rectNew.origin.y; 59 | CGFloat w= rectNew.size.width; 60 | CGFloat h = rectNew.size.height; 61 | 62 | return CGPointMake(x + w * 0.5 + h * 0.5, y + h * 0.5 - w * 0.5); 63 | } 64 | 65 | + (CGPoint)getPointForValue:(CGFloat)value index:(NSUInteger)index width:(CGFloat)width rangeBig:(CGFloat)rangeBig rangeSma:(CGFloat)rangeSma length:(CGFloat)length andGap:(CGFloat)gap{ 66 | 67 | CGFloat itemW = width / 241; 68 | CGFloat x = itemW * 0.5 + itemW * index; 69 | CGFloat y = [self getRightForValue:value rangeBig:rangeBig rangeSma:rangeSma length:length andGap:gap]; 70 | 71 | return CGPointMake(x, y); 72 | } 73 | 74 | + (CGRect)getRectForValue:(CGFloat)value index:(NSUInteger)index width:(CGFloat)width rangeBig:(CGFloat)rangeBig rangeSma:(CGFloat)rangeSma andLength:(CGFloat)length{ 75 | 76 | CGFloat itemW = width / 241; 77 | CGFloat gap = itemW * 0.1; 78 | 79 | CGFloat x = gap + itemW * index; 80 | 81 | CGFloat y = [self getRightForValue:value rangeBig:rangeBig rangeSma:rangeSma length:length andGap:0]; 82 | CGFloat h = length - y; 83 | CGFloat w = itemW - gap * 2; 84 | 85 | return CGRectMake(x, y, w, h); 86 | } 87 | 88 | 89 | @end 90 | -------------------------------------------------------------------------------- /CCLKLineChartView/kLineChart/CCLKLineAboveCell.h: -------------------------------------------------------------------------------- 1 | // 2 | // CCLKLineAboveCell.h 3 | // KlineChart 4 | // 5 | // Created by Crisps on 16/9/6. 6 | // Copyright © 2016年 cclion.cc. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "CCLKLineData.h" 11 | #import "CCLKLineShareData.h" 12 | 13 | @interface CCLKLineAboveCell : UITableViewCell 14 | 15 | @property (nonatomic, strong) CCLKLineData *data; 16 | 17 | @property (nonatomic, strong) CCLKLineData *lastData; // 可选数据,展示均线 18 | 19 | @property (nonatomic, strong) CCLKLineData *nextData; // 可选数据,展示均线 20 | 21 | 22 | @property (nonatomic, strong) CATextLayer *textLayer; 23 | 24 | @property (nonatomic, assign) BOOL isShowDay; 25 | 26 | 27 | @property (nonatomic, strong) CCLKLineShareData *shareData; // 必须最先赋值 28 | 29 | - (void)setData:(CCLKLineData *)data max:(CGFloat )max andMin:(CGFloat)min; 30 | 31 | - (void)setindex:(NSUInteger)row; 32 | 33 | 34 | 35 | 36 | @end 37 | -------------------------------------------------------------------------------- /CCLKLineChartView/kLineChart/CCLKLineAboveCell.m: -------------------------------------------------------------------------------- 1 | // 2 | // CCLKLineAboveCell.m 3 | // KlineChart 4 | // 5 | // Created by Crisps on 16/9/6. 6 | // Copyright © 2016年 cclion.cc. All rights reserved. 7 | // 8 | 9 | #import "CCLKLineAboveCell.h" 10 | #import "CCLFrameTool.h" 11 | 12 | @interface CCLKLineAboveCell () 13 | 14 | /** 15 | * 柱状图 16 | */ 17 | @property (nonatomic, strong) CAShapeLayer *pillarLayer; 18 | /** 19 | * 线图 20 | */ 21 | @property (nonatomic, strong) CAShapeLayer *lineLayer; 22 | 23 | /** 24 | * 记录位置 25 | */ 26 | @property (nonatomic, strong) UILabel *indexLabel; 27 | 28 | /** 29 | * 均线5分钟 接上 30 | */ 31 | @property (nonatomic, strong) CAShapeLayer *avg_5_up; 32 | 33 | /** 34 | * 均线5分钟 接下 35 | */ 36 | @property (nonatomic, strong) CAShapeLayer *avg_5_down; 37 | 38 | /** 39 | * 均线10分钟 接上 40 | */ 41 | @property (nonatomic, strong) CAShapeLayer *avg_10_up; 42 | 43 | /** 44 | * 均线10分钟 接下 45 | */ 46 | @property (nonatomic, strong) CAShapeLayer *avg_10_down; 47 | 48 | /** 49 | * 均线20分钟 接上 50 | */ 51 | @property (nonatomic, strong) CAShapeLayer *avg_20_up; 52 | 53 | /** 54 | * 均线20分钟 接下 55 | */ 56 | @property (nonatomic, strong) CAShapeLayer *avg_20_down; 57 | 58 | 59 | /** 60 | * 标志线 61 | */ 62 | @property (nonatomic, strong) CAShapeLayer *markLayer; 63 | 64 | /** 65 | * 当前界面最大值、最小值 (转shareData) 66 | */ 67 | @property (nonatomic, assign) CGFloat visibleMax; 68 | 69 | @property (nonatomic, assign) CGFloat visibleMin; 70 | 71 | @property (nonatomic, assign) CGFloat visibleGap; 72 | 73 | @property (nonatomic, assign) CGFloat currHeight; 74 | 75 | @property (nonatomic, assign) CGFloat currWidth; 76 | 77 | @end 78 | 79 | @implementation CCLKLineAboveCell 80 | 81 | 82 | - (UILabel *)indexLabel{ 83 | if (_indexLabel == nil) { 84 | _indexLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 50, 20)]; 85 | _indexLabel.textColor = [UIColor blackColor]; 86 | } 87 | return _indexLabel; 88 | } 89 | -(CATextLayer *)textLayer{ 90 | if (_textLayer == nil) { 91 | _textLayer = [CATextLayer layer]; 92 | _textLayer.frame = self.contentView.bounds; 93 | _textLayer.foregroundColor = [UIColor blackColor].CGColor; 94 | _textLayer.alignmentMode = kCAAlignmentJustified; 95 | UIFont *font = [UIFont systemFontOfSize:9]; 96 | CFStringRef fontName = (__bridge CFStringRef)font.fontName; 97 | CGFontRef fontRef = CGFontCreateWithFontName(fontName); 98 | _textLayer.font = fontRef; 99 | _textLayer.fontSize = font.pointSize; 100 | CGFontRelease(fontRef); 101 | _textLayer.contentsScale = [UIScreen mainScreen].scale; 102 | 103 | } 104 | return _textLayer; 105 | } 106 | - (CGFloat)visibleMax{ 107 | return self.shareData.aboveMax; 108 | } 109 | 110 | - (CGFloat)visibleMin{ 111 | return self.shareData.abovemin; 112 | } 113 | 114 | - (CGFloat)visibleGap{ 115 | return self.shareData.gap; 116 | } 117 | 118 | - (CGFloat)currHeight{ 119 | return self.shareData.currHeight; 120 | } 121 | 122 | -(CGFloat)currWidth{ 123 | return self.shareData.aboveWidth; 124 | } 125 | 126 | - (CAShapeLayer *)pillarLayer{ 127 | 128 | if (_pillarLayer == nil) { 129 | _pillarLayer = [CAShapeLayer layer]; 130 | _pillarLayer.strokeColor = [UIColor clearColor].CGColor; 131 | // _pillarLayer.fillColor = [UIColor redColor].CGColor; 132 | _pillarLayer.lineWidth = 1; 133 | // _pillarLayer.lineJoin = kCALineJoinRound; 134 | // _pillarLayer.lineCap = kCALineCapRound; 135 | } 136 | return _pillarLayer; 137 | } 138 | 139 | - (CAShapeLayer *)lineLayer 140 | { 141 | if (_lineLayer == nil) { 142 | _lineLayer = [CAShapeLayer layer]; 143 | _lineLayer.strokeColor = [UIColor redColor].CGColor; 144 | _lineLayer.lineWidth = 1; 145 | _lineLayer.lineJoin = kCALineJoinRound; 146 | _lineLayer.lineCap = kCALineCapRound; 147 | } 148 | return _lineLayer; 149 | } 150 | 151 | 152 | - (CAShapeLayer *)markLayer{ 153 | if (_markLayer == nil) { 154 | _markLayer = [CAShapeLayer layer]; 155 | _markLayer.strokeColor = [UIColor grayColor].CGColor; 156 | _markLayer.lineWidth = 0.5; 157 | } 158 | return _markLayer; 159 | } 160 | 161 | 162 | 163 | - (CAShapeLayer *)avg_5_up{ 164 | 165 | if (_avg_5_up == nil) { 166 | _avg_5_up = [CAShapeLayer layer]; 167 | _avg_5_up.strokeColor = [UIColor blueColor].CGColor; 168 | _avg_5_up.lineWidth = 1; 169 | _avg_5_up.lineJoin = kCALineJoinRound; 170 | _avg_5_up.lineCap = kCALineCapRound; 171 | } 172 | return _avg_5_up; 173 | } 174 | 175 | - (CAShapeLayer *)avg_5_down{ 176 | 177 | if (_avg_5_down == nil) { 178 | _avg_5_down = [CAShapeLayer layer]; 179 | _avg_5_down.strokeColor = [UIColor blueColor].CGColor; 180 | _avg_5_down.lineWidth = 1; 181 | _avg_5_down.lineJoin = kCALineJoinRound; 182 | _avg_5_down.lineCap = kCALineCapRound; 183 | } 184 | return _avg_5_down; 185 | } 186 | 187 | - (CAShapeLayer *)avg_10_up{ 188 | 189 | if (_avg_10_up == nil) { 190 | _avg_10_up = [CAShapeLayer layer]; 191 | _avg_10_up.strokeColor = [UIColor orangeColor].CGColor; 192 | _avg_10_up.lineWidth = 1; 193 | _avg_10_up.lineJoin = kCALineJoinRound; 194 | _avg_10_up.lineCap = kCALineCapRound; 195 | } 196 | return _avg_10_up; 197 | } 198 | 199 | - (CAShapeLayer *)avg_10_down{ 200 | 201 | if (_avg_10_down == nil) { 202 | _avg_10_down = [CAShapeLayer layer]; 203 | _avg_10_down.strokeColor = [UIColor orangeColor].CGColor; 204 | _avg_10_down.lineWidth = 1; 205 | _avg_10_down.lineJoin = kCALineJoinRound; 206 | _avg_10_down.lineCap = kCALineCapRound; 207 | } 208 | return _avg_10_down; 209 | } 210 | 211 | - (CAShapeLayer *)avg_20_up{ 212 | 213 | if (_avg_20_up == nil) { 214 | _avg_20_up = [CAShapeLayer layer]; 215 | _avg_20_up.strokeColor = [UIColor purpleColor].CGColor; 216 | _avg_20_up.lineWidth = 1; 217 | _avg_20_up.lineJoin = kCALineJoinRound; 218 | _avg_20_up.lineCap = kCALineCapRound; 219 | } 220 | return _avg_20_up; 221 | } 222 | 223 | - (CAShapeLayer *)avg_20_down{ 224 | 225 | if (_avg_20_down == nil) { 226 | _avg_20_down = [CAShapeLayer layer]; 227 | _avg_20_down.strokeColor = [UIColor purpleColor].CGColor; 228 | _avg_20_down.lineWidth = 1; 229 | _avg_20_down.lineJoin = kCALineJoinRound; 230 | _avg_20_down.lineCap = kCALineCapRound; 231 | } 232 | return _avg_20_down; 233 | } 234 | 235 | - (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(nullable NSString *)reuseIdentifier{ 236 | if (self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]) { 237 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(dataChange) name:@"kLinedataChange" object:nil]; 238 | 239 | } 240 | return self; 241 | } 242 | - (void)dataChange{ 243 | 244 | 245 | 246 | [self reloadPillarChart]; 247 | [self reloadAvg_up]; 248 | [self reloadAvg_down]; 249 | } 250 | 251 | - (void)setShareData:(CCLKLineShareData *)shareData{ 252 | _shareData = shareData; 253 | } 254 | 255 | // 基本展示 256 | - (void)setData:(CCLKLineData *)data{ 257 | _data = data; 258 | 259 | if ([[_data.tradedate substringFromIndex:8] isEqualToString:@"01"]) { 260 | self.isShowDay = YES; 261 | 262 | self.textLayer.string = [_data.tradedate substringToIndex:7]; 263 | 264 | 265 | }else{ 266 | self.isShowDay = NO; 267 | [_textLayer removeFromSuperlayer]; 268 | _textLayer = nil; 269 | } 270 | 271 | [self reloadPillarChart]; 272 | 273 | } 274 | - (void)reloadPillarChart{ 275 | // 防止未赋值 就执行通知 276 | if (_data == nil) { 277 | return; 278 | } 279 | 280 | if (self.isShowDay) { 281 | 282 | UIBezierPath *path = [[UIBezierPath alloc] init]; 283 | [path moveToPoint:CGPointMake(0, self.currHeight * 0.5)]; 284 | [path addLineToPoint:CGPointMake(self.currWidth, self.currHeight * 0.5)]; 285 | [self.contentView.layer addSublayer: self.markLayer]; 286 | self.markLayer.path = path.CGPath; 287 | }else{ 288 | self.markLayer.path = nil; 289 | 290 | } 291 | 292 | 293 | 294 | CGFloat openprice = self.data.openprice; 295 | CGFloat closeprice = self.data.closeprice; 296 | 297 | CGFloat leftPillar; 298 | CGFloat widthPillar; 299 | 300 | 301 | // 开盘大于收盘 302 | if (openprice >= closeprice) { 303 | [CATransaction begin]; 304 | [CATransaction setDisableActions:YES]; 305 | 306 | self.pillarLayer.fillColor = [UIColor greenColor].CGColor; 307 | self.lineLayer.strokeColor = [UIColor greenColor].CGColor; 308 | 309 | [CATransaction commit]; 310 | 311 | 312 | leftPillar = [CCLFrameTool getLeftForValue:openprice 313 | rangeBig:self.visibleMax 314 | rangeSma:self.visibleMin 315 | length:self.currWidth 316 | andGap:self.visibleGap]; 317 | 318 | widthPillar = [CCLFrameTool getWidthForValue:openprice - closeprice 319 | rangeBig:self.visibleMax 320 | rangeSma:self.visibleMin 321 | length:self.currWidth 322 | andGap:self.visibleGap]; 323 | 324 | 325 | }else{ 326 | 327 | [CATransaction begin]; 328 | [CATransaction setDisableActions:YES]; 329 | 330 | self.pillarLayer.fillColor = [UIColor redColor].CGColor; 331 | self.lineLayer.strokeColor = [UIColor redColor].CGColor; 332 | 333 | [CATransaction commit]; 334 | 335 | leftPillar = [CCLFrameTool getLeftForValue:closeprice 336 | rangeBig:self.visibleMax 337 | rangeSma:self.visibleMin 338 | length:self.currWidth 339 | andGap:self.visibleGap]; 340 | widthPillar = [CCLFrameTool getWidthForValue:closeprice - openprice 341 | rangeBig:self.visibleMax 342 | rangeSma:self.visibleMin 343 | length:self.currWidth 344 | andGap:self.visibleGap]; 345 | } 346 | 347 | 348 | 349 | UIBezierPath *pathP = [UIBezierPath bezierPathWithRect:CGRectMake(leftPillar, self.currHeight * 0.05, widthPillar, self.currHeight * 0.9)]; 350 | [self.contentView.layer addSublayer: self.pillarLayer]; 351 | self.pillarLayer.path = pathP.CGPath; 352 | 353 | 354 | 355 | 356 | CGFloat leftLine = [CCLFrameTool getLeftForValue:_data.highestprice 357 | rangeBig:self.visibleMax 358 | rangeSma:self.visibleMin 359 | length:self.currWidth 360 | andGap:self.visibleGap]; 361 | CGFloat rightLine = [CCLFrameTool getLeftForValue:_data.lowestprice 362 | rangeBig:self.visibleMax 363 | rangeSma:self.visibleMin 364 | length:self.currWidth 365 | andGap:self.visibleGap]; 366 | 367 | UIBezierPath *path = [[UIBezierPath alloc] init]; 368 | [path moveToPoint:CGPointMake(leftLine, self.currHeight * 0.5)]; 369 | [path addLineToPoint:CGPointMake(rightLine, self.currHeight * 0.5)]; 370 | [self.contentView.layer addSublayer: self.lineLayer]; 371 | self.lineLayer.path = path.CGPath; 372 | 373 | 374 | 375 | } 376 | - (void)setLastData:(CCLKLineData *)lastData{ 377 | 378 | _lastData = lastData; 379 | 380 | [self reloadAvg_up]; 381 | 382 | } 383 | 384 | - (void)reloadAvg_up{ 385 | 386 | 387 | // 防止未赋值 就执行通知 388 | if (_data == nil) { 389 | return; 390 | } 391 | if (_lastData == nil) { 392 | 393 | self.avg_5_up.path = nil; 394 | self.avg_10_up.path = nil; 395 | self.avg_20_up.path = nil; 396 | 397 | return; 398 | } 399 | 400 | 401 | 402 | CGFloat avg_5_up = [CCLFrameTool getLeftForValue:(_data.avg_5 + _lastData.avg_5) / 2 403 | rangeBig:self.visibleMax 404 | rangeSma:self.visibleMin 405 | length:self.currWidth 406 | andGap:self.visibleGap]; 407 | 408 | CGFloat avg_5 = [CCLFrameTool getLeftForValue:_data.avg_5 409 | rangeBig:self.visibleMax 410 | rangeSma:self.visibleMin 411 | length:self.currWidth 412 | andGap:self.visibleGap]; 413 | 414 | UIBezierPath *path_5 = [[UIBezierPath alloc] init]; 415 | [path_5 moveToPoint:CGPointMake(avg_5_up, 0)]; 416 | [path_5 addLineToPoint:CGPointMake(avg_5, self.currHeight / 2)]; 417 | 418 | self.avg_5_up.path = path_5.CGPath; 419 | [self.contentView.layer addSublayer: self.avg_5_up]; 420 | 421 | 422 | CGFloat avg_10_up = [CCLFrameTool getLeftForValue:(_data.avg_10 + _lastData.avg_10) / 2 423 | rangeBig:self.visibleMax 424 | rangeSma:self.visibleMin 425 | length:self.currWidth 426 | andGap:self.visibleGap]; 427 | 428 | CGFloat avg_10 = [CCLFrameTool getLeftForValue:_data.avg_10 429 | rangeBig:self.visibleMax 430 | rangeSma:self.visibleMin 431 | length:self.currWidth 432 | andGap:self.visibleGap]; 433 | 434 | UIBezierPath *path_10 = [[UIBezierPath alloc] init]; 435 | [path_10 moveToPoint:CGPointMake(avg_10_up, 0)]; 436 | [path_10 addLineToPoint:CGPointMake(avg_10, self.currHeight / 2)]; 437 | 438 | self.avg_10_up.path = path_10.CGPath; 439 | [self.contentView.layer addSublayer: self.avg_10_up]; 440 | 441 | 442 | 443 | CGFloat avg_20_up = [CCLFrameTool getLeftForValue:(_data.avg_20 + _lastData.avg_20) / 2 444 | rangeBig:self.visibleMax 445 | rangeSma:self.visibleMin 446 | length:self.currWidth 447 | andGap:self.visibleGap]; 448 | 449 | CGFloat avg_20 = [CCLFrameTool getLeftForValue:_data.avg_20 450 | rangeBig:self.visibleMax 451 | rangeSma:self.visibleMin 452 | length:self.currWidth 453 | andGap:self.visibleGap]; 454 | 455 | UIBezierPath *path_20 = [[UIBezierPath alloc] init]; 456 | [path_20 moveToPoint:CGPointMake(avg_20_up, 0)]; 457 | [path_20 addLineToPoint:CGPointMake(avg_20, self.currHeight / 2)]; 458 | 459 | self.avg_20_up.path = path_20.CGPath; 460 | [self.contentView.layer addSublayer: self.avg_20_up]; 461 | // 462 | 463 | 464 | } 465 | 466 | 467 | - (void)setNextData:(CCLKLineData *)nextData{ 468 | 469 | _nextData = nextData; 470 | 471 | [self reloadAvg_down]; 472 | 473 | } 474 | 475 | - (void)reloadAvg_down{ 476 | 477 | // 防止未赋值 就执行通知 478 | if (_data == nil) { 479 | return; 480 | } 481 | if (_nextData == nil) { 482 | 483 | self.avg_5_down.path = nil; 484 | self.avg_10_down.path = nil; 485 | self.avg_20_down.path = nil; 486 | return; 487 | } 488 | 489 | CGFloat avg_5 = [CCLFrameTool getLeftForValue:_data.avg_5 490 | rangeBig:self.visibleMax 491 | rangeSma:self.visibleMin 492 | length:self.currWidth 493 | andGap:self.visibleGap]; 494 | 495 | CGFloat avg_5_down = [CCLFrameTool getLeftForValue:(_data.avg_5 + _nextData.avg_5) / 2 496 | rangeBig:self.visibleMax 497 | rangeSma:self.visibleMin 498 | length:self.currWidth 499 | andGap:self.visibleGap]; 500 | 501 | UIBezierPath *path_5 = [[UIBezierPath alloc] init]; 502 | [path_5 moveToPoint:CGPointMake(avg_5, self.currHeight / 2)]; 503 | [path_5 addLineToPoint:CGPointMake(avg_5_down, self.currHeight)]; 504 | 505 | self.avg_5_down.path = path_5.CGPath; 506 | [self.contentView.layer addSublayer: self.avg_5_down]; 507 | 508 | 509 | CGFloat avg_10 = [CCLFrameTool getLeftForValue:_data.avg_10 510 | rangeBig:self.visibleMax 511 | rangeSma:self.visibleMin 512 | length:self.currWidth 513 | andGap:self.visibleGap]; 514 | 515 | CGFloat avg_10_down = [CCLFrameTool getLeftForValue:(_data.avg_10 + _nextData.avg_10) / 2 516 | rangeBig:self.visibleMax 517 | rangeSma:self.visibleMin 518 | length:self.currWidth 519 | andGap:self.visibleGap]; 520 | 521 | UIBezierPath *path_10 = [[UIBezierPath alloc] init]; 522 | [path_10 moveToPoint:CGPointMake(avg_10, self.currHeight / 2)]; 523 | [path_10 addLineToPoint:CGPointMake(avg_10_down, self.currHeight)]; 524 | 525 | self.avg_10_down.path = path_10.CGPath; 526 | [self.contentView.layer addSublayer: self.avg_10_down]; 527 | 528 | 529 | CGFloat avg_20 = [CCLFrameTool getLeftForValue:_data.avg_20 530 | rangeBig:self.visibleMax 531 | rangeSma:self.visibleMin 532 | length:self.currWidth 533 | andGap:self.visibleGap]; 534 | 535 | CGFloat avg_20_down = [CCLFrameTool getLeftForValue:(_data.avg_20 + _nextData.avg_20) / 2 536 | rangeBig:self.visibleMax 537 | rangeSma:self.visibleMin 538 | length:self.currWidth 539 | andGap:self.visibleGap]; 540 | 541 | UIBezierPath *path_20 = [[UIBezierPath alloc] init]; 542 | [path_20 moveToPoint:CGPointMake(avg_20, self.currHeight / 2)]; 543 | [path_20 addLineToPoint:CGPointMake(avg_20_down, self.currHeight)]; 544 | 545 | self.avg_20_down.path = path_20.CGPath; 546 | [self.contentView.layer addSublayer: self.avg_20_down]; 547 | 548 | } 549 | 550 | - (void)dealloc{ 551 | 552 | if (_textLayer) { 553 | [_textLayer removeFromSuperlayer]; 554 | } 555 | 556 | _textLayer = nil; 557 | 558 | } 559 | 560 | 561 | - (void)awakeFromNib { 562 | [super awakeFromNib]; 563 | // Initialization code 564 | } 565 | 566 | - (void)setSelected:(BOOL)selected animated:(BOOL)animated { 567 | [super setSelected:selected animated:animated]; 568 | 569 | // Configure the view for the selected state 570 | } 571 | 572 | @end 573 | -------------------------------------------------------------------------------- /CCLKLineChartView/kLineChart/CCLKLineAboveView.h: -------------------------------------------------------------------------------- 1 | // 2 | // CCLKLineAboveView.h 3 | // KlineChart 4 | // 5 | // Created by Crisps on 16/9/6. 6 | // Copyright © 2016年 cclion.cc. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "CCLKLineShareData.h" 11 | 12 | @protocol CCLKLineAboveViewDelegate 13 | 14 | - (void)KLineViewDidScroll:(UITableView*)tableView; 15 | 16 | - (void)reloadDate:(UITableView*)tableView; 17 | 18 | - (void)handleLongPress:(UILongPressGestureRecognizer *)gestureRecognizer forIndex:(NSIndexPath *)indexPath andTableView:(UITableView*)tableView; 19 | 20 | @end 21 | 22 | 23 | @interface CCLKLineAboveView : UITableView 24 | 25 | @property (nonatomic, strong) CCLKLineShareData *shareData; 26 | 27 | - (instancetype)initWithFrame:(CGRect)frame andShareData:(CCLKLineShareData *)shareData; 28 | 29 | @property (nonatomic, weak) id kDelegate; 30 | 31 | - (void)drawWithLongPress:(UILongPressGestureRecognizer *)gestureRecognizer forIndex:(NSIndexPath *)indexPath; 32 | 33 | 34 | @end 35 | -------------------------------------------------------------------------------- /CCLKLineChartView/kLineChart/CCLKLineAboveView.m: -------------------------------------------------------------------------------- 1 | // 2 | // CCLKLineAboveView.m 3 | // KlineChart 4 | // 5 | // Created by Crisps on 16/9/6. 6 | // Copyright © 2016年 cclion.cc. All rights reserved. 7 | // 8 | 9 | #import "CCLKLineAboveView.h" 10 | #import "CCLKLineData.h" 11 | #import "CCLFrameTool.h" 12 | #import "MJExtension.h" 13 | 14 | #import "CCLKLineAboveCell.h" 15 | 16 | @interface CCLKLineAboveView () 17 | 20 | 21 | @property (nonatomic, assign) CGFloat max; 22 | 23 | @property (nonatomic, assign) CGFloat min; 24 | 25 | @property (nonatomic, assign) CGFloat belowMax; 26 | 27 | // 上一次结束时的高度 28 | @property (nonatomic, assign) CGFloat lastHeight; 29 | // 当前展示的高度 30 | @property (nonatomic, assign) CGFloat currHeight; 31 | // 展示数据 32 | @property (nonatomic, strong) NSArray *dataArr; 33 | 34 | /** 35 | * 长按信息横向 36 | */ 37 | @property (nonatomic, strong) CAShapeLayer *horizontalLine; 38 | /** 39 | * 长按信息纵向 40 | */ 41 | @property (nonatomic, strong) CAShapeLayer *verticalLine; 42 | /** 43 | * 选中的row 44 | */ 45 | @property (nonatomic, assign) NSUInteger selRow; 46 | /** 47 | * 背景图纵向 48 | */ 49 | @property (nonatomic, strong) CAShapeLayer *backLine; 50 | 51 | /** 52 | * 最大值 53 | */ 54 | @property (nonatomic, strong) CAShapeLayer *maxLine; 55 | 56 | /** 57 | * 最小值 58 | */ 59 | @property (nonatomic, strong) CAShapeLayer *minLine; 60 | 61 | /** 62 | * 不复权 63 | */ 64 | @property (nonatomic, strong) CATextLayer *markTextLayer; 65 | 66 | /** 67 | * 最大值显示 68 | */ 69 | @property (nonatomic, strong) CATextLayer *maxTextLayer; 70 | 71 | /** 72 | * 最大值显示 73 | */ 74 | @property (nonatomic, strong) CATextLayer *minTextLayer; 75 | 76 | /** 77 | * 中值显示 78 | */ 79 | @property (nonatomic, strong) CATextLayer *midTextLayer; 80 | 81 | 82 | 83 | @end 84 | 85 | @implementation CCLKLineAboveView 86 | 87 | 88 | - (CAShapeLayer *)backLine{ 89 | 90 | if (_backLine == nil) { 91 | _backLine = [CAShapeLayer layer]; 92 | _backLine.strokeColor = [UIColor grayColor].CGColor; 93 | _backLine.lineWidth = 0.5; 94 | } 95 | return _backLine; 96 | } 97 | 98 | - (CAShapeLayer *)maxLine{ 99 | 100 | if (_maxLine == nil) { 101 | _maxLine = [CAShapeLayer layer]; 102 | _maxLine.strokeColor = [UIColor grayColor].CGColor; 103 | _maxLine.lineWidth = 0.5; 104 | } 105 | return _maxLine; 106 | } 107 | - (CAShapeLayer *)minLine{ 108 | 109 | if (_minLine == nil) { 110 | _minLine = [CAShapeLayer layer]; 111 | _minLine.strokeColor = [UIColor grayColor].CGColor; 112 | _minLine.lineWidth = 0.5; 113 | } 114 | return _minLine; 115 | } 116 | 117 | 118 | - (CATextLayer *)markTextLayer{ 119 | if (_markTextLayer == nil) { 120 | _markTextLayer = [CATextLayer layer]; 121 | _markTextLayer.foregroundColor = [UIColor blackColor].CGColor; 122 | _markTextLayer.backgroundColor = [UIColor greenColor].CGColor; 123 | _markTextLayer.alignmentMode = kCAAlignmentLeft; 124 | _markTextLayer.fontSize = 9.f; //字体的大小 125 | _markTextLayer.contentsScale = [UIScreen mainScreen].scale; 126 | 127 | NSString *text = @"不复权"; 128 | _markTextLayer.string = text; 129 | // 计算高度 130 | UIFont *font = [UIFont systemFontOfSize:9]; 131 | CGSize textSize = [text sizeWithFont:font]; 132 | _markTextLayer.bounds = CGRectMake(0, 0, 30, 20); 133 | 134 | 135 | } 136 | return _markTextLayer; 137 | } 138 | 139 | 140 | - (CATextLayer *)maxTextLayer{ 141 | if (_maxTextLayer == nil) { 142 | _maxTextLayer = [CATextLayer layer]; 143 | _maxTextLayer = [CATextLayer layer]; 144 | _maxTextLayer.foregroundColor = [UIColor blackColor].CGColor; 145 | // _maxTextLayer.backgroundColor = [UIColor greenColor].CGColor; 146 | _maxTextLayer.alignmentMode = kCAAlignmentRight; 147 | _maxTextLayer.fontSize = 9.f; //字体的大小 148 | _maxTextLayer.contentsScale = [UIScreen mainScreen].scale; 149 | } 150 | return _maxTextLayer; 151 | } 152 | 153 | -(CATextLayer *)minTextLayer{ 154 | if (_minTextLayer == nil) { 155 | _minTextLayer = [CATextLayer layer]; 156 | _minTextLayer = [CATextLayer layer]; 157 | _minTextLayer.foregroundColor = [UIColor blackColor].CGColor; 158 | // _minTextLayer.backgroundColor = [UIColor greenColor].CGColor; 159 | _minTextLayer.alignmentMode = kCAAlignmentRight; 160 | _minTextLayer.fontSize = 9.f; //字体的大小 161 | _minTextLayer.contentsScale = [UIScreen mainScreen].scale; 162 | } 163 | return _minTextLayer; 164 | } 165 | 166 | -(CATextLayer *)midTextLayer{ 167 | if (_midTextLayer == nil) { 168 | _midTextLayer = [CATextLayer layer]; 169 | _midTextLayer = [CATextLayer layer]; 170 | _midTextLayer.foregroundColor = [UIColor blackColor].CGColor; 171 | // _midTextLayer.backgroundColor = [UIColor greenColor].CGColor; 172 | _midTextLayer.alignmentMode = kCAAlignmentRight; 173 | _midTextLayer.fontSize = 9.f; //字体的大小 174 | _midTextLayer.contentsScale = [UIScreen mainScreen].scale; 175 | } 176 | return _midTextLayer; 177 | } 178 | 179 | 180 | - (CAShapeLayer *)horizontalLine{ 181 | 182 | if (_horizontalLine == nil) { 183 | _horizontalLine = [CAShapeLayer layer]; 184 | _horizontalLine.strokeColor = [UIColor blueColor].CGColor; 185 | _horizontalLine.lineWidth = 0.5; 186 | } 187 | return _horizontalLine; 188 | } 189 | 190 | - (CAShapeLayer *)verticalLine{ 191 | 192 | if (_verticalLine == nil) { 193 | _verticalLine = [CAShapeLayer layer]; 194 | _verticalLine.strokeColor = [UIColor blueColor].CGColor; 195 | _verticalLine.lineWidth = 0.5; 196 | } 197 | return _verticalLine; 198 | } 199 | 200 | - (instancetype)initWithFrame:(CGRect)frame andShareData:(CCLKLineShareData *)shareData{ 201 | if (self = [super initWithFrame:frame]) { 202 | 203 | _shareData = shareData; 204 | 205 | self.delegate = self; 206 | self.dataSource = self; 207 | 208 | 209 | NSString * path =[[NSBundle mainBundle]pathForResource:@"stockData.plist" ofType:nil]; 210 | NSArray * sourceArray = [[NSDictionary dictionaryWithContentsOfFile:path] objectForKey:@"data"]; 211 | self.dataArr = [CCLKLineData mj_objectArrayWithKeyValuesArray:sourceArray]; 212 | NSLog(@"%@",self.dataArr); 213 | [self registerClass:[CCLKLineAboveCell class] forCellReuseIdentifier:@"CCLKLineAboveCell"]; 214 | 215 | _lastHeight = 10; 216 | _currHeight = 10; 217 | self.shareData.currHeight = _currHeight; 218 | 219 | NSArray *smallArray = [self.dataArr subarrayWithRange:NSMakeRange(0, 20)]; 220 | [self getPeakWithArray:smallArray]; 221 | 222 | [self drawBackLine]; 223 | 224 | UIPinchGestureRecognizer* gesture = [[UIPinchGestureRecognizer alloc] initWithTarget:self 225 | action:@selector(scaleTableView:)]; 226 | [self addGestureRecognizer:gesture]; 227 | 228 | UILongPressGestureRecognizer *longPressReger = [[UILongPressGestureRecognizer alloc] initWithTarget:self 229 | action:@selector(handleLongPress:)]; 230 | longPressReger.minimumPressDuration = 0.3f; 231 | longPressReger.delegate = self; 232 | [self addGestureRecognizer:longPressReger]; 233 | 234 | self.separatorStyle = UITableViewCellSeparatorStyleNone; 235 | 236 | } 237 | return self; 238 | } 239 | #pragma mark - 长按手势 240 | -(void)handleLongPress:(UILongPressGestureRecognizer *)gestureRecognizer 241 | { 242 | 243 | CGPoint p = [gestureRecognizer locationInView:self]; 244 | 245 | NSIndexPath *indexPath = [self indexPathForRowAtPoint:p];//获取响应的长按的indexpath 246 | 247 | [self drawWithLongPress:gestureRecognizer forIndex:indexPath]; 248 | if ([self.kDelegate respondsToSelector:@selector(handleLongPress:forIndex:andTableView:)]) { 249 | [self.kDelegate handleLongPress:gestureRecognizer forIndex:indexPath andTableView:self]; 250 | } 251 | } 252 | 253 | - (void)drawWithLongPress:(UILongPressGestureRecognizer *)gestureRecognizer forIndex:(NSIndexPath *)indexPath{ 254 | CGRect rect = [self rectForRowAtIndexPath:indexPath]; 255 | 256 | if (gestureRecognizer.state == 1) { 257 | self.selRow = indexPath.row; 258 | [self reloadCrossLineWith:indexPath]; 259 | }else if (gestureRecognizer.state == 3){ 260 | 261 | self.horizontalLine.path = nil; 262 | self.verticalLine.path = nil; 263 | 264 | // [self.horizontalLine removeFromSuperlayer]; 265 | 266 | 267 | }else{ 268 | if (indexPath.row != self.selRow) { 269 | self.selRow = indexPath.row; 270 | [self reloadCrossLineWith:indexPath]; 271 | } 272 | } 273 | 274 | if (indexPath == nil) 275 | NSLog(@"long press on table view but not on a row"); 276 | else 277 | NSLog(@"long press on table view at row %ld --- rect %@", (long)indexPath.row, NSStringFromCGRect(rect)); 278 | } 279 | 280 | - (void)reloadCrossLineWith:(NSIndexPath *)indexPath{ 281 | CGRect rect = [self rectForRowAtIndexPath:indexPath]; 282 | // 计算出中心点 283 | 284 | CGFloat y = rect.origin.y + rect.size.height / 2; 285 | CGFloat x; 286 | // 根据涨跌显示的点不固定 287 | CCLKLineData *data = self.dataArr[indexPath.row]; 288 | 289 | // 跌看闭盘 290 | if (data.openprice >= data.closeprice) { 291 | x = [CCLFrameTool getLeftForValue:data.closeprice 292 | rangeBig: self.shareData.aboveMax 293 | rangeSma:self.shareData.abovemin 294 | length:self.shareData.aboveWidth 295 | andGap:self.shareData.gap]; 296 | }else{ 297 | x = [CCLFrameTool getLeftForValue:data.closeprice 298 | rangeBig: self.shareData.aboveMax 299 | rangeSma:self.shareData.abovemin 300 | length:self.shareData.aboveWidth 301 | andGap:self.shareData.gap]; 302 | } 303 | 304 | 305 | UIBezierPath *path_h = [[UIBezierPath alloc] init]; 306 | [path_h moveToPoint:CGPointMake(0, y)]; 307 | [path_h addLineToPoint:CGPointMake(self.shareData.aboveWidth, y)]; 308 | 309 | self.horizontalLine.path =path_h.CGPath; 310 | [self.layer addSublayer: self.horizontalLine]; 311 | 312 | 313 | UIBezierPath *path_v = [[UIBezierPath alloc] init]; 314 | [path_v moveToPoint:CGPointMake(x, self.contentOffset.y)]; 315 | [path_v addLineToPoint:CGPointMake(x, self.contentOffset.y + self.shareData.aboveHeight)]; 316 | 317 | self.verticalLine.path =path_v.CGPath; 318 | [self.layer addSublayer: self.verticalLine]; 319 | 320 | 321 | } 322 | 323 | 324 | #pragma mark - 捏合手势 325 | - (void) scaleTableView:(UIPinchGestureRecognizer*)gesture{ 326 | NSLog(@"state%ld--- scale%f ---velocity%f",(long)gesture.state,gesture.scale,gesture.velocity); 327 | //控制手指数 328 | // if (gesture.numberOfTouches != 2) { 329 | // return; 330 | // } 331 | 332 | 333 | if (gesture.numberOfTouches == 2 && gesture.state != 0.0f) { 334 | //计算捏合中心,根据中心点,确定放大位置 335 | CGPoint p1 = [gesture locationOfTouch: 0 inView:self]; 336 | CGPoint p2 = [gesture locationOfTouch: 1 inView:self]; 337 | CGPoint newCenter = CGPointMake((p1.x+p2.x)/2,(p1.y+p2.y)/2); 338 | NSIndexPath *indexPath = [self indexPathForRowAtPoint:newCenter];//获取响应的长按的indexpath 339 | 340 | 341 | //T添加临时变数 342 | CGFloat tempHeight = _lastHeight * gesture.scale; 343 | 344 | int h = (int)tempHeight; 345 | tempHeight = h; 346 | 347 | if (_currHeight == tempHeight || tempHeight < 10 || tempHeight > 40) { 348 | 349 | }else{ 350 | // 变化之前 351 | CGFloat y1 = indexPath.row * _currHeight; 352 | CGFloat o1 = self.contentOffset.y; 353 | CGFloat h1 = _currHeight * 0.5; 354 | 355 | // 变化之后 356 | CGFloat y2 = indexPath.row * tempHeight; 357 | CGFloat h2 = tempHeight * 0.5; 358 | 359 | CGFloat o2 = y2 + h2 - y1 + o1 - h1; 360 | 361 | _currHeight = tempHeight; 362 | [self reloadData]; 363 | self.contentOffset = CGPointMake(0, o2); 364 | if ([self.kDelegate respondsToSelector:@selector(reloadDate:)]) { 365 | [self.kDelegate reloadDate:self]; 366 | } 367 | 368 | } 369 | } 370 | 371 | // 当滑动结束时 372 | if (gesture.state == 3 || gesture.state == 6) { 373 | _lastHeight = _currHeight; 374 | } 375 | 376 | 377 | 378 | } 379 | 380 | // 获取当前数组极值 381 | - (void)getPeakWithArray:(NSArray *)arr{ 382 | 383 | CGFloat max = 0; 384 | NSUInteger maxRow = 0; 385 | 386 | CGFloat min = 200000.f; 387 | NSUInteger minRow = 0; 388 | 389 | CGFloat belowMax = 0; 390 | 391 | for (CCLKLineData *data in arr) { 392 | if (data.highestprice >= max) { 393 | max = data.highestprice; 394 | maxRow = [self.dataArr indexOfObject:data]; 395 | 396 | } 397 | if (data.lowestprice <= min) { 398 | min = data.lowestprice; 399 | minRow = [self.dataArr indexOfObject:data]; 400 | } 401 | 402 | 403 | if (data.turnovervol >= belowMax) { 404 | belowMax = data.turnovervol; 405 | } 406 | 407 | } 408 | 409 | // NSMutableArray 410 | if (max != self.max) { 411 | self.max = max; 412 | } 413 | if (min != self.min) { 414 | self.min = min; 415 | } 416 | if (belowMax != self.belowMax) { 417 | self.belowMax = belowMax; 418 | } 419 | 420 | 421 | // NSLog(@"max %f %lu --- min %f %lu",max,(unsigned long)maxRow,min,(unsigned long)minRow ); 422 | } 423 | 424 | - (void)setMax:(CGFloat)max{ 425 | _max = max; 426 | self.shareData.aboveMax = _max; 427 | [[NSNotificationCenter defaultCenter] postNotificationName:@"kLinedataChange" object:@"This is posterone!"]; 428 | } 429 | 430 | - (void)setMin:(CGFloat)min{ 431 | _min = min; 432 | self.shareData.abovemin = _min; 433 | [[NSNotificationCenter defaultCenter] postNotificationName:@"kLinedataChange" object:@"This is posterone!"]; 434 | } 435 | 436 | - (void)setBelowMax:(CGFloat)belowMax{ 437 | _belowMax = belowMax; 438 | self.shareData.belowMax = _belowMax; 439 | [[NSNotificationCenter defaultCenter] postNotificationName:@"kLinedataBelowMaxChange" object:@"This is posterone!"]; 440 | } 441 | 442 | - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{ 443 | self.shareData.currHeight = _currHeight; 444 | return self.shareData.currHeight; 445 | } 446 | 447 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{ 448 | return self.dataArr.count; 449 | } 450 | 451 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{ 452 | 453 | // return [UITableViewCell new]; 454 | 455 | NSArray *arr = [self indexPathsForVisibleRows]; 456 | 457 | NSIndexPath *first = [arr firstObject]; 458 | NSIndexPath *last = [arr lastObject]; 459 | 460 | 461 | NSArray *smallArray = [self.dataArr subarrayWithRange:NSMakeRange(first.row, last.row - first.row)]; 462 | 463 | [self getPeakWithArray:smallArray]; 464 | 465 | CCLKLineAboveCell *cell = [tableView dequeueReusableCellWithIdentifier:@"CCLKLineAboveCell"]; 466 | 467 | cell.shareData = self.shareData; 468 | 469 | CCLKLineData *data = self.dataArr[indexPath.row]; 470 | cell.data = data; 471 | // [cell setindex:indexPath.row]; 472 | cell.selectionStyle = UITableViewCellSelectionStyleNone; 473 | 474 | //拓展 475 | //判断是否有上一个值 476 | if (indexPath.row >= 1) { 477 | CCLKLineData *lastData = self.dataArr[indexPath.row - 1]; 478 | cell.lastData = lastData; 479 | }else{ 480 | // 赋值nil是为了清楚 481 | cell.lastData = nil; 482 | } 483 | //判断是否有下一个 484 | if (self.dataArr.count - indexPath.row - 1) { 485 | 486 | CCLKLineData *nextData = self.dataArr[indexPath.row + 1]; 487 | cell.nextData = nextData; 488 | }else{ 489 | cell.nextData = nil; 490 | } 491 | 492 | return cell; 493 | } 494 | 495 | - (void)scrollViewDidScroll:(UIScrollView *)scrollView{ 496 | 497 | if ([self.kDelegate respondsToSelector:@selector(KLineViewDidScroll:)]) { 498 | [self.kDelegate KLineViewDidScroll:self]; 499 | } 500 | 501 | [self drawBackLine]; 502 | 503 | 504 | NSArray *arr = [self visibleCells]; 505 | 506 | for (CCLKLineAboveCell *cell in arr) { 507 | 508 | if (cell.isShowDay) { 509 | 510 | NSIndexPath *indexPath = [self indexPathForCell:cell];//获取响应的长按的indexpath 511 | 512 | CGRect rect = [self rectForRowAtIndexPath:indexPath]; 513 | 514 | CGFloat yCenter = rect.origin.y + rect.size.height / 2 - self.contentOffset.y; 515 | 516 | [self.superview.layer addSublayer:cell.textLayer]; 517 | 518 | CGFloat w = 40; 519 | CGFloat h = 14; 520 | CGFloat x = self.shareData.aboveHeight - yCenter - w * 0.5; 521 | CGFloat y = self.shareData.aboveWidth; 522 | 523 | CGRect frame = CGRectMake(x, y, w, h); 524 | 525 | [CATransaction begin]; 526 | [CATransaction setDisableActions:YES]; 527 | 528 | cell.textLayer.frame = frame; 529 | 530 | [CATransaction commit]; 531 | 532 | } 533 | } 534 | } 535 | 536 | - (void)drawBackLine{ 537 | 538 | UIBezierPath *path_mid = [[UIBezierPath alloc] init]; 539 | [path_mid moveToPoint:CGPointMake(self.shareData.aboveWidth * 0.5, self.contentOffset.y)]; 540 | [path_mid addLineToPoint:CGPointMake(self.shareData.aboveWidth * 0.5, self.contentOffset.y + self.bounds.size.height)]; 541 | 542 | self.backLine.path = path_mid.CGPath; 543 | [self.layer addSublayer:self.backLine]; 544 | 545 | UIBezierPath *path_max = [[UIBezierPath alloc] init]; 546 | [path_max moveToPoint:CGPointMake(10, self.contentOffset.y)]; 547 | [path_max addLineToPoint:CGPointMake(10, self.contentOffset.y + self.bounds.size.height)]; 548 | 549 | self.maxLine.path = path_max.CGPath; 550 | [self.layer addSublayer:self.maxLine]; 551 | 552 | 553 | UIBezierPath *path_min = [[UIBezierPath alloc] init]; 554 | [path_min moveToPoint:CGPointMake(self.shareData.aboveWidth - 10, self.contentOffset.y)]; 555 | [path_min addLineToPoint:CGPointMake(self.shareData.aboveWidth - 10, self.contentOffset.y + self.bounds.size.height)]; 556 | 557 | self.minLine.path = path_min.CGPath; 558 | [self.layer addSublayer:self.minLine]; 559 | 560 | 561 | 562 | 563 | [self.layer addSublayer:self.markTextLayer]; 564 | 565 | CGAffineTransform transform = CGAffineTransformMakeRotation(-M_PI * 0.5); 566 | [self.markTextLayer setAffineTransform:transform]; 567 | 568 | 569 | CGRect frame = CGRectMake(0, self.contentOffset.y + self.bounds.size.height - 30, 13, 30); 570 | 571 | [CATransaction begin]; 572 | [CATransaction setDisableActions:YES]; 573 | 574 | self.markTextLayer.frame = frame; 575 | [CATransaction commit]; 576 | 577 | 578 | 579 | if (_max) { 580 | [self.layer addSublayer:self.maxTextLayer]; 581 | self.maxTextLayer.string = [NSString stringWithFormat:@"%.2f",_max]; 582 | CGAffineTransform transform = CGAffineTransformMakeRotation(-M_PI * 0.5); 583 | [self.maxTextLayer setAffineTransform:transform]; 584 | CGRect frame = CGRectMake(10 - 7, self.contentOffset.y, 14, 60); 585 | 586 | [CATransaction begin]; 587 | [CATransaction setDisableActions:YES]; 588 | 589 | self.maxTextLayer.frame = frame; 590 | [CATransaction commit]; 591 | } 592 | if (_min) { 593 | [self.layer addSublayer:self.minTextLayer]; 594 | self.minTextLayer.string = [NSString stringWithFormat:@"%.2f",_min]; 595 | CGAffineTransform transform = CGAffineTransformMakeRotation(-M_PI * 0.5); 596 | [self.minTextLayer setAffineTransform:transform]; 597 | CGRect frame = CGRectMake(self.shareData.aboveWidth - 10 - 7 , self.contentOffset.y, 14, 60); 598 | 599 | [CATransaction begin]; 600 | [CATransaction setDisableActions:YES]; 601 | 602 | self.minTextLayer.frame = frame; 603 | [CATransaction commit]; 604 | } 605 | if (_min && _max) { 606 | [self.layer addSublayer:self.midTextLayer]; 607 | self.midTextLayer.string = [NSString stringWithFormat:@"%.2f",(_min + _max) / 2]; 608 | CGAffineTransform transform = CGAffineTransformMakeRotation(-M_PI * 0.5); 609 | [self.midTextLayer setAffineTransform:transform]; 610 | CGRect frame = CGRectMake(self.shareData.aboveWidth * 0.5 - 7, self.contentOffset.y, 14, 60); 611 | 612 | [CATransaction begin]; 613 | [CATransaction setDisableActions:YES]; 614 | 615 | self.midTextLayer.frame = frame; 616 | [CATransaction commit]; 617 | } 618 | } 619 | 620 | 621 | @end 622 | -------------------------------------------------------------------------------- /CCLKLineChartView/kLineChart/CCLKLineBelowCell.h: -------------------------------------------------------------------------------- 1 | // 2 | // CCLKLineBelowCell.h 3 | // KlineChart 4 | // 5 | // Created by Crisps on 16/9/7. 6 | // Copyright © 2016年 cclion.cc. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "CCLKLineData.h" 11 | #import "CCLKLineShareData.h" 12 | 13 | @interface CCLKLineBelowCell : UITableViewCell 14 | 15 | @property (nonatomic, strong) CCLKLineShareData *shareData; // 必须最先赋值 16 | 17 | @property (nonatomic, strong) CCLKLineData *data; 18 | 19 | @end 20 | -------------------------------------------------------------------------------- /CCLKLineChartView/kLineChart/CCLKLineBelowCell.m: -------------------------------------------------------------------------------- 1 | // 2 | // CCLKLineBelowCell.m 3 | // KlineChart 4 | // 5 | // Created by Crisps on 16/9/7. 6 | // Copyright © 2016年 cclion.cc. All rights reserved. 7 | // 8 | 9 | #import "CCLKLineBelowCell.h" 10 | #import "CCLFrameTool.h" 11 | 12 | @interface CCLKLineBelowCell () 13 | /** 14 | * 柱状图 15 | */ 16 | @property (nonatomic, strong) CAShapeLayer *pillarLayer; 17 | 18 | /** 19 | * 当前界面最大值、最小值 (转shareData) 20 | */ 21 | @property (nonatomic, assign) CGFloat visibleMax; 22 | 23 | @property (nonatomic, assign) CGFloat currHeight; 24 | 25 | @property (nonatomic, assign) CGFloat currWidth; 26 | 27 | /** 28 | * 标志线 29 | */ 30 | @property (nonatomic, strong) CAShapeLayer *markLayer; 31 | 32 | @end 33 | 34 | @implementation CCLKLineBelowCell 35 | 36 | - (CAShapeLayer *)pillarLayer{ 37 | 38 | if (_pillarLayer == nil) { 39 | _pillarLayer = [CAShapeLayer layer]; 40 | _pillarLayer.strokeColor = [UIColor clearColor].CGColor; 41 | _pillarLayer.lineWidth = 1; 42 | } 43 | return _pillarLayer; 44 | } 45 | 46 | - (CGFloat)visibleMax{ 47 | return self.shareData.belowMax; 48 | } 49 | 50 | -(CGFloat)currHeight{ 51 | return self.shareData.currHeight; 52 | } 53 | 54 | - (CGFloat)currWidth{ 55 | return self.shareData.belowWidth; 56 | } 57 | - (CAShapeLayer *)markLayer{ 58 | if (_markLayer == nil) { 59 | _markLayer = [CAShapeLayer layer]; 60 | _markLayer.strokeColor = [UIColor grayColor].CGColor; 61 | _markLayer.lineWidth = 0.5; 62 | } 63 | return _markLayer; 64 | } 65 | 66 | 67 | 68 | - (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(nullable NSString *)reuseIdentifier{ 69 | if (self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]) { 70 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(dataChange) name:@"kLinedataBelowMaxChange" object:nil]; 71 | 72 | } 73 | return self; 74 | } 75 | 76 | - (void)dataChange{ 77 | [self reloadPillarChart]; 78 | } 79 | 80 | - (void)setShareData:(CCLKLineShareData *)shareData{ 81 | _shareData = shareData; 82 | } 83 | 84 | // 基本展示 85 | - (void)setData:(CCLKLineData *)data{ 86 | _data = data; 87 | 88 | [self reloadPillarChart]; 89 | 90 | } 91 | 92 | - (void)reloadPillarChart{ 93 | // 防止未赋值 就执行通知 94 | if (_data == nil) { 95 | return; 96 | } 97 | 98 | 99 | if ([[_data.tradedate substringFromIndex:8] isEqualToString:@"01"]) { 100 | UIBezierPath *path = [[UIBezierPath alloc] init]; 101 | [path moveToPoint:CGPointMake(0, self.currHeight * 0.5)]; 102 | [path addLineToPoint:CGPointMake(self.currWidth, self.currHeight * 0.5)]; 103 | [self.contentView.layer addSublayer: self.markLayer]; 104 | self.markLayer.path = path.CGPath; 105 | 106 | 107 | }else{ 108 | self.markLayer.path = nil; 109 | } 110 | 111 | 112 | [CATransaction begin]; 113 | [CATransaction setDisableActions:YES]; 114 | 115 | // 开盘大于收盘 116 | if (self.data.openprice >= self.data.closeprice) { 117 | 118 | self.pillarLayer.fillColor = [UIColor greenColor].CGColor; 119 | 120 | }else{ 121 | 122 | self.pillarLayer.fillColor = [UIColor redColor].CGColor; 123 | 124 | } 125 | 126 | [CATransaction commit]; 127 | 128 | CGFloat leftPillar = [CCLFrameTool getLeftForValue:self.data.turnovervol 129 | rangeBig:self.visibleMax 130 | rangeSma:0 131 | andLength:self.currWidth]; 132 | 133 | CGFloat widthPillar = [CCLFrameTool getWidthForValue:self.data.turnovervol 134 | rangeBig:self.visibleMax 135 | rangeSma:0 136 | andLength:self.currWidth]; 137 | 138 | UIBezierPath *pathP = [UIBezierPath bezierPathWithRect:CGRectMake(leftPillar, self.currHeight * 0.05, widthPillar, self.currHeight * 0.9)]; 139 | 140 | [self.contentView.layer addSublayer: self.pillarLayer]; 141 | self.pillarLayer.path = pathP.CGPath; 142 | 143 | 144 | 145 | } 146 | 147 | - (void)awakeFromNib { 148 | [super awakeFromNib]; 149 | // Initialization code 150 | } 151 | 152 | - (void)setSelected:(BOOL)selected animated:(BOOL)animated { 153 | [super setSelected:selected animated:animated]; 154 | 155 | // Configure the view for the selected state 156 | } 157 | 158 | @end 159 | -------------------------------------------------------------------------------- /CCLKLineChartView/kLineChart/CCLKLineBelowView.h: -------------------------------------------------------------------------------- 1 | // 2 | // CCLKLineBelowView.h 3 | // KlineChart 4 | // 5 | // Created by Crisps on 16/9/7. 6 | // Copyright © 2016年 cclion.cc. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "CCLKLineShareData.h" 11 | 12 | @protocol CCLKLineBelowViewDelegate 13 | 14 | - (void)KLineViewDidScroll:(UITableView*)tableView; 15 | 16 | - (void)reloadDate:(UITableView*)tableView; 17 | 18 | - (void)handleLongPress:(UILongPressGestureRecognizer *)gestureRecognizer forIndex:(NSIndexPath *)indexPath andTableView:(UITableView*)tableView; 19 | @end 20 | 21 | @interface CCLKLineBelowView : UITableView 22 | 23 | @property (nonatomic, strong) CCLKLineShareData *shareData; 24 | 25 | - (instancetype)initWithFrame:(CGRect)frame andShareData:(CCLKLineShareData *)shareData; 26 | 27 | @property (nonatomic, weak) id kDelegate; 28 | 29 | - (void)drawWithLongPress:(UILongPressGestureRecognizer *)gestureRecognizer forIndex:(NSIndexPath *)indexPath; 30 | 31 | 32 | @end 33 | -------------------------------------------------------------------------------- /CCLKLineChartView/kLineChart/CCLKLineBelowView.m: -------------------------------------------------------------------------------- 1 | // 2 | // CCLKLineBelowView.m 3 | // KlineChart 4 | // 5 | // Created by Crisps on 16/9/7. 6 | // Copyright © 2016年 cclion.cc. All rights reserved. 7 | // 8 | 9 | #import "CCLKLineBelowView.h" 10 | #import "CCLKLineData.h" 11 | #import "CCLFrameTool.h" 12 | #import "MJExtension.h" 13 | #import "CCLKLineBelowCell.h" 14 | @interface CCLKLineBelowView () 15 | 18 | 19 | 20 | // 展示数据 21 | @property (nonatomic, strong) NSArray *dataArr; 22 | 23 | /** 24 | * 成交量 25 | */ 26 | @property (nonatomic, strong) CATextLayer *markTextLayer; 27 | 28 | /** 29 | * 最大值显示 30 | */ 31 | @property (nonatomic, strong) CATextLayer *maxTextLayer; 32 | 33 | /** 34 | * 选中的row 35 | */ 36 | @property (nonatomic, assign) NSUInteger selRow; 37 | 38 | /** 39 | * 长按信息横向 40 | */ 41 | @property (nonatomic, strong) CAShapeLayer *horizontalLine; 42 | 43 | @end 44 | 45 | @implementation CCLKLineBelowView 46 | 47 | - (CATextLayer *)markTextLayer{ 48 | if (_markTextLayer == nil) { 49 | _markTextLayer = [CATextLayer layer]; 50 | _markTextLayer.foregroundColor = [UIColor blackColor].CGColor; 51 | _markTextLayer.backgroundColor = [UIColor greenColor].CGColor; 52 | _markTextLayer.alignmentMode = kCAAlignmentLeft; 53 | _markTextLayer.fontSize = 9.f; //字体的大小 54 | _markTextLayer.contentsScale = [UIScreen mainScreen].scale; 55 | 56 | NSString *text = @"成交量"; 57 | _markTextLayer.string = text; 58 | // 计算高度 59 | UIFont *font = [UIFont systemFontOfSize:9]; 60 | CGSize textSize = [text sizeWithFont:font]; 61 | _markTextLayer.bounds = CGRectMake(0, 0, 30, 20); 62 | 63 | } 64 | return _markTextLayer; 65 | } 66 | 67 | - (CATextLayer *)maxTextLayer{ 68 | if (_maxTextLayer == nil) { 69 | _maxTextLayer = [CATextLayer layer]; 70 | _maxTextLayer = [CATextLayer layer]; 71 | _maxTextLayer.foregroundColor = [UIColor blackColor].CGColor; 72 | // _maxTextLayer.backgroundColor = [UIColor greenColor].CGColor; 73 | _maxTextLayer.alignmentMode = kCAAlignmentRight; 74 | _maxTextLayer.fontSize = 9.f; //字体的大小 75 | _maxTextLayer.contentsScale = [UIScreen mainScreen].scale; 76 | } 77 | return _maxTextLayer; 78 | } 79 | 80 | - (CAShapeLayer *)horizontalLine{ 81 | 82 | if (_horizontalLine == nil) { 83 | _horizontalLine = [CAShapeLayer layer]; 84 | _horizontalLine.strokeColor = [UIColor blueColor].CGColor; 85 | _horizontalLine.lineWidth = 0.5; 86 | } 87 | return _horizontalLine; 88 | } 89 | 90 | - (instancetype)initWithFrame:(CGRect)frame andShareData:(CCLKLineShareData *)shareData{ 91 | if (self = [super initWithFrame:frame]) { 92 | 93 | _shareData = shareData; 94 | 95 | self.delegate = self; 96 | self.dataSource = self; 97 | 98 | 99 | NSString * path =[[NSBundle mainBundle]pathForResource:@"stockData.plist" ofType:nil]; 100 | NSArray * sourceArray = [[NSDictionary dictionaryWithContentsOfFile:path] objectForKey:@"data"]; 101 | self.dataArr = [CCLKLineData mj_objectArrayWithKeyValuesArray:sourceArray]; 102 | NSLog(@"%@",self.dataArr); 103 | [self registerClass:[CCLKLineBelowCell class] forCellReuseIdentifier:@"CCLKLineBelowCell"]; 104 | 105 | // _lastHeight = 10; 106 | // _currHeight = 10; 107 | // self.shareData.currHeight = _currHeight; 108 | // 109 | // NSArray *smallArray = [self.dataArr subarrayWithRange:NSMakeRange(0, 20)]; 110 | // [self getPeakWithArray:smallArray]; 111 | // 112 | [self drawBackLine]; 113 | 114 | // UIPinchGestureRecognizer* gesture = [[UIPinchGestureRecognizer alloc] initWithTarget:self 115 | // action:@selector(scaleTableView:)]; 116 | // [self addGestureRecognizer:gesture]; 117 | // 118 | UILongPressGestureRecognizer *longPressReger = [[UILongPressGestureRecognizer alloc] initWithTarget:self 119 | action:@selector(handleLongPress:)]; 120 | longPressReger.minimumPressDuration = 0.3f; 121 | longPressReger.delegate = self; 122 | [self addGestureRecognizer:longPressReger]; 123 | 124 | self.separatorStyle = UITableViewCellSeparatorStyleNone; 125 | 126 | } 127 | return self; 128 | } 129 | 130 | #pragma mark - 长按手势 131 | -(void)handleLongPress:(UILongPressGestureRecognizer *)gestureRecognizer 132 | { 133 | 134 | CGPoint p = [gestureRecognizer locationInView:self]; 135 | 136 | NSIndexPath *indexPath = [self indexPathForRowAtPoint:p];//获取响应的长按的indexpath 137 | 138 | [self drawWithLongPress:gestureRecognizer forIndex:indexPath]; 139 | if ([self.kDelegate respondsToSelector:@selector(handleLongPress:forIndex:andTableView:)]) { 140 | [self.kDelegate handleLongPress:gestureRecognizer forIndex:indexPath andTableView:self]; 141 | } 142 | 143 | } 144 | 145 | - (void)drawWithLongPress:(UILongPressGestureRecognizer *)gestureRecognizer forIndex:(NSIndexPath *)indexPath{ 146 | CGRect rect = [self rectForRowAtIndexPath:indexPath]; 147 | 148 | if (gestureRecognizer.state == 1) { 149 | self.selRow = indexPath.row; 150 | [self reloadCrossLineWith:indexPath]; 151 | }else if (gestureRecognizer.state == 3){ 152 | 153 | self.horizontalLine.path = nil; 154 | 155 | }else{ 156 | if (indexPath.row != self.selRow) { 157 | self.selRow = indexPath.row; 158 | [self reloadCrossLineWith:indexPath]; 159 | } 160 | } 161 | 162 | if (indexPath == nil) 163 | NSLog(@"long press on table view but not on a row"); 164 | else 165 | NSLog(@"long press on table view at row %ld --- rect %@", (long)indexPath.row, NSStringFromCGRect(rect)); 166 | } 167 | 168 | - (void)reloadCrossLineWith:(NSIndexPath *)indexPath{ 169 | CGRect rect = [self rectForRowAtIndexPath:indexPath]; 170 | // 计算出中心点 171 | 172 | CGFloat y = rect.origin.y + rect.size.height / 2; 173 | 174 | UIBezierPath *path_h = [[UIBezierPath alloc] init]; 175 | [path_h moveToPoint:CGPointMake(0, y)]; 176 | [path_h addLineToPoint:CGPointMake(self.shareData.aboveWidth, y)]; 177 | 178 | self.horizontalLine.path =path_h.CGPath; 179 | [self.layer addSublayer: self.horizontalLine]; 180 | 181 | } 182 | 183 | 184 | 185 | 186 | - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{ 187 | return self.shareData.currHeight; 188 | } 189 | 190 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{ 191 | return self.dataArr.count; 192 | } 193 | 194 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{ 195 | 196 | // return [UITableViewCell new]; 197 | 198 | NSArray *arr = [self indexPathsForVisibleRows]; 199 | 200 | NSIndexPath *first = [arr firstObject]; 201 | NSIndexPath *last = [arr lastObject]; 202 | 203 | 204 | // NSArray *smallArray = [self.dataArr subarrayWithRange:NSMakeRange(first.row, last.row - first.row)]; 205 | 206 | // [self getPeakWithArray:smallArray]; 207 | 208 | CCLKLineBelowCell *cell = [tableView dequeueReusableCellWithIdentifier:@"CCLKLineBelowCell"]; 209 | 210 | cell.shareData = self.shareData; 211 | CCLKLineData *data = self.dataArr[indexPath.row]; 212 | cell.data = data; 213 | 214 | cell.selectionStyle = UITableViewCellSelectionStyleNone; 215 | 216 | 217 | return cell; 218 | } 219 | 220 | 221 | - (void)drawBackLine{ 222 | 223 | 224 | [self.layer addSublayer:self.markTextLayer]; 225 | 226 | CGAffineTransform transform = CGAffineTransformMakeRotation(-M_PI * 0.5); 227 | [self.markTextLayer setAffineTransform:transform]; 228 | 229 | CGRect frame = CGRectMake(0, self.contentOffset.y + self.bounds.size.height - 30, 13, 30); 230 | 231 | [CATransaction begin]; 232 | [CATransaction setDisableActions:YES]; 233 | 234 | self.markTextLayer.frame = frame; 235 | [CATransaction commit]; 236 | 237 | if (self.shareData.belowMax) { 238 | [self.layer addSublayer:self.maxTextLayer]; 239 | self.maxTextLayer.string = [NSString stringWithFormat:@"%.2f万",self.shareData.belowMax/10000]; 240 | CGAffineTransform transform = CGAffineTransformMakeRotation(-M_PI * 0.5); 241 | [self.maxTextLayer setAffineTransform:transform]; 242 | CGRect frame = CGRectMake(0, self.contentOffset.y, 14, 60); 243 | 244 | [CATransaction begin]; 245 | [CATransaction setDisableActions:YES]; 246 | 247 | self.maxTextLayer.frame = frame; 248 | [CATransaction commit]; 249 | } 250 | 251 | 252 | } 253 | 254 | 255 | 256 | - (void)scrollViewDidScroll:(UIScrollView *)scrollView{ 257 | if ([self.kDelegate respondsToSelector:@selector(KLineViewDidScroll:)]) { 258 | [self.kDelegate KLineViewDidScroll:self]; 259 | } 260 | [self drawBackLine]; 261 | } 262 | 263 | 264 | @end 265 | -------------------------------------------------------------------------------- /CCLKLineChartView/kLineChart/CCLKLineChartView.h: -------------------------------------------------------------------------------- 1 | // 2 | // CCLLineChartView.h 3 | // KlineChart 4 | // 5 | // Created by Crisps on 16/9/6. 6 | // Copyright © 2016年 cclion.cc. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | /** 12 | K线类型 13 | */ 14 | typedef enum : NSUInteger { 15 | CCLKLineDay, 16 | CCLKLineWeek, 17 | CCLKLineMonth, 18 | } CCLKLineType; 19 | 20 | 21 | @interface CCLKLineChartView : UIView 22 | 23 | - (instancetype)initWithFrame:(CGRect)frame secID:(NSString *)secID andtype:(CCLKLineType)type; 24 | 25 | @end 26 | -------------------------------------------------------------------------------- /CCLKLineChartView/kLineChart/CCLKLineChartView.m: -------------------------------------------------------------------------------- 1 | // 2 | // CCLLineChartView.m 3 | // KlineChart 4 | // 5 | // Created by Crisps on 16/9/6. 6 | // Copyright © 2016年 cclion.cc. All rights reserved. 7 | // 8 | 9 | #import "CCLKLineChartView.h" 10 | #import "CCLKLineShareData.h" 11 | #import "CCLKLineAboveView.h" 12 | #import "CCLKLineBelowView.h" 13 | @interface CCLKLineChartView () 14 | < 15 | CCLKLineAboveViewDelegate, 16 | CCLKLineBelowViewDelegate 17 | > 18 | @property (nonatomic, copy) NSString *secID; 19 | 20 | @property (nonatomic, assign) CCLKLineType type; 21 | 22 | @property (nonatomic, strong) CCLKLineShareData *shareData; 23 | 24 | @property (nonatomic, strong) CCLKLineAboveView *aboveView; 25 | 26 | @property (nonatomic, strong) CCLKLineBelowView *belowView; 27 | 28 | 29 | @end 30 | 31 | @implementation CCLKLineChartView 32 | 33 | - (instancetype)initWithFrame:(CGRect)frame secID:(NSString *)secID andtype:(CCLKLineType)type{ 34 | if (self = [super initWithFrame:frame]) { 35 | _secID = secID; 36 | _type = type; 37 | 38 | // 旋转 39 | [self addSubview:self.aboveView]; 40 | self.aboveView.transform = CGAffineTransformMakeRotation( M_PI * 0.5); 41 | self.aboveView.frame = CGRectMake(0, 0, self.shareData.aboveHeight, self.shareData.aboveWidth); 42 | 43 | [self addSubview:self.belowView]; 44 | self.belowView.transform = CGAffineTransformMakeRotation( M_PI * 0.5); 45 | self.belowView.frame = CGRectMake(0, self.shareData.aboveWidth +20, self.shareData.belowHeight, self.shareData.belowWidth); 46 | 47 | self.layer.borderWidth = 1; 48 | self.layer.borderColor = [UIColor blackColor].CGColor; 49 | } 50 | return self; 51 | } 52 | 53 | 54 | - (CCLKLineShareData *)shareData{ 55 | if (_shareData == nil) { 56 | _shareData = [[CCLKLineShareData alloc] init]; 57 | _shareData.aboveHeight = self.bounds.size.width; 58 | _shareData.aboveWidth = self.bounds.size.height *0.6; 59 | 60 | _shareData.gap = 10; 61 | _shareData.currHeight = 10; 62 | 63 | _shareData.belowHeight = self.bounds.size.width; 64 | _shareData.belowWidth = self.bounds.size.height * 0.3; 65 | 66 | } 67 | return _shareData; 68 | } 69 | 70 | #pragma mark - 滑动 71 | - (void)KLineViewDidScroll:(UITableView *)tableView{ 72 | 73 | if (tableView == self.aboveView) { 74 | [self.belowView setContentOffset:self.aboveView.contentOffset]; 75 | }else if (tableView == self.belowView){ 76 | [self.aboveView setContentOffset:self.belowView.contentOffset]; 77 | } 78 | 79 | } 80 | 81 | 82 | #pragma mark - 捏合 83 | - (void)reloadDate:(UITableView *)tableView{ 84 | if (tableView == self.aboveView) { 85 | [self.belowView reloadData]; 86 | [self.belowView setContentOffset:self.aboveView.contentOffset]; 87 | }else if (tableView == self.belowView){ 88 | [self.aboveView reloadData]; 89 | [self.aboveView setContentOffset:self.belowView.contentOffset]; 90 | } 91 | } 92 | 93 | 94 | #pragma mark - 长按 95 | - (void)handleLongPress:(UILongPressGestureRecognizer *)gestureRecognizer forIndex:(NSIndexPath *)indexPath andTableView:(UITableView *)tableView{ 96 | if (tableView == self.aboveView) { 97 | [self.belowView drawWithLongPress:gestureRecognizer forIndex:indexPath]; 98 | 99 | }else if (tableView == self.belowView){ 100 | [self.aboveView drawWithLongPress:gestureRecognizer forIndex:indexPath]; 101 | } 102 | } 103 | 104 | 105 | - (CCLKLineAboveView *)aboveView{ 106 | if (_aboveView == nil) { 107 | _aboveView = [[CCLKLineAboveView alloc] initWithFrame:CGRectMake(0, 0, self.shareData.aboveWidth, self.shareData.aboveHeight) andShareData:self.shareData]; 108 | _aboveView.layer.borderWidth = 1; 109 | _aboveView.layer.borderColor = [UIColor blackColor].CGColor; 110 | _aboveView.kDelegate = self; 111 | } 112 | return _aboveView; 113 | } 114 | 115 | 116 | - (CCLKLineBelowView *)belowView{ 117 | if (_belowView == nil) { 118 | _belowView = [[CCLKLineBelowView alloc] initWithFrame:CGRectMake(0 , self.shareData.aboveWidth +20 , self.shareData.belowWidth, self.shareData.belowHeight) andShareData:self.shareData]; 119 | _belowView.layer.borderWidth = 1; 120 | _belowView.layer.borderColor = [UIColor blackColor].CGColor; 121 | _belowView.kDelegate = self; 122 | } 123 | return _belowView; 124 | } 125 | 126 | 127 | 128 | 129 | @end 130 | -------------------------------------------------------------------------------- /CCLKLineChartView/kLineChart/CCLKLineData.h: -------------------------------------------------------------------------------- 1 | // 2 | // CCLKLineData.h 3 | // KlineChart 4 | // 5 | // Created by Crisps on 16/8/24. 6 | // Copyright © 2016年 cclion.cc. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface CCLKLineData : NSObject 12 | 13 | @property (nonatomic, copy) NSString *rise_and_fall_rate; 14 | 15 | @property (nonatomic, copy) NSString *tradedate; 16 | 17 | @property (nonatomic, copy) NSString *date_week; 18 | 19 | @property (nonatomic, assign) CGFloat avg_5; 20 | 21 | @property (nonatomic, assign) CGFloat avg_10; 22 | 23 | @property (nonatomic, assign) CGFloat avg_20; 24 | 25 | @property (nonatomic, assign) CGFloat total_value_trade; 26 | 27 | @property (nonatomic, assign) CGFloat pre_close_px; 28 | 29 | // 开盘 收盘 30 | @property (nonatomic, assign) CGFloat openprice; 31 | 32 | @property (nonatomic, assign) CGFloat closeprice; 33 | 34 | // 最高 最低 35 | @property (nonatomic, assign) CGFloat highestprice; 36 | 37 | @property (nonatomic, assign) CGFloat lowestprice; 38 | 39 | // 出手量 40 | @property (nonatomic, assign) CGFloat turnovervol; 41 | 42 | @property (nonatomic, assign) CGFloat rise_and_fall_value; 43 | 44 | // 自己计算 45 | @property (nonatomic, assign) CGFloat max; 46 | @property (nonatomic, assign) CGFloat min; 47 | 48 | // 是不是涨 49 | @property (nonatomic, assign) BOOL isRise; 50 | @end 51 | -------------------------------------------------------------------------------- /CCLKLineChartView/kLineChart/CCLKLineData.m: -------------------------------------------------------------------------------- 1 | // 2 | // CCLKLineData.m 3 | // KlineChart 4 | // 5 | // Created by Crisps on 16/8/24. 6 | // Copyright © 2016年 cclion.cc. All rights reserved. 7 | // 8 | 9 | #import "CCLKLineData.h" 10 | 11 | @implementation CCLKLineData 12 | 13 | /* 14 | // Only override drawRect: if you perform custom drawing. 15 | // An empty implementation adversely affects performance during animation. 16 | - (void)drawRect:(CGRect)rect { 17 | // Drawing code 18 | } 19 | */ 20 | 21 | - (CGFloat)max{ 22 | 23 | return (self.closeprice > _openprice) ? _closeprice : _openprice; 24 | 25 | } 26 | 27 | - (CGFloat)min{ 28 | 29 | return (self.closeprice > _openprice) ? _openprice : _closeprice; 30 | 31 | } 32 | 33 | - (BOOL)isRise{ 34 | return self.openprice >= self.closeprice; 35 | } 36 | 37 | @end 38 | -------------------------------------------------------------------------------- /CCLKLineChartView/kLineChart/CCLKLineShareData.h: -------------------------------------------------------------------------------- 1 | // 2 | // CCLKLineShareData.h 3 | // KlineChart 4 | // 5 | // Created by Crisps on 16/9/6. 6 | // Copyright © 2016年 cclion.cc. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @interface CCLKLineShareData : NSObject 13 | 14 | @property (nonatomic, assign) CGFloat aboveMax; 15 | 16 | @property (nonatomic, assign) CGFloat abovemin; 17 | 18 | @property (nonatomic, assign) CGFloat currHeight; 19 | 20 | @property (nonatomic, assign) CGFloat aboveHeight; 21 | 22 | @property (nonatomic, assign) CGFloat aboveWidth; 23 | 24 | @property (nonatomic, assign) CGFloat belowMax; 25 | 26 | @property (nonatomic, assign) CGFloat belowHeight; 27 | 28 | @property (nonatomic, assign) CGFloat belowWidth; 29 | 30 | @property (nonatomic, assign) CGFloat gap; 31 | 32 | @end 33 | -------------------------------------------------------------------------------- /CCLKLineChartView/kLineChart/CCLKLineShareData.m: -------------------------------------------------------------------------------- 1 | // 2 | // CCLKLineShareData.m 3 | // KlineChart 4 | // 5 | // Created by Crisps on 16/9/6. 6 | // Copyright © 2016年 cclion.cc. All rights reserved. 7 | // 8 | 9 | #import "CCLKLineShareData.h" 10 | 11 | @implementation CCLKLineShareData 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /CCLKLineChartView/timeChart/CCLTimeAboveView.h: -------------------------------------------------------------------------------- 1 | // 2 | // CCLTimeAboveView.h 3 | // KlineChart 4 | // 5 | // Created by Crisps on 16/9/28. 6 | // Copyright © 2016年 cclion.cc. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "CCLTimeShareData.h" 11 | 12 | @protocol CCLTimeAboveViewDelegate 13 | 14 | - (void)handleLongPress:(UILongPressGestureRecognizer *)gestureRecognizer forIndex:(NSUInteger)index andView:(UIView *)view; 15 | 16 | @end 17 | 18 | 19 | @interface CCLTimeAboveView : UIView 20 | 21 | @property (nonatomic, strong) CCLTimeShareData *shareData; 22 | 23 | @property (nonatomic, weak) id delegate; 24 | 25 | - (instancetype)initWithFrame:(CGRect)frame andShareData:(CCLTimeShareData *)shareData; 26 | 27 | - (void)drawWithLongPress:(UILongPressGestureRecognizer *)gestureRecognizer forIndex:(NSUInteger )index; 28 | 29 | @end 30 | -------------------------------------------------------------------------------- /CCLKLineChartView/timeChart/CCLTimeAboveView.m: -------------------------------------------------------------------------------- 1 | // 2 | // CCLTimeAboveView.m 3 | // KlineChart 4 | // 5 | // Created by Crisps on 16/9/28. 6 | // Copyright © 2016年 cclion.cc. All rights reserved. 7 | // 8 | 9 | #import "CCLTimeAboveView.h" 10 | #import "CCLTimeModel.h" 11 | #import "MJExtension.h" 12 | #import "CCLFrameTool.h" 13 | @interface CCLTimeAboveView () 14 | 15 | @property (nonatomic, strong) NSArray *dataArr; 16 | 17 | /** 18 | * 当前时间线 19 | */ 20 | @property (nonatomic, strong) CAShapeLayer *nowData; 21 | 22 | /** 23 | * 均线 24 | */ 25 | @property (nonatomic, strong) CAShapeLayer *avgData; 26 | 27 | /** 28 | * 中间分割线 29 | */ 30 | @property (nonatomic, strong) CAShapeLayer *midSeg; 31 | 32 | /** 33 | * 昨日收盘 34 | */ 35 | @property (nonatomic, assign) CGFloat closePrice; 36 | 37 | /** 38 | * 最高价 39 | */ 40 | @property (nonatomic, assign) CGFloat maxPrice; 41 | 42 | /** 43 | * 最底价 44 | */ 45 | @property (nonatomic, assign) CGFloat minPrice; 46 | 47 | /** 48 | * 长按信息横向 49 | */ 50 | @property (nonatomic, strong) CAShapeLayer *horizontalLine; 51 | 52 | /** 53 | * 长按信息纵向 54 | */ 55 | @property (nonatomic, strong) CAShapeLayer *verticalLine; 56 | 57 | /** 58 | * 选中的row 59 | */ 60 | @property (nonatomic, assign) NSUInteger selIndex; 61 | 62 | /** 63 | * 阴影 64 | */ 65 | @property (nonatomic, strong) CAShapeLayer *shadowLayer; 66 | 67 | @end 68 | 69 | 70 | @implementation CCLTimeAboveView 71 | 72 | - (NSArray *)dataArr{ 73 | if (_dataArr == nil) { 74 | _dataArr = [NSArray array]; 75 | } 76 | return _dataArr; 77 | } 78 | 79 | - (CAShapeLayer *)nowData{ 80 | if (_nowData == nil) { 81 | _nowData = [CAShapeLayer layer]; 82 | _nowData.lineWidth = 1; 83 | _nowData.lineJoin = kCALineJoinRound; 84 | _nowData.lineCap = kCALineCapRound; 85 | _nowData.strokeColor = [UIColor blueColor].CGColor; 86 | _nowData.fillColor = [UIColor clearColor].CGColor; 87 | } 88 | return _nowData; 89 | } 90 | 91 | - (CAShapeLayer *)shadowLayer{ 92 | if (_shadowLayer == nil) { 93 | _shadowLayer = [CAShapeLayer layer]; 94 | _shadowLayer.lineWidth = 1; 95 | _shadowLayer.lineJoin = kCALineJoinRound; 96 | _shadowLayer.lineCap = kCALineCapRound; 97 | _shadowLayer.fillColor = [[UIColor alloc] initWithRed:189.f/255.f green:207.f/255.f blue:251.f/255.f alpha:0.8].CGColor; 98 | } 99 | return _shadowLayer; 100 | } 101 | 102 | 103 | - (CAShapeLayer *)avgData{ 104 | if (_avgData == nil) { 105 | _avgData = [CAShapeLayer layer]; 106 | _avgData.lineWidth = 1; 107 | _avgData.lineJoin = kCALineJoinRound; 108 | _avgData.lineCap = kCALineCapRound; 109 | _avgData.strokeColor = [UIColor blueColor].CGColor; 110 | } 111 | return _avgData; 112 | } 113 | - (CAShapeLayer *)horizontalLine{ 114 | 115 | if (_horizontalLine == nil) { 116 | _horizontalLine = [CAShapeLayer layer]; 117 | _horizontalLine.strokeColor = [UIColor blueColor].CGColor; 118 | _horizontalLine.lineWidth = 0.5; 119 | } 120 | return _horizontalLine; 121 | } 122 | 123 | - (CAShapeLayer *)verticalLine{ 124 | 125 | if (_verticalLine == nil) { 126 | _verticalLine = [CAShapeLayer layer]; 127 | _verticalLine.strokeColor = [UIColor blueColor].CGColor; 128 | _verticalLine.lineWidth = 0.5; 129 | } 130 | return _verticalLine; 131 | } 132 | 133 | - (CAShapeLayer *)midSeg{ 134 | if (_midSeg == nil) { 135 | _midSeg = [CAShapeLayer layer]; 136 | _midSeg.lineWidth = 1; 137 | _midSeg.lineJoin = kCALineJoinRound; 138 | _midSeg.lineCap = kCALineCapRound; 139 | _midSeg.strokeColor = [UIColor blueColor].CGColor; 140 | 141 | } 142 | return _midSeg; 143 | } 144 | 145 | 146 | - (instancetype)initWithFrame:(CGRect)frame andShareData:(CCLTimeShareData *)shareData{ 147 | if (self = [super initWithFrame:frame]) { 148 | _shareData = shareData; 149 | _closePrice = 9.06; 150 | NSString * path =[[NSBundle mainBundle]pathForResource:@"timeData.plist" ofType:nil]; 151 | NSDictionary *dataDict = [[NSDictionary dictionaryWithContentsOfFile:path] objectForKey:@"data"]; 152 | NSArray *sourceArray = dataDict[@"barBodys"]; 153 | self.dataArr = [CCLTimeModel mj_objectArrayWithKeyValuesArray:sourceArray]; 154 | NSLog(@"%@",self.dataArr); 155 | 156 | [self drawBackLine]; 157 | [self drawDataLine]; 158 | 159 | UILongPressGestureRecognizer *longPressReger = [[UILongPressGestureRecognizer alloc] initWithTarget:self 160 | action:@selector(handleLongPress:)]; 161 | longPressReger.minimumPressDuration = 0.3f; 162 | longPressReger.delegate = self; 163 | [self addGestureRecognizer:longPressReger]; 164 | } 165 | return self; 166 | } 167 | 168 | #pragma mark - 长按手势 169 | -(void)handleLongPress:(UILongPressGestureRecognizer *)gestureRecognizer 170 | { 171 | 172 | CGPoint p = [gestureRecognizer locationInView:self]; 173 | 174 | 175 | 176 | int index = p.x / (_shareData.allWidth / 241); 177 | NSLog(@"%d",index); 178 | [self drawWithLongPress:gestureRecognizer forIndex:index]; 179 | if ([self.delegate respondsToSelector:@selector(handleLongPress:forIndex:andView:)]) { 180 | [self.delegate handleLongPress:gestureRecognizer forIndex:index andView:self]; 181 | } 182 | } 183 | 184 | - (void)drawWithLongPress:(UILongPressGestureRecognizer *)gestureRecognizer forIndex:(NSUInteger )index{ 185 | 186 | 187 | if (gestureRecognizer.state == 1) { 188 | 189 | [self reloadCrossLineWith:index]; 190 | self.selIndex = index; 191 | }else if (gestureRecognizer.state == 3){ 192 | 193 | self.horizontalLine.path = nil; 194 | self.verticalLine.path = nil; 195 | 196 | // [self.horizontalLine removeFromSuperlayer]; 197 | 198 | 199 | }else{ 200 | if (index != self.selIndex) { 201 | self.selIndex = index; 202 | [self reloadCrossLineWith:index]; 203 | } 204 | } 205 | 206 | } 207 | - (void)reloadCrossLineWith:(NSUInteger)index{ 208 | 209 | CCLTimeModel *model = self.dataArr[index]; 210 | CGPoint point = [CCLFrameTool getPointForValue:model.closePrice 211 | index:index 212 | width:_shareData.allWidth 213 | rangeBig:self.maxPrice 214 | rangeSma:self.minPrice 215 | length:_shareData.aboveHeight 216 | andGap:_shareData.gap]; 217 | 218 | // 计算出中心点 219 | 220 | CGFloat y = point.y; 221 | CGFloat x = point.x; 222 | 223 | UIBezierPath *path_h = [[UIBezierPath alloc] init]; 224 | [path_h moveToPoint:CGPointMake(0, y)]; 225 | [path_h addLineToPoint:CGPointMake(self.shareData.allWidth, y)]; 226 | 227 | self.horizontalLine.path =path_h.CGPath; 228 | [self.layer addSublayer: self.horizontalLine]; 229 | 230 | UIBezierPath *path_v = [[UIBezierPath alloc] init]; 231 | [path_v moveToPoint:CGPointMake(x, 0)]; 232 | [path_v addLineToPoint:CGPointMake(x, _shareData.aboveHeight)]; 233 | 234 | self.verticalLine.path =path_v.CGPath; 235 | [self.layer addSublayer: self.verticalLine]; 236 | 237 | } 238 | 239 | - (void)drawDataLine{ 240 | 241 | if (self.dataArr.count == 0) { 242 | return; 243 | } 244 | if (_closePrice == 0) { 245 | return; 246 | } 247 | 248 | CGFloat maxPrice = 0.0; 249 | CGFloat minPrice = 0.0; 250 | 251 | for (int i = 0; i < self.dataArr.count; i++) { 252 | 253 | CCLTimeModel *model = self.dataArr[i]; 254 | 255 | if (i == 0) { 256 | maxPrice = model.closePrice; 257 | minPrice = model.closePrice; 258 | 259 | }else{ 260 | if ( model.closePrice > maxPrice) { 261 | maxPrice = model.closePrice; 262 | } 263 | if (model.closePrice < minPrice) { 264 | minPrice = model.closePrice; 265 | } 266 | } 267 | } 268 | 269 | //比较绝对值 270 | // CGFloat abs = maxPrice - _closePrice > _closePrice - minPrice? : _closePrice - minPrice; 271 | CGFloat abs; 272 | if ( maxPrice - _closePrice > _closePrice - minPrice) { 273 | abs = maxPrice - _closePrice; 274 | }else{ 275 | abs = maxPrice - _closePrice; 276 | } 277 | 278 | self.maxPrice = _closePrice + abs; 279 | self.minPrice = _closePrice - abs; 280 | 281 | UIBezierPath *pathNow = [[UIBezierPath alloc] init]; 282 | 283 | UIBezierPath *pathShadow = [[UIBezierPath alloc] init]; 284 | 285 | [pathShadow moveToPoint:CGPointMake(0, _shareData.aboveHeight)]; 286 | 287 | for (int i = 0; i < self.dataArr.count; i++) { 288 | 289 | CCLTimeModel *model = self.dataArr[i]; 290 | 291 | CGPoint point = [CCLFrameTool getPointForValue:model.closePrice 292 | index:i 293 | width:_shareData.allWidth 294 | rangeBig:self.maxPrice 295 | rangeSma:self.minPrice 296 | length:_shareData.aboveHeight 297 | andGap:_shareData.gap]; 298 | 299 | [pathShadow addLineToPoint:point]; 300 | 301 | if (i == 0) { 302 | [pathNow moveToPoint:point]; 303 | 304 | }else{ 305 | [pathNow addLineToPoint:point]; 306 | 307 | if (i == self.dataArr.count - 1) { 308 | [pathShadow addLineToPoint:CGPointMake(point.x, _shareData.aboveHeight)]; 309 | } 310 | 311 | } 312 | 313 | } 314 | self.nowData.path = pathNow.CGPath; 315 | //add it to our view 316 | [self.layer addSublayer: self.nowData]; 317 | 318 | self.shadowLayer.path = pathShadow.CGPath; 319 | [self.layer addSublayer:self.shadowLayer]; 320 | 321 | } 322 | 323 | 324 | - (void)drawBackLine{ 325 | 326 | // 中间线 327 | [self.midSeg setLineDashPattern: 328 | [NSArray arrayWithObjects:[NSNumber numberWithInt:5], 329 | [NSNumber numberWithInt:5],nil]]; 330 | 331 | CGMutablePathRef path = CGPathCreateMutable(); 332 | CGPathMoveToPoint(path, NULL, 0, _shareData.aboveHeight * 0.5); 333 | CGPathAddLineToPoint(path, NULL, _shareData.allWidth, _shareData.aboveHeight * 0.5); 334 | [self.midSeg setPath:path]; 335 | CGPathRelease(path); 336 | 337 | [self.layer addSublayer:self.midSeg]; 338 | } 339 | 340 | 341 | @end 342 | -------------------------------------------------------------------------------- /CCLKLineChartView/timeChart/CCLTimeBelowView.h: -------------------------------------------------------------------------------- 1 | // 2 | // CCLTimeBelowView.h 3 | // KlineChart 4 | // 5 | // Created by Crisps on 16/9/28. 6 | // Copyright © 2016年 cclion.cc. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "CCLTimeShareData.h" 11 | 12 | @protocol CCLTimeBelowViewDelegate 13 | 14 | - (void)handleLongPress:(UILongPressGestureRecognizer *)gestureRecognizer forIndex:(NSUInteger)index andView:(UIView *)view; 15 | 16 | @end 17 | 18 | @interface CCLTimeBelowView : UIView 19 | 20 | @property (nonatomic, strong) CCLTimeShareData *shareData; 21 | 22 | @property (nonatomic, weak) id delegate; 23 | 24 | - (instancetype)initWithFrame:(CGRect)frame andShareData:(CCLTimeShareData *)shareData; 25 | 26 | - (void)drawWithLongPress:(UILongPressGestureRecognizer *)gestureRecognizer forIndex:(NSUInteger )index; 27 | 28 | @end 29 | -------------------------------------------------------------------------------- /CCLKLineChartView/timeChart/CCLTimeBelowView.m: -------------------------------------------------------------------------------- 1 | // 2 | // CCLTimeBelowView.m 3 | // KlineChart 4 | // 5 | // Created by Crisps on 16/9/28. 6 | // Copyright © 2016年 cclion.cc. All rights reserved. 7 | // 8 | 9 | #import "CCLTimeBelowView.h" 10 | #import "CCLTimeModel.h" 11 | #import "MJExtension.h" 12 | #import "CCLFrameTool.h" 13 | 14 | @interface CCLTimeBelowView () 15 | 16 | @property (nonatomic, strong) NSArray *dataArr; 17 | 18 | /** 19 | * 最高价 20 | */ 21 | @property (nonatomic, assign) CGFloat maxVol; 22 | 23 | @property (nonatomic, strong) NSMutableArray *volArr; 24 | /** 25 | * 长按信息纵向 26 | */ 27 | @property (nonatomic, strong) CAShapeLayer *verticalLine; 28 | /** 29 | * 选中的row 30 | */ 31 | @property (nonatomic, assign) NSUInteger selIndex; 32 | 33 | @end 34 | 35 | @implementation CCLTimeBelowView 36 | 37 | - (NSArray *)dataArr{ 38 | if (_dataArr == nil) { 39 | _dataArr = [NSArray array]; 40 | } 41 | return _dataArr; 42 | } 43 | 44 | - (NSMutableArray *)volArr{ 45 | if (_volArr == nil) { 46 | _volArr = [NSMutableArray array]; 47 | } 48 | return _volArr; 49 | } 50 | 51 | - (CAShapeLayer *)verticalLine{ 52 | 53 | if (_verticalLine == nil) { 54 | _verticalLine = [CAShapeLayer layer]; 55 | _verticalLine.strokeColor = [UIColor blueColor].CGColor; 56 | _verticalLine.lineWidth = 0.5; 57 | } 58 | return _verticalLine; 59 | } 60 | 61 | - (instancetype)initWithFrame:(CGRect)frame andShareData:(CCLTimeShareData *)shareData{ 62 | if (self = [super initWithFrame:frame]) { 63 | _shareData = shareData; 64 | 65 | NSString * path =[[NSBundle mainBundle]pathForResource:@"timeData.plist" ofType:nil]; 66 | NSDictionary *dataDict = [[NSDictionary dictionaryWithContentsOfFile:path] objectForKey:@"data"]; 67 | NSArray *sourceArray = dataDict[@"barBodys"]; 68 | self.dataArr = [CCLTimeModel mj_objectArrayWithKeyValuesArray:sourceArray]; 69 | NSLog(@"%@",self.dataArr); 70 | 71 | [self drawDataLine]; 72 | 73 | UILongPressGestureRecognizer *longPressReger = [[UILongPressGestureRecognizer alloc] initWithTarget:self 74 | action:@selector(handleLongPress:)]; 75 | longPressReger.minimumPressDuration = 0.3f; 76 | longPressReger.delegate = self; 77 | [self addGestureRecognizer:longPressReger]; 78 | 79 | } 80 | return self; 81 | } 82 | 83 | #pragma mark - 长按手势 84 | -(void)handleLongPress:(UILongPressGestureRecognizer *)gestureRecognizer 85 | { 86 | 87 | CGPoint p = [gestureRecognizer locationInView:self]; 88 | 89 | 90 | 91 | int index = p.x / (_shareData.allWidth / 241); 92 | NSLog(@"%d",index); 93 | [self drawWithLongPress:gestureRecognizer forIndex:index]; 94 | if ([self.delegate respondsToSelector:@selector(handleLongPress:forIndex:andView:)]) { 95 | [self.delegate handleLongPress:gestureRecognizer forIndex:index andView:self]; 96 | } 97 | } 98 | 99 | - (void)drawWithLongPress:(UILongPressGestureRecognizer *)gestureRecognizer forIndex:(NSUInteger )index{ 100 | 101 | 102 | if (gestureRecognizer.state == 1) { 103 | 104 | [self reloadCrossLineWith:index]; 105 | self.selIndex = index; 106 | }else if (gestureRecognizer.state == 3){ 107 | 108 | self.verticalLine.path = nil; 109 | 110 | // [self.horizontalLine removeFromSuperlayer]; 111 | 112 | }else{ 113 | if (index != self.selIndex) { 114 | self.selIndex = index; 115 | [self reloadCrossLineWith:index]; 116 | } 117 | } 118 | 119 | } 120 | 121 | - (void)reloadCrossLineWith:(NSUInteger)index{ 122 | 123 | CGFloat itemW = _shareData.allWidth / 241; 124 | CGFloat x = itemW * 0.5 + itemW * index; 125 | 126 | // 计算出中心点 127 | 128 | UIBezierPath *path_v = [[UIBezierPath alloc] init]; 129 | [path_v moveToPoint:CGPointMake(x, 0)]; 130 | [path_v addLineToPoint:CGPointMake(x, _shareData.belowHeight)]; 131 | 132 | self.verticalLine.path =path_v.CGPath; 133 | [self.layer addSublayer: self.verticalLine]; 134 | 135 | } 136 | 137 | 138 | 139 | 140 | - (void)drawDataLine{ 141 | 142 | if (self.dataArr.count == 0) { 143 | return; 144 | } 145 | 146 | 147 | CGFloat maxVol = 0.0; 148 | 149 | 150 | 151 | for (int i = 0; i < self.dataArr.count; i++) { 152 | 153 | CCLTimeModel *model = self.dataArr[i]; 154 | 155 | if (i == 0) { 156 | maxVol = model.totalVolume; 157 | 158 | }else{ 159 | if ( model.totalVolume > maxVol) { 160 | maxVol = model.totalVolume; 161 | } 162 | } 163 | } 164 | 165 | self.maxVol = maxVol; 166 | 167 | [self removeSubLayrs]; 168 | 169 | NSMutableArray *muVolArr = [NSMutableArray array]; 170 | 171 | for (int i = 0; i < self.dataArr.count; i++) { 172 | 173 | CCLTimeModel *model = self.dataArr[i]; 174 | 175 | 176 | CGRect rect = [CCLFrameTool getRectForValue:model.totalVolume 177 | index:i 178 | width:_shareData.allWidth 179 | rangeBig:self.maxVol 180 | rangeSma:0 181 | andLength:_shareData.belowHeight]; 182 | 183 | CAShapeLayer *layer = [CAShapeLayer layer]; 184 | layer.fillColor = [UIColor redColor].CGColor; 185 | layer.lineWidth = 0.1; 186 | UIBezierPath *path = [UIBezierPath bezierPathWithRect:rect]; 187 | layer.path = path.CGPath; 188 | 189 | 190 | NSLog(@"%@",NSStringFromCGRect(rect)); 191 | [self.layer addSublayer:layer]; 192 | 193 | [muVolArr addObject:layer]; 194 | } 195 | 196 | self.volArr = muVolArr; 197 | 198 | 199 | 200 | } 201 | 202 | - (void)removeSubLayrs{ 203 | 204 | if (self.volArr.count == 0) { 205 | return; 206 | } 207 | 208 | for (CAShapeLayer *layer in self.volArr) { 209 | [layer removeFromSuperlayer]; 210 | } 211 | 212 | } 213 | 214 | @end 215 | -------------------------------------------------------------------------------- /CCLKLineChartView/timeChart/CCLTimeModel.h: -------------------------------------------------------------------------------- 1 | // 2 | // CCLTimeModel.h 3 | // KlineChart 4 | // 5 | // Created by Crisps on 16/10/8. 6 | // Copyright © 2016年 cclion.cc. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | @interface CCLTimeModel : NSObject 12 | 13 | @property (nonatomic, copy) NSString *MAvgData; 14 | 15 | @property (nonatomic, copy) NSString *barTime; 16 | 17 | 18 | @property (nonatomic, assign) CGFloat closePrice; 19 | 20 | @property (nonatomic, assign) CGFloat highPrice; 21 | 22 | @property (nonatomic, assign) CGFloat lowPrice; 23 | 24 | @property (nonatomic, assign) CGFloat openPrice; 25 | 26 | @property (nonatomic, assign) CGFloat totalValue; 27 | 28 | @property (nonatomic, assign) CGFloat totalVolume; 29 | 30 | @end 31 | -------------------------------------------------------------------------------- /CCLKLineChartView/timeChart/CCLTimeModel.m: -------------------------------------------------------------------------------- 1 | // 2 | // CCLTimeModel.m 3 | // KlineChart 4 | // 5 | // Created by Crisps on 16/10/8. 6 | // Copyright © 2016年 cclion.cc. All rights reserved. 7 | // 8 | 9 | #import "CCLTimeModel.h" 10 | 11 | @implementation CCLTimeModel 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /CCLKLineChartView/timeChart/CCLTimeShareData.h: -------------------------------------------------------------------------------- 1 | // 2 | // CCLTimeShareData.h 3 | // KlineChart 4 | // 5 | // Created by Crisps on 16/10/8. 6 | // Copyright © 2016年 cclion.cc. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | @interface CCLTimeShareData : NSObject 12 | 13 | @property (nonatomic, assign) CGFloat aboveMax; 14 | 15 | @property (nonatomic, assign) CGFloat abovemin; 16 | 17 | @property (nonatomic, assign) CGFloat allWidth; 18 | 19 | @property (nonatomic, assign) CGFloat aboveHeight; 20 | 21 | @property (nonatomic, assign) CGFloat belowHeight; 22 | 23 | @property (nonatomic, assign) CGFloat gap; 24 | 25 | @end 26 | -------------------------------------------------------------------------------- /CCLKLineChartView/timeChart/CCLTimeShareData.m: -------------------------------------------------------------------------------- 1 | // 2 | // CCLTimeShareData.m 3 | // KlineChart 4 | // 5 | // Created by Crisps on 16/10/8. 6 | // Copyright © 2016年 cclion.cc. All rights reserved. 7 | // 8 | 9 | #import "CCLTimeShareData.h" 10 | 11 | @implementation CCLTimeShareData 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /CCLKLineChartView/timeChart/CCLTimeView.h: -------------------------------------------------------------------------------- 1 | // 2 | // CCLTimeView.h 3 | // KlineChart 4 | // 5 | // Created by Crisps on 16/9/28. 6 | // Copyright © 2016年 cclion.cc. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface CCLTimeView : UIView 12 | 13 | - (instancetype)initWithFrame:(CGRect)frame andSecID:(NSString *)secID; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /CCLKLineChartView/timeChart/CCLTimeView.m: -------------------------------------------------------------------------------- 1 | // 2 | // CCLTimeView.m 3 | // KlineChart 4 | // 5 | // Created by Crisps on 16/9/28. 6 | // Copyright © 2016年 cclion.cc. All rights reserved. 7 | // 8 | 9 | #import "CCLTimeView.h" 10 | #import "CCLTimeAboveView.h" 11 | #import "CCLTimeBelowView.h" 12 | #import "CCLTimeShareData.h" 13 | 14 | @interface CCLTimeView () 15 | 17 | 18 | @property (nonatomic, strong) CCLTimeShareData *shareData; 19 | 20 | @property (nonatomic, strong) CCLTimeAboveView *aboveView; 21 | 22 | @property (nonatomic, strong) CCLTimeBelowView *belowView; 23 | 24 | @end 25 | 26 | 27 | @implementation CCLTimeView 28 | 29 | - (instancetype)initWithFrame:(CGRect)frame andSecID:(NSString *)secID{ 30 | if (self = [super initWithFrame:frame]) { 31 | 32 | [self addSubview:self.aboveView]; 33 | [self addSubview:self.belowView]; 34 | 35 | self.layer.borderWidth = 1; 36 | self.layer.borderColor = [UIColor blackColor].CGColor; 37 | } 38 | return self; 39 | } 40 | 41 | - (CCLTimeAboveView *)aboveView{ 42 | if (_aboveView == nil) { 43 | _aboveView = [[CCLTimeAboveView alloc] initWithFrame:CGRectMake(0, 0, self.shareData.allWidth, self.shareData.aboveHeight) andShareData:self.shareData]; 44 | _aboveView.layer.borderWidth = 1; 45 | _aboveView.layer.borderColor = [UIColor blackColor].CGColor; 46 | _aboveView.delegate = self; 47 | } 48 | return _aboveView; 49 | } 50 | 51 | - (CCLTimeBelowView *)belowView{ 52 | if (_belowView == nil) { 53 | _belowView = [[CCLTimeBelowView alloc] initWithFrame:CGRectMake(0, self.shareData.aboveHeight + 20, self.shareData.allWidth, self.shareData.belowHeight) andShareData:self.shareData]; 54 | _belowView.layer.borderWidth = 1; 55 | _belowView.layer.borderColor = [UIColor blackColor].CGColor; 56 | _belowView.delegate = self; 57 | } 58 | return _belowView; 59 | } 60 | 61 | #pragma mark - 长按 62 | - (void)handleLongPress:(UILongPressGestureRecognizer *)gestureRecognizer forIndex:(NSUInteger)index andView:(UIView *)view{ 63 | if (view == self.aboveView) { 64 | [self.belowView drawWithLongPress:gestureRecognizer forIndex:index]; 65 | 66 | }else if (view == self.belowView){ 67 | [self.aboveView drawWithLongPress:gestureRecognizer forIndex:index]; 68 | } 69 | } 70 | 71 | 72 | - (CCLTimeShareData *)shareData{ 73 | if (_shareData == nil) { 74 | _shareData = [[CCLTimeShareData alloc] init]; 75 | 76 | _shareData.allWidth = self.bounds.size.width; 77 | 78 | _shareData.aboveHeight = self.bounds.size.height * 0.6; 79 | _shareData.belowHeight = self.bounds.size.height * 0.3; 80 | 81 | _shareData.gap = 10; 82 | 83 | } 84 | return _shareData; 85 | } 86 | 87 | 88 | @end 89 | -------------------------------------------------------------------------------- /CCLKLineChartViewDemo/CCLKLineChartViewDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /CCLKLineChartViewDemo/CCLKLineChartViewDemo.xcodeproj/project.xcworkspace/xcuserdata/Crisps_13.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cclion/CCLKLineChartView/52b27848e37ef4c7da859ae35446a234623e4983/CCLKLineChartViewDemo/CCLKLineChartViewDemo.xcodeproj/project.xcworkspace/xcuserdata/Crisps_13.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /CCLKLineChartViewDemo/CCLKLineChartViewDemo.xcodeproj/xcuserdata/Crisps_13.xcuserdatad/xcschemes/CCLKLineChartViewDemo.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 43 | 49 | 50 | 51 | 52 | 53 | 59 | 60 | 61 | 62 | 63 | 64 | 74 | 76 | 82 | 83 | 84 | 85 | 86 | 87 | 93 | 95 | 101 | 102 | 103 | 104 | 106 | 107 | 110 | 111 | 112 | -------------------------------------------------------------------------------- /CCLKLineChartViewDemo/CCLKLineChartViewDemo.xcodeproj/xcuserdata/Crisps_13.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | CCLKLineChartViewDemo.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | DFFE64641DC1D626007673D8 16 | 17 | primary 18 | 19 | 20 | DFFE647D1DC1D627007673D8 21 | 22 | primary 23 | 24 | 25 | DFFE64881DC1D627007673D8 26 | 27 | primary 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /CCLKLineChartViewDemo/CCLKLineChartViewDemo/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // CCLKLineChartViewDemo 4 | // 5 | // Created by Crisps on 16/10/27. 6 | // Copyright © 2016年 来一碗鸭汤. 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 | -------------------------------------------------------------------------------- /CCLKLineChartViewDemo/CCLKLineChartViewDemo/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // CCLKLineChartViewDemo 4 | // 5 | // Created by Crisps on 16/10/27. 6 | // Copyright © 2016年 来一碗鸭汤. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | 11 | @interface AppDelegate () 12 | 13 | @end 14 | 15 | @implementation AppDelegate 16 | 17 | 18 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 19 | // Override point for customization after application launch. 20 | return YES; 21 | } 22 | 23 | 24 | - (void)applicationWillResignActive:(UIApplication *)application { 25 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 26 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. 27 | } 28 | 29 | 30 | - (void)applicationDidEnterBackground:(UIApplication *)application { 31 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 32 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 33 | } 34 | 35 | 36 | - (void)applicationWillEnterForeground:(UIApplication *)application { 37 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background. 38 | } 39 | 40 | 41 | - (void)applicationDidBecomeActive:(UIApplication *)application { 42 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 43 | } 44 | 45 | 46 | - (void)applicationWillTerminate:(UIApplication *)application { 47 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 48 | } 49 | 50 | 51 | @end 52 | -------------------------------------------------------------------------------- /CCLKLineChartViewDemo/CCLKLineChartViewDemo/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "29x29", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "40x40", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "40x40", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "60x60", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "60x60", 31 | "scale" : "3x" 32 | } 33 | ], 34 | "info" : { 35 | "version" : 1, 36 | "author" : "xcode" 37 | } 38 | } -------------------------------------------------------------------------------- /CCLKLineChartViewDemo/CCLKLineChartViewDemo/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 | -------------------------------------------------------------------------------- /CCLKLineChartViewDemo/CCLKLineChartViewDemo/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 | -------------------------------------------------------------------------------- /CCLKLineChartViewDemo/CCLKLineChartViewDemo/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 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /CCLKLineChartViewDemo/CCLKLineChartViewDemo/MJExtension/MJExtension.h: -------------------------------------------------------------------------------- 1 | // 2 | // MJExtension.h 3 | // MJExtension 4 | // 5 | // Created by mj on 14-1-15. 6 | // Copyright (c) 2014年 小码哥. All rights reserved. 7 | // 代码地址:https://github.com/CoderMJLee/MJExtension 8 | // 代码地址:http://code4app.com/ios/%E5%AD%97%E5%85%B8-JSON-%E4%B8%8E%E6%A8%A1%E5%9E%8B%E7%9A%84%E8%BD%AC%E6%8D%A2/5339992a933bf062608b4c57 9 | 10 | #import "NSObject+MJCoding.h" 11 | #import "NSObject+MJProperty.h" 12 | #import "NSObject+MJClass.h" 13 | #import "NSObject+MJKeyValue.h" 14 | #import "NSString+MJExtension.h" 15 | #import "MJExtensionConst.h" -------------------------------------------------------------------------------- /CCLKLineChartViewDemo/CCLKLineChartViewDemo/MJExtension/MJExtensionConst.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef __MJExtensionConst__H__ 3 | #define __MJExtensionConst__H__ 4 | 5 | #import 6 | 7 | // 过期 8 | #define MJExtensionDeprecated(instead) NS_DEPRECATED(2_0, 2_0, 2_0, 2_0, instead) 9 | 10 | // 构建错误 11 | #define MJExtensionBuildError(clazz, msg) \ 12 | NSError *error = [NSError errorWithDomain:msg code:250 userInfo:nil]; \ 13 | [clazz setMj_error:error]; 14 | 15 | // 日志输出 16 | #ifdef DEBUG 17 | #define MJExtensionLog(...) NSLog(__VA_ARGS__) 18 | #else 19 | #define MJExtensionLog(...) 20 | #endif 21 | 22 | /** 23 | * 断言 24 | * @param condition 条件 25 | * @param returnValue 返回值 26 | */ 27 | #define MJExtensionAssertError(condition, returnValue, clazz, msg) \ 28 | [clazz setMj_error:nil]; \ 29 | if ((condition) == NO) { \ 30 | MJExtensionBuildError(clazz, msg); \ 31 | return returnValue;\ 32 | } 33 | 34 | #define MJExtensionAssert2(condition, returnValue) \ 35 | if ((condition) == NO) return returnValue; 36 | 37 | /** 38 | * 断言 39 | * @param condition 条件 40 | */ 41 | #define MJExtensionAssert(condition) MJExtensionAssert2(condition, ) 42 | 43 | /** 44 | * 断言 45 | * @param param 参数 46 | * @param returnValue 返回值 47 | */ 48 | #define MJExtensionAssertParamNotNil2(param, returnValue) \ 49 | MJExtensionAssert2((param) != nil, returnValue) 50 | 51 | /** 52 | * 断言 53 | * @param param 参数 54 | */ 55 | #define MJExtensionAssertParamNotNil(param) MJExtensionAssertParamNotNil2(param, ) 56 | 57 | /** 58 | * 打印所有的属性 59 | */ 60 | #define MJLogAllIvars \ 61 | -(NSString *)description \ 62 | { \ 63 | return [self mj_keyValues].description; \ 64 | } 65 | #define MJExtensionLogAllProperties MJLogAllIvars 66 | 67 | /** 68 | * 类型(属性类型) 69 | */ 70 | extern NSString *const MJPropertyTypeInt; 71 | extern NSString *const MJPropertyTypeShort; 72 | extern NSString *const MJPropertyTypeFloat; 73 | extern NSString *const MJPropertyTypeDouble; 74 | extern NSString *const MJPropertyTypeLong; 75 | extern NSString *const MJPropertyTypeLongLong; 76 | extern NSString *const MJPropertyTypeChar; 77 | extern NSString *const MJPropertyTypeBOOL1; 78 | extern NSString *const MJPropertyTypeBOOL2; 79 | extern NSString *const MJPropertyTypePointer; 80 | 81 | extern NSString *const MJPropertyTypeIvar; 82 | extern NSString *const MJPropertyTypeMethod; 83 | extern NSString *const MJPropertyTypeBlock; 84 | extern NSString *const MJPropertyTypeClass; 85 | extern NSString *const MJPropertyTypeSEL; 86 | extern NSString *const MJPropertyTypeId; 87 | 88 | #endif -------------------------------------------------------------------------------- /CCLKLineChartViewDemo/CCLKLineChartViewDemo/MJExtension/MJExtensionConst.m: -------------------------------------------------------------------------------- 1 | #ifndef __MJExtensionConst__M__ 2 | #define __MJExtensionConst__M__ 3 | 4 | #import 5 | 6 | /** 7 | * 成员变量类型(属性类型) 8 | */ 9 | NSString *const MJPropertyTypeInt = @"i"; 10 | NSString *const MJPropertyTypeShort = @"s"; 11 | NSString *const MJPropertyTypeFloat = @"f"; 12 | NSString *const MJPropertyTypeDouble = @"d"; 13 | NSString *const MJPropertyTypeLong = @"l"; 14 | NSString *const MJPropertyTypeLongLong = @"q"; 15 | NSString *const MJPropertyTypeChar = @"c"; 16 | NSString *const MJPropertyTypeBOOL1 = @"c"; 17 | NSString *const MJPropertyTypeBOOL2 = @"b"; 18 | NSString *const MJPropertyTypePointer = @"*"; 19 | 20 | NSString *const MJPropertyTypeIvar = @"^{objc_ivar=}"; 21 | NSString *const MJPropertyTypeMethod = @"^{objc_method=}"; 22 | NSString *const MJPropertyTypeBlock = @"@?"; 23 | NSString *const MJPropertyTypeClass = @"#"; 24 | NSString *const MJPropertyTypeSEL = @":"; 25 | NSString *const MJPropertyTypeId = @"@"; 26 | 27 | #endif -------------------------------------------------------------------------------- /CCLKLineChartViewDemo/CCLKLineChartViewDemo/MJExtension/MJFoundation.h: -------------------------------------------------------------------------------- 1 | // 2 | // MJFoundation.h 3 | // MJExtensionExample 4 | // 5 | // Created by MJ Lee on 14/7/16. 6 | // Copyright (c) 2014年 小码哥. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface MJFoundation : NSObject 12 | + (BOOL)isClassFromFoundation:(Class)c; 13 | @end 14 | -------------------------------------------------------------------------------- /CCLKLineChartViewDemo/CCLKLineChartViewDemo/MJExtension/MJFoundation.m: -------------------------------------------------------------------------------- 1 | // 2 | // MJFoundation.m 3 | // MJExtensionExample 4 | // 5 | // Created by MJ Lee on 14/7/16. 6 | // Copyright (c) 2014年 小码哥. All rights reserved. 7 | // 8 | 9 | #import "MJFoundation.h" 10 | #import "MJExtensionConst.h" 11 | #import 12 | 13 | static NSSet *foundationClasses_; 14 | 15 | @implementation MJFoundation 16 | 17 | + (NSSet *)foundationClasses 18 | { 19 | if (foundationClasses_ == nil) { 20 | // 集合中没有NSObject,因为几乎所有的类都是继承自NSObject,具体是不是NSObject需要特殊判断 21 | foundationClasses_ = [NSSet setWithObjects: 22 | [NSURL class], 23 | [NSDate class], 24 | [NSValue class], 25 | [NSData class], 26 | [NSError class], 27 | [NSArray class], 28 | [NSDictionary class], 29 | [NSString class], 30 | [NSAttributedString class], nil]; 31 | } 32 | return foundationClasses_; 33 | } 34 | 35 | + (BOOL)isClassFromFoundation:(Class)c 36 | { 37 | if (c == [NSObject class] || c == [NSManagedObject class]) return YES; 38 | 39 | __block BOOL result = NO; 40 | [[self foundationClasses] enumerateObjectsUsingBlock:^(Class foundationClass, BOOL *stop) { 41 | if ([c isSubclassOfClass:foundationClass]) { 42 | result = YES; 43 | *stop = YES; 44 | } 45 | }]; 46 | return result; 47 | } 48 | @end 49 | -------------------------------------------------------------------------------- /CCLKLineChartViewDemo/CCLKLineChartViewDemo/MJExtension/MJProperty.h: -------------------------------------------------------------------------------- 1 | // 2 | // MJProperty.h 3 | // MJExtensionExample 4 | // 5 | // Created by MJ Lee on 15/4/17. 6 | // Copyright (c) 2015年 小码哥. All rights reserved. 7 | // 包装一个成员属性 8 | 9 | #import 10 | #import 11 | #import "MJPropertyType.h" 12 | #import "MJPropertyKey.h" 13 | 14 | /** 15 | * 包装一个成员 16 | */ 17 | @interface MJProperty : NSObject 18 | /** 成员属性 */ 19 | @property (nonatomic, assign) objc_property_t property; 20 | /** 成员属性的名字 */ 21 | @property (nonatomic, readonly) NSString *name; 22 | 23 | /** 成员属性的类型 */ 24 | @property (nonatomic, readonly) MJPropertyType *type; 25 | /** 成员属性来源于哪个类(可能是父类) */ 26 | @property (nonatomic, assign) Class srcClass; 27 | 28 | /**** 同一个成员属性 - 父类和子类的行为可能不一致(originKey、propertyKeys、objectClassInArray) ****/ 29 | /** 设置最原始的key */ 30 | - (void)setOriginKey:(id)originKey forClass:(Class)c; 31 | /** 对应着字典中的多级key(里面存放的数组,数组里面都是MJPropertyKey对象) */ 32 | - (NSArray *)propertyKeysForClass:(Class)c; 33 | 34 | /** 模型数组中的模型类型 */ 35 | - (void)setObjectClassInArray:(Class)objectClass forClass:(Class)c; 36 | - (Class)objectClassInArrayForClass:(Class)c; 37 | /**** 同一个成员变量 - 父类和子类的行为可能不一致(key、keys、objectClassInArray) ****/ 38 | 39 | /** 40 | * 设置object的成员变量值 41 | */ 42 | - (void)setValue:(id)value forObject:(id)object; 43 | /** 44 | * 得到object的成员属性值 45 | */ 46 | - (id)valueForObject:(id)object; 47 | 48 | /** 49 | * 初始化 50 | */ 51 | + (instancetype)cachedPropertyWithProperty:(objc_property_t)property; 52 | 53 | @end 54 | -------------------------------------------------------------------------------- /CCLKLineChartViewDemo/CCLKLineChartViewDemo/MJExtension/MJProperty.m: -------------------------------------------------------------------------------- 1 | // 2 | // MJProperty.m 3 | // MJExtensionExample 4 | // 5 | // Created by MJ Lee on 15/4/17. 6 | // Copyright (c) 2015年 小码哥. All rights reserved. 7 | // 8 | 9 | #import "MJProperty.h" 10 | #import "MJFoundation.h" 11 | #import "MJExtensionConst.h" 12 | #import 13 | 14 | @interface MJProperty() 15 | @property (strong, nonatomic) NSMutableDictionary *propertyKeysDict; 16 | @property (strong, nonatomic) NSMutableDictionary *objectClassInArrayDict; 17 | @end 18 | 19 | @implementation MJProperty 20 | 21 | #pragma mark - 懒加载 22 | - (NSMutableDictionary *)propertyKeysDict 23 | { 24 | if (!_propertyKeysDict) { 25 | _propertyKeysDict = [NSMutableDictionary dictionary]; 26 | } 27 | return _propertyKeysDict; 28 | } 29 | 30 | - (NSMutableDictionary *)objectClassInArrayDict 31 | { 32 | if (!_objectClassInArrayDict) { 33 | _objectClassInArrayDict = [NSMutableDictionary dictionary]; 34 | } 35 | return _objectClassInArrayDict; 36 | } 37 | 38 | #pragma mark - 缓存 39 | + (instancetype)cachedPropertyWithProperty:(objc_property_t)property 40 | { 41 | MJProperty *propertyObj = objc_getAssociatedObject(self, property); 42 | if (propertyObj == nil) { 43 | propertyObj = [[self alloc] init]; 44 | propertyObj.property = property; 45 | objc_setAssociatedObject(self, property, propertyObj, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 46 | } 47 | return propertyObj; 48 | } 49 | 50 | #pragma mark - 公共方法 51 | - (void)setProperty:(objc_property_t)property 52 | { 53 | _property = property; 54 | 55 | MJExtensionAssertParamNotNil(property); 56 | 57 | // 1.属性名 58 | _name = @(property_getName(property)); 59 | 60 | // 2.成员类型 61 | NSString *attrs = @(property_getAttributes(property)); 62 | NSUInteger dotLoc = [attrs rangeOfString:@","].location; 63 | NSString *code = nil; 64 | NSUInteger loc = 1; 65 | if (dotLoc == NSNotFound) { // 没有, 66 | code = [attrs substringFromIndex:loc]; 67 | } else { 68 | code = [attrs substringWithRange:NSMakeRange(loc, dotLoc - loc)]; 69 | } 70 | _type = [MJPropertyType cachedTypeWithCode:code]; 71 | } 72 | 73 | /** 74 | * 获得成员变量的值 75 | */ 76 | - (id)valueForObject:(id)object 77 | { 78 | if (self.type.KVCDisabled) return [NSNull null]; 79 | return [object valueForKey:self.name]; 80 | } 81 | 82 | /** 83 | * 设置成员变量的值 84 | */ 85 | - (void)setValue:(id)value forObject:(id)object 86 | { 87 | if (self.type.KVCDisabled || value == nil) return; 88 | [object setValue:value forKey:self.name]; 89 | } 90 | 91 | /** 92 | * 通过字符串key创建对应的keys 93 | */ 94 | - (NSArray *)propertyKeysWithStringKey:(NSString *)stringKey 95 | { 96 | if (stringKey.length == 0) return nil; 97 | 98 | NSMutableArray *propertyKeys = [NSMutableArray array]; 99 | // 如果有多级映射 100 | NSArray *oldKeys = [stringKey componentsSeparatedByString:@"."]; 101 | 102 | for (NSString *oldKey in oldKeys) { 103 | NSUInteger start = [oldKey rangeOfString:@"["].location; 104 | if (start != NSNotFound) { // 有索引的key 105 | NSString *prefixKey = [oldKey substringToIndex:start]; 106 | NSString *indexKey = prefixKey; 107 | if (prefixKey.length) { 108 | MJPropertyKey *propertyKey = [[MJPropertyKey alloc] init]; 109 | propertyKey.name = prefixKey; 110 | [propertyKeys addObject:propertyKey]; 111 | 112 | indexKey = [oldKey stringByReplacingOccurrencesOfString:prefixKey withString:@""]; 113 | } 114 | 115 | /** 解析索引 **/ 116 | // 元素 117 | NSArray *cmps = [[indexKey stringByReplacingOccurrencesOfString:@"[" withString:@""] componentsSeparatedByString:@"]"]; 118 | for (NSInteger i = 0; i 10 | 11 | typedef enum { 12 | MJPropertyKeyTypeDictionary = 0, // 字典的key 13 | MJPropertyKeyTypeArray // 数组的key 14 | } MJPropertyKeyType; 15 | 16 | /** 17 | * 属性的key 18 | */ 19 | @interface MJPropertyKey : NSObject 20 | /** key的名字 */ 21 | @property (copy, nonatomic) NSString *name; 22 | /** key的种类,可能是@"10",可能是@"age" */ 23 | @property (assign, nonatomic) MJPropertyKeyType type; 24 | 25 | /** 26 | * 根据当前的key,也就是name,从object(字典或者数组)中取值 27 | */ 28 | - (id)valueInObject:(id)object; 29 | 30 | @end 31 | -------------------------------------------------------------------------------- /CCLKLineChartViewDemo/CCLKLineChartViewDemo/MJExtension/MJPropertyKey.m: -------------------------------------------------------------------------------- 1 | // 2 | // MJPropertyKey.m 3 | // MJExtensionExample 4 | // 5 | // Created by MJ Lee on 15/8/11. 6 | // Copyright (c) 2015年 小码哥. All rights reserved. 7 | // 8 | 9 | #import "MJPropertyKey.h" 10 | 11 | @implementation MJPropertyKey 12 | 13 | - (id)valueInObject:(id)object 14 | { 15 | if ([object isKindOfClass:[NSDictionary class]] && self.type == MJPropertyKeyTypeDictionary) { 16 | return object[self.name]; 17 | } else if ([object isKindOfClass:[NSArray class]] && self.type == MJPropertyKeyTypeArray) { 18 | NSArray *array = object; 19 | NSUInteger index = self.name.intValue; 20 | if (index < array.count) return array[index]; 21 | return nil; 22 | } 23 | return nil; 24 | } 25 | @end 26 | -------------------------------------------------------------------------------- /CCLKLineChartViewDemo/CCLKLineChartViewDemo/MJExtension/MJPropertyType.h: -------------------------------------------------------------------------------- 1 | // 2 | // MJPropertyType.h 3 | // MJExtension 4 | // 5 | // Created by mj on 14-1-15. 6 | // Copyright (c) 2014年 小码哥. All rights reserved. 7 | // 包装一种类型 8 | 9 | #import 10 | 11 | /** 12 | * 包装一种类型 13 | */ 14 | @interface MJPropertyType : NSObject 15 | /** 类型标识符 */ 16 | @property (nonatomic, copy) NSString *code; 17 | 18 | /** 是否为id类型 */ 19 | @property (nonatomic, readonly, getter=isIdType) BOOL idType; 20 | 21 | /** 是否为基本数字类型:int、float等 */ 22 | @property (nonatomic, readonly, getter=isNumberType) BOOL numberType; 23 | 24 | /** 是否为BOOL类型 */ 25 | @property (nonatomic, readonly, getter=isBoolType) BOOL boolType; 26 | 27 | /** 对象类型(如果是基本数据类型,此值为nil) */ 28 | @property (nonatomic, readonly) Class typeClass; 29 | 30 | /** 类型是否来自于Foundation框架,比如NSString、NSArray */ 31 | @property (nonatomic, readonly, getter = isFromFoundation) BOOL fromFoundation; 32 | /** 类型是否不支持KVC */ 33 | @property (nonatomic, readonly, getter = isKVCDisabled) BOOL KVCDisabled; 34 | 35 | /** 36 | * 获得缓存的类型对象 37 | */ 38 | + (instancetype)cachedTypeWithCode:(NSString *)code; 39 | @end -------------------------------------------------------------------------------- /CCLKLineChartViewDemo/CCLKLineChartViewDemo/MJExtension/MJPropertyType.m: -------------------------------------------------------------------------------- 1 | // 2 | // MJPropertyType.m 3 | // MJExtension 4 | // 5 | // Created by mj on 14-1-15. 6 | // Copyright (c) 2014年 小码哥. All rights reserved. 7 | // 8 | 9 | #import "MJPropertyType.h" 10 | #import "MJExtension.h" 11 | #import "MJFoundation.h" 12 | #import "MJExtensionConst.h" 13 | 14 | @implementation MJPropertyType 15 | 16 | static NSMutableDictionary *types_; 17 | + (void)initialize 18 | { 19 | types_ = [NSMutableDictionary dictionary]; 20 | } 21 | 22 | + (instancetype)cachedTypeWithCode:(NSString *)code 23 | { 24 | MJExtensionAssertParamNotNil2(code, nil); 25 | 26 | MJPropertyType *type = types_[code]; 27 | if (type == nil) { 28 | type = [[self alloc] init]; 29 | type.code = code; 30 | types_[code] = type; 31 | } 32 | return type; 33 | } 34 | 35 | #pragma mark - 公共方法 36 | - (void)setCode:(NSString *)code 37 | { 38 | _code = code; 39 | 40 | MJExtensionAssertParamNotNil(code); 41 | 42 | if ([code isEqualToString:MJPropertyTypeId]) { 43 | _idType = YES; 44 | } else if (code.length == 0) { 45 | _KVCDisabled = YES; 46 | } else if (code.length > 3 && [code hasPrefix:@"@\""]) { 47 | // 去掉@"和",截取中间的类型名称 48 | _code = [code substringWithRange:NSMakeRange(2, code.length - 3)]; 49 | _typeClass = NSClassFromString(_code); 50 | _fromFoundation = [MJFoundation isClassFromFoundation:_typeClass]; 51 | _numberType = [_typeClass isSubclassOfClass:[NSNumber class]]; 52 | 53 | } else if ([code isEqualToString:MJPropertyTypeSEL] || 54 | [code isEqualToString:MJPropertyTypeIvar] || 55 | [code isEqualToString:MJPropertyTypeMethod]) { 56 | _KVCDisabled = YES; 57 | } 58 | 59 | // 是否为数字类型 60 | NSString *lowerCode = _code.lowercaseString; 61 | NSArray *numberTypes = @[MJPropertyTypeInt, MJPropertyTypeShort, MJPropertyTypeBOOL1, MJPropertyTypeBOOL2, MJPropertyTypeFloat, MJPropertyTypeDouble, MJPropertyTypeLong, MJPropertyTypeLongLong, MJPropertyTypeChar]; 62 | if ([numberTypes containsObject:lowerCode]) { 63 | _numberType = YES; 64 | 65 | if ([lowerCode isEqualToString:MJPropertyTypeBOOL1] 66 | || [lowerCode isEqualToString:MJPropertyTypeBOOL2]) { 67 | _boolType = YES; 68 | } 69 | } 70 | } 71 | @end 72 | -------------------------------------------------------------------------------- /CCLKLineChartViewDemo/CCLKLineChartViewDemo/MJExtension/NSObject+MJClass.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSObject+MJClass.h 3 | // MJExtensionExample 4 | // 5 | // Created by MJ Lee on 15/8/11. 6 | // Copyright (c) 2015年 小码哥. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | /** 12 | * 遍历所有类的block(父类) 13 | */ 14 | typedef void (^MJClassesEnumeration)(Class c, BOOL *stop); 15 | 16 | /** 这个数组中的属性名才会进行字典和模型的转换 */ 17 | typedef NSArray * (^MJAllowedPropertyNames)(); 18 | /** 这个数组中的属性名才会进行归档 */ 19 | typedef NSArray * (^MJAllowedCodingPropertyNames)(); 20 | 21 | /** 这个数组中的属性名将会被忽略:不进行字典和模型的转换 */ 22 | typedef NSArray * (^MJIgnoredPropertyNames)(); 23 | /** 这个数组中的属性名将会被忽略:不进行归档 */ 24 | typedef NSArray * (^MJIgnoredCodingPropertyNames)(); 25 | 26 | /** 27 | * 类相关的扩展 28 | */ 29 | @interface NSObject (MJClass) 30 | /** 31 | * 遍历所有的类 32 | */ 33 | + (void)mj_enumerateClasses:(MJClassesEnumeration)enumeration; 34 | + (void)mj_enumerateAllClasses:(MJClassesEnumeration)enumeration; 35 | 36 | #pragma mark - 属性白名单配置 37 | /** 38 | * 这个数组中的属性名才会进行字典和模型的转换 39 | * 40 | * @param allowedPropertyNames 这个数组中的属性名才会进行字典和模型的转换 41 | */ 42 | + (void)mj_setupAllowedPropertyNames:(MJAllowedPropertyNames)allowedPropertyNames; 43 | 44 | /** 45 | * 这个数组中的属性名才会进行字典和模型的转换 46 | */ 47 | + (NSMutableArray *)mj_totalAllowedPropertyNames; 48 | 49 | #pragma mark - 属性黑名单配置 50 | /** 51 | * 这个数组中的属性名将会被忽略:不进行字典和模型的转换 52 | * 53 | * @param ignoredPropertyNames 这个数组中的属性名将会被忽略:不进行字典和模型的转换 54 | */ 55 | + (void)mj_setupIgnoredPropertyNames:(MJIgnoredPropertyNames)ignoredPropertyNames; 56 | 57 | /** 58 | * 这个数组中的属性名将会被忽略:不进行字典和模型的转换 59 | */ 60 | + (NSMutableArray *)mj_totalIgnoredPropertyNames; 61 | 62 | #pragma mark - 归档属性白名单配置 63 | /** 64 | * 这个数组中的属性名才会进行归档 65 | * 66 | * @param allowedCodingPropertyNames 这个数组中的属性名才会进行归档 67 | */ 68 | + (void)mj_setupAllowedCodingPropertyNames:(MJAllowedCodingPropertyNames)allowedCodingPropertyNames; 69 | 70 | /** 71 | * 这个数组中的属性名才会进行字典和模型的转换 72 | */ 73 | + (NSMutableArray *)mj_totalAllowedCodingPropertyNames; 74 | 75 | #pragma mark - 归档属性黑名单配置 76 | /** 77 | * 这个数组中的属性名将会被忽略:不进行归档 78 | * 79 | * @param ignoredCodingPropertyNames 这个数组中的属性名将会被忽略:不进行归档 80 | */ 81 | + (void)mj_setupIgnoredCodingPropertyNames:(MJIgnoredCodingPropertyNames)ignoredCodingPropertyNames; 82 | 83 | /** 84 | * 这个数组中的属性名将会被忽略:不进行归档 85 | */ 86 | + (NSMutableArray *)mj_totalIgnoredCodingPropertyNames; 87 | 88 | #pragma mark - 内部使用 89 | + (void)mj_setupBlockReturnValue:(id (^)())block key:(const char *)key; 90 | @end 91 | -------------------------------------------------------------------------------- /CCLKLineChartViewDemo/CCLKLineChartViewDemo/MJExtension/NSObject+MJClass.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSObject+MJClass.m 3 | // MJExtensionExample 4 | // 5 | // Created by MJ Lee on 15/8/11. 6 | // Copyright (c) 2015年 小码哥. All rights reserved. 7 | // 8 | 9 | #import "NSObject+MJClass.h" 10 | #import "NSObject+MJCoding.h" 11 | #import "NSObject+MJKeyValue.h" 12 | #import "MJFoundation.h" 13 | #import 14 | 15 | static const char MJAllowedPropertyNamesKey = '\0'; 16 | static const char MJIgnoredPropertyNamesKey = '\0'; 17 | static const char MJAllowedCodingPropertyNamesKey = '\0'; 18 | static const char MJIgnoredCodingPropertyNamesKey = '\0'; 19 | 20 | static NSMutableDictionary *allowedPropertyNamesDict_; 21 | static NSMutableDictionary *ignoredPropertyNamesDict_; 22 | static NSMutableDictionary *allowedCodingPropertyNamesDict_; 23 | static NSMutableDictionary *ignoredCodingPropertyNamesDict_; 24 | 25 | @implementation NSObject (MJClass) 26 | 27 | + (void)load 28 | { 29 | allowedPropertyNamesDict_ = [NSMutableDictionary dictionary]; 30 | ignoredPropertyNamesDict_ = [NSMutableDictionary dictionary]; 31 | allowedCodingPropertyNamesDict_ = [NSMutableDictionary dictionary]; 32 | ignoredCodingPropertyNamesDict_ = [NSMutableDictionary dictionary]; 33 | } 34 | 35 | + (NSMutableDictionary *)dictForKey:(const void *)key 36 | { 37 | if (key == &MJAllowedPropertyNamesKey) return allowedPropertyNamesDict_; 38 | if (key == &MJIgnoredPropertyNamesKey) return ignoredPropertyNamesDict_; 39 | if (key == &MJAllowedCodingPropertyNamesKey) return allowedCodingPropertyNamesDict_; 40 | if (key == &MJIgnoredCodingPropertyNamesKey) return ignoredCodingPropertyNamesDict_; 41 | return nil; 42 | } 43 | 44 | + (void)mj_enumerateClasses:(MJClassesEnumeration)enumeration 45 | { 46 | // 1.没有block就直接返回 47 | if (enumeration == nil) return; 48 | 49 | // 2.停止遍历的标记 50 | BOOL stop = NO; 51 | 52 | // 3.当前正在遍历的类 53 | Class c = self; 54 | 55 | // 4.开始遍历每一个类 56 | while (c && !stop) { 57 | // 4.1.执行操作 58 | enumeration(c, &stop); 59 | 60 | // 4.2.获得父类 61 | c = class_getSuperclass(c); 62 | 63 | if ([MJFoundation isClassFromFoundation:c]) break; 64 | } 65 | } 66 | 67 | + (void)mj_enumerateAllClasses:(MJClassesEnumeration)enumeration 68 | { 69 | // 1.没有block就直接返回 70 | if (enumeration == nil) return; 71 | 72 | // 2.停止遍历的标记 73 | BOOL stop = NO; 74 | 75 | // 3.当前正在遍历的类 76 | Class c = self; 77 | 78 | // 4.开始遍历每一个类 79 | while (c && !stop) { 80 | // 4.1.执行操作 81 | enumeration(c, &stop); 82 | 83 | // 4.2.获得父类 84 | c = class_getSuperclass(c); 85 | } 86 | } 87 | 88 | #pragma mark - 属性黑名单配置 89 | + (void)mj_setupIgnoredPropertyNames:(MJIgnoredPropertyNames)ignoredPropertyNames 90 | { 91 | [self mj_setupBlockReturnValue:ignoredPropertyNames key:&MJIgnoredPropertyNamesKey]; 92 | } 93 | 94 | + (NSMutableArray *)mj_totalIgnoredPropertyNames 95 | { 96 | return [self mj_totalObjectsWithSelector:@selector(mj_ignoredPropertyNames) key:&MJIgnoredPropertyNamesKey]; 97 | } 98 | 99 | #pragma mark - 归档属性黑名单配置 100 | + (void)mj_setupIgnoredCodingPropertyNames:(MJIgnoredCodingPropertyNames)ignoredCodingPropertyNames 101 | { 102 | [self mj_setupBlockReturnValue:ignoredCodingPropertyNames key:&MJIgnoredCodingPropertyNamesKey]; 103 | } 104 | 105 | + (NSMutableArray *)mj_totalIgnoredCodingPropertyNames 106 | { 107 | return [self mj_totalObjectsWithSelector:@selector(mj_ignoredCodingPropertyNames) key:&MJIgnoredCodingPropertyNamesKey]; 108 | } 109 | 110 | #pragma mark - 属性白名单配置 111 | + (void)mj_setupAllowedPropertyNames:(MJAllowedPropertyNames)allowedPropertyNames; 112 | { 113 | [self mj_setupBlockReturnValue:allowedPropertyNames key:&MJAllowedPropertyNamesKey]; 114 | } 115 | 116 | + (NSMutableArray *)mj_totalAllowedPropertyNames 117 | { 118 | return [self mj_totalObjectsWithSelector:@selector(mj_allowedPropertyNames) key:&MJAllowedPropertyNamesKey]; 119 | } 120 | 121 | #pragma mark - 归档属性白名单配置 122 | + (void)mj_setupAllowedCodingPropertyNames:(MJAllowedCodingPropertyNames)allowedCodingPropertyNames 123 | { 124 | [self mj_setupBlockReturnValue:allowedCodingPropertyNames key:&MJAllowedCodingPropertyNamesKey]; 125 | } 126 | 127 | + (NSMutableArray *)mj_totalAllowedCodingPropertyNames 128 | { 129 | return [self mj_totalObjectsWithSelector:@selector(mj_allowedCodingPropertyNames) key:&MJAllowedCodingPropertyNamesKey]; 130 | } 131 | #pragma mark - block和方法处理:存储block的返回值 132 | + (void)mj_setupBlockReturnValue:(id (^)())block key:(const char *)key 133 | { 134 | if (block) { 135 | objc_setAssociatedObject(self, key, block(), OBJC_ASSOCIATION_RETAIN_NONATOMIC); 136 | } else { 137 | objc_setAssociatedObject(self, key, nil, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 138 | } 139 | 140 | // 清空数据 141 | [[self dictForKey:key] removeAllObjects]; 142 | } 143 | 144 | + (NSMutableArray *)mj_totalObjectsWithSelector:(SEL)selector key:(const char *)key 145 | { 146 | NSMutableArray *array = [self dictForKey:key][NSStringFromClass(self)]; 147 | if (array) return array; 148 | 149 | // 创建、存储 150 | [self dictForKey:key][NSStringFromClass(self)] = array = [NSMutableArray array]; 151 | 152 | if ([self respondsToSelector:selector]) { 153 | #pragma clang diagnostic push 154 | #pragma clang diagnostic ignored "-Warc-performSelector-leaks" 155 | NSArray *subArray = [self performSelector:selector]; 156 | #pragma clang diagnostic pop 157 | if (subArray) { 158 | [array addObjectsFromArray:subArray]; 159 | } 160 | } 161 | 162 | [self mj_enumerateAllClasses:^(__unsafe_unretained Class c, BOOL *stop) { 163 | NSArray *subArray = objc_getAssociatedObject(c, key); 164 | [array addObjectsFromArray:subArray]; 165 | }]; 166 | return array; 167 | } 168 | @end 169 | -------------------------------------------------------------------------------- /CCLKLineChartViewDemo/CCLKLineChartViewDemo/MJExtension/NSObject+MJCoding.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSObject+MJCoding.h 3 | // MJExtension 4 | // 5 | // Created by mj on 14-1-15. 6 | // Copyright (c) 2014年 小码哥. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "MJExtensionConst.h" 11 | 12 | /** 13 | * Codeing协议 14 | */ 15 | @protocol MJCoding 16 | @optional 17 | /** 18 | * 这个数组中的属性名才会进行归档 19 | */ 20 | + (NSArray *)mj_allowedCodingPropertyNames; 21 | /** 22 | * 这个数组中的属性名将会被忽略:不进行归档 23 | */ 24 | + (NSArray *)mj_ignoredCodingPropertyNames; 25 | @end 26 | 27 | @interface NSObject (MJCoding) 28 | /** 29 | * 解码(从文件中解析对象) 30 | */ 31 | - (void)mj_decode:(NSCoder *)decoder; 32 | /** 33 | * 编码(将对象写入文件中) 34 | */ 35 | - (void)mj_encode:(NSCoder *)encoder; 36 | @end 37 | 38 | /** 39 | 归档的实现 40 | */ 41 | #define MJCodingImplementation \ 42 | - (id)initWithCoder:(NSCoder *)decoder \ 43 | { \ 44 | if (self = [super init]) { \ 45 | [self mj_decode:decoder]; \ 46 | } \ 47 | return self; \ 48 | } \ 49 | \ 50 | - (void)encodeWithCoder:(NSCoder *)encoder \ 51 | { \ 52 | [self mj_encode:encoder]; \ 53 | } 54 | 55 | #define MJExtensionCodingImplementation MJCodingImplementation -------------------------------------------------------------------------------- /CCLKLineChartViewDemo/CCLKLineChartViewDemo/MJExtension/NSObject+MJCoding.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSObject+MJCoding.m 3 | // MJExtension 4 | // 5 | // Created by mj on 14-1-15. 6 | // Copyright (c) 2014年 小码哥. All rights reserved. 7 | // 8 | 9 | #import "NSObject+MJCoding.h" 10 | #import "NSObject+MJClass.h" 11 | #import "NSObject+MJProperty.h" 12 | #import "MJProperty.h" 13 | 14 | @implementation NSObject (MJCoding) 15 | 16 | - (void)mj_encode:(NSCoder *)encoder 17 | { 18 | Class clazz = [self class]; 19 | 20 | NSArray *allowedCodingPropertyNames = [clazz mj_totalAllowedCodingPropertyNames]; 21 | NSArray *ignoredCodingPropertyNames = [clazz mj_totalIgnoredCodingPropertyNames]; 22 | 23 | [clazz mj_enumerateProperties:^(MJProperty *property, BOOL *stop) { 24 | // 检测是否被忽略 25 | if (allowedCodingPropertyNames.count && ![allowedCodingPropertyNames containsObject:property.name]) return; 26 | if ([ignoredCodingPropertyNames containsObject:property.name]) return; 27 | 28 | id value = [property valueForObject:self]; 29 | if (value == nil) return; 30 | [encoder encodeObject:value forKey:property.name]; 31 | }]; 32 | } 33 | 34 | - (void)mj_decode:(NSCoder *)decoder 35 | { 36 | Class clazz = [self class]; 37 | 38 | NSArray *allowedCodingPropertyNames = [clazz mj_totalAllowedCodingPropertyNames]; 39 | NSArray *ignoredCodingPropertyNames = [clazz mj_totalIgnoredCodingPropertyNames]; 40 | 41 | [clazz mj_enumerateProperties:^(MJProperty *property, BOOL *stop) { 42 | // 检测是否被忽略 43 | if (allowedCodingPropertyNames.count && ![allowedCodingPropertyNames containsObject:property.name]) return; 44 | if ([ignoredCodingPropertyNames containsObject:property.name]) return; 45 | 46 | id value = [decoder decodeObjectForKey:property.name]; 47 | if (value == nil) { // 兼容以前的MJExtension版本 48 | value = [decoder decodeObjectForKey:[@"_" stringByAppendingString:property.name]]; 49 | } 50 | if (value == nil) return; 51 | [property setValue:value forObject:self]; 52 | }]; 53 | } 54 | @end 55 | -------------------------------------------------------------------------------- /CCLKLineChartViewDemo/CCLKLineChartViewDemo/MJExtension/NSObject+MJKeyValue.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSObject+MJKeyValue.h 3 | // MJExtension 4 | // 5 | // Created by mj on 13-8-24. 6 | // Copyright (c) 2013年 小码哥. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "MJExtensionConst.h" 11 | #import 12 | #import "MJProperty.h" 13 | 14 | /** 15 | * KeyValue协议 16 | */ 17 | @protocol MJKeyValue 18 | @optional 19 | /** 20 | * 只有这个数组中的属性名才允许进行字典和模型的转换 21 | */ 22 | + (NSArray *)mj_allowedPropertyNames; 23 | 24 | /** 25 | * 这个数组中的属性名将会被忽略:不进行字典和模型的转换 26 | */ 27 | + (NSArray *)mj_ignoredPropertyNames; 28 | 29 | /** 30 | * 将属性名换为其他key去字典中取值 31 | * 32 | * @return 字典中的key是属性名,value是从字典中取值用的key 33 | */ 34 | + (NSDictionary *)mj_replacedKeyFromPropertyName; 35 | 36 | /** 37 | * 将属性名换为其他key去字典中取值 38 | * 39 | * @return 从字典中取值用的key 40 | */ 41 | + (NSString *)mj_replacedKeyFromPropertyName121:(NSString *)propertyName; 42 | 43 | /** 44 | * 数组中需要转换的模型类 45 | * 46 | * @return 字典中的key是数组属性名,value是数组中存放模型的Class(Class类型或者NSString类型) 47 | */ 48 | + (NSDictionary *)mj_objectClassInArray; 49 | 50 | /** 51 | * 旧值换新值,用于过滤字典中的值 52 | * 53 | * @param oldValue 旧值 54 | * 55 | * @return 新值 56 | */ 57 | - (id)mj_newValueFromOldValue:(id)oldValue property:(MJProperty *)property; 58 | 59 | /** 60 | * 当字典转模型完毕时调用 61 | */ 62 | - (void)mj_keyValuesDidFinishConvertingToObject; 63 | 64 | /** 65 | * 当模型转字典完毕时调用 66 | */ 67 | - (void)mj_objectDidFinishConvertingToKeyValues; 68 | @end 69 | 70 | @interface NSObject (MJKeyValue) 71 | #pragma mark - 类方法 72 | /** 73 | * 字典转模型过程中遇到的错误 74 | */ 75 | + (NSError *)mj_error; 76 | 77 | /** 78 | * 模型转字典时,字典的key是否参考replacedKeyFromPropertyName等方法(父类设置了,子类也会继承下来) 79 | */ 80 | + (void)mj_referenceReplacedKeyWhenCreatingKeyValues:(BOOL)reference; 81 | 82 | #pragma mark - 对象方法 83 | /** 84 | * 将字典的键值对转成模型属性 85 | * @param keyValues 字典(可以是NSDictionary、NSData、NSString) 86 | */ 87 | - (instancetype)mj_setKeyValues:(id)keyValues; 88 | 89 | /** 90 | * 将字典的键值对转成模型属性 91 | * @param keyValues 字典(可以是NSDictionary、NSData、NSString) 92 | * @param context CoreData上下文 93 | */ 94 | - (instancetype)mj_setKeyValues:(id)keyValues context:(NSManagedObjectContext *)context; 95 | 96 | /** 97 | * 将模型转成字典 98 | * @return 字典 99 | */ 100 | - (NSMutableDictionary *)mj_keyValues; 101 | - (NSMutableDictionary *)mj_keyValuesWithKeys:(NSArray *)keys; 102 | - (NSMutableDictionary *)mj_keyValuesWithIgnoredKeys:(NSArray *)ignoredKeys; 103 | 104 | /** 105 | * 通过模型数组来创建一个字典数组 106 | * @param objectArray 模型数组 107 | * @return 字典数组 108 | */ 109 | + (NSMutableArray *)mj_keyValuesArrayWithObjectArray:(NSArray *)objectArray; 110 | + (NSMutableArray *)mj_keyValuesArrayWithObjectArray:(NSArray *)objectArray keys:(NSArray *)keys; 111 | + (NSMutableArray *)mj_keyValuesArrayWithObjectArray:(NSArray *)objectArray ignoredKeys:(NSArray *)ignoredKeys; 112 | 113 | #pragma mark - 字典转模型 114 | /** 115 | * 通过字典来创建一个模型 116 | * @param keyValues 字典(可以是NSDictionary、NSData、NSString) 117 | * @return 新建的对象 118 | */ 119 | + (instancetype)mj_objectWithKeyValues:(id)keyValues; 120 | 121 | /** 122 | * 通过字典来创建一个CoreData模型 123 | * @param keyValues 字典(可以是NSDictionary、NSData、NSString) 124 | * @param context CoreData上下文 125 | * @return 新建的对象 126 | */ 127 | + (instancetype)mj_objectWithKeyValues:(id)keyValues context:(NSManagedObjectContext *)context; 128 | 129 | /** 130 | * 通过plist来创建一个模型 131 | * @param filename 文件名(仅限于mainBundle中的文件) 132 | * @return 新建的对象 133 | */ 134 | + (instancetype)mj_objectWithFilename:(NSString *)filename; 135 | 136 | /** 137 | * 通过plist来创建一个模型 138 | * @param file 文件全路径 139 | * @return 新建的对象 140 | */ 141 | + (instancetype)mj_objectWithFile:(NSString *)file; 142 | 143 | #pragma mark - 字典数组转模型数组 144 | /** 145 | * 通过字典数组来创建一个模型数组 146 | * @param keyValuesArray 字典数组(可以是NSDictionary、NSData、NSString) 147 | * @return 模型数组 148 | */ 149 | + (NSMutableArray *)mj_objectArrayWithKeyValuesArray:(id)keyValuesArray; 150 | 151 | /** 152 | * 通过字典数组来创建一个模型数组 153 | * @param keyValuesArray 字典数组(可以是NSDictionary、NSData、NSString) 154 | * @param context CoreData上下文 155 | * @return 模型数组 156 | */ 157 | + (NSMutableArray *)mj_objectArrayWithKeyValuesArray:(id)keyValuesArray context:(NSManagedObjectContext *)context; 158 | 159 | /** 160 | * 通过plist来创建一个模型数组 161 | * @param filename 文件名(仅限于mainBundle中的文件) 162 | * @return 模型数组 163 | */ 164 | + (NSMutableArray *)mj_objectArrayWithFilename:(NSString *)filename; 165 | 166 | /** 167 | * 通过plist来创建一个模型数组 168 | * @param file 文件全路径 169 | * @return 模型数组 170 | */ 171 | + (NSMutableArray *)mj_objectArrayWithFile:(NSString *)file; 172 | 173 | #pragma mark - 转换为JSON 174 | /** 175 | * 转换为JSON Data 176 | */ 177 | - (NSData *)mj_JSONData; 178 | /** 179 | * 转换为字典或者数组 180 | */ 181 | - (id)mj_JSONObject; 182 | /** 183 | * 转换为JSON 字符串 184 | */ 185 | - (NSString *)mj_JSONString; 186 | @end 187 | 188 | @interface NSObject (MJKeyValueDeprecated_v_2_5_16) 189 | - (instancetype)setKeyValues:(id)keyValue MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 190 | - (instancetype)setKeyValues:(id)keyValues error:(NSError **)error MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 191 | - (instancetype)setKeyValues:(id)keyValues context:(NSManagedObjectContext *)context MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 192 | - (instancetype)setKeyValues:(id)keyValues context:(NSManagedObjectContext *)context error:(NSError **)error MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 193 | + (void)referenceReplacedKeyWhenCreatingKeyValues:(BOOL)reference MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 194 | - (NSMutableDictionary *)keyValues MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 195 | - (NSMutableDictionary *)keyValuesWithError:(NSError **)error MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 196 | - (NSMutableDictionary *)keyValuesWithKeys:(NSArray *)keys MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 197 | - (NSMutableDictionary *)keyValuesWithKeys:(NSArray *)keys error:(NSError **)error MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 198 | - (NSMutableDictionary *)keyValuesWithIgnoredKeys:(NSArray *)ignoredKeys MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 199 | - (NSMutableDictionary *)keyValuesWithIgnoredKeys:(NSArray *)ignoredKeys error:(NSError **)error MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 200 | + (NSMutableArray *)keyValuesArrayWithObjectArray:(NSArray *)objectArray MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 201 | + (NSMutableArray *)keyValuesArrayWithObjectArray:(NSArray *)objectArray error:(NSError **)error MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 202 | + (NSMutableArray *)keyValuesArrayWithObjectArray:(NSArray *)objectArray keys:(NSArray *)keys MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 203 | + (NSMutableArray *)keyValuesArrayWithObjectArray:(NSArray *)objectArray keys:(NSArray *)keys error:(NSError **)error MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 204 | + (NSMutableArray *)keyValuesArrayWithObjectArray:(NSArray *)objectArray ignoredKeys:(NSArray *)ignoredKeys MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 205 | + (NSMutableArray *)keyValuesArrayWithObjectArray:(NSArray *)objectArray ignoredKeys:(NSArray *)ignoredKeys error:(NSError **)error MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 206 | + (instancetype)objectWithKeyValues:(id)keyValues MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 207 | + (instancetype)objectWithKeyValues:(id)keyValues error:(NSError **)error MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 208 | + (instancetype)objectWithKeyValues:(id)keyValues context:(NSManagedObjectContext *)context MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 209 | + (instancetype)objectWithKeyValues:(id)keyValues context:(NSManagedObjectContext *)context error:(NSError **)error MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 210 | + (instancetype)objectWithFilename:(NSString *)filename MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 211 | + (instancetype)objectWithFilename:(NSString *)filename error:(NSError **)error MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 212 | + (instancetype)objectWithFile:(NSString *)file MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 213 | + (instancetype)objectWithFile:(NSString *)file error:(NSError **)error MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 214 | + (NSMutableArray *)objectArrayWithKeyValuesArray:(id)keyValuesArray MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 215 | + (NSMutableArray *)objectArrayWithKeyValuesArray:(id)keyValuesArray error:(NSError **)error MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 216 | + (NSMutableArray *)objectArrayWithKeyValuesArray:(id)keyValuesArray context:(NSManagedObjectContext *)context MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 217 | + (NSMutableArray *)objectArrayWithKeyValuesArray:(id)keyValuesArray context:(NSManagedObjectContext *)context error:(NSError **)error MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 218 | + (NSMutableArray *)objectArrayWithFilename:(NSString *)filename MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 219 | + (NSMutableArray *)objectArrayWithFilename:(NSString *)filename error:(NSError **)error MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 220 | + (NSMutableArray *)objectArrayWithFile:(NSString *)file MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 221 | + (NSMutableArray *)objectArrayWithFile:(NSString *)file error:(NSError **)error MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 222 | - (NSData *)JSONData MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 223 | - (id)JSONObject MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 224 | - (NSString *)JSONString MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 225 | @end 226 | -------------------------------------------------------------------------------- /CCLKLineChartViewDemo/CCLKLineChartViewDemo/MJExtension/NSObject+MJKeyValue.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSObject+MJKeyValue.m 3 | // MJExtension 4 | // 5 | // Created by mj on 13-8-24. 6 | // Copyright (c) 2013年 小码哥. All rights reserved. 7 | // 8 | 9 | #import "NSObject+MJKeyValue.h" 10 | #import "NSObject+MJProperty.h" 11 | #import "NSString+MJExtension.h" 12 | #import "MJProperty.h" 13 | #import "MJPropertyType.h" 14 | #import "MJExtensionConst.h" 15 | #import "MJFoundation.h" 16 | #import "NSString+MJExtension.h" 17 | #import "NSObject+MJClass.h" 18 | 19 | @implementation NSObject (MJKeyValue) 20 | 21 | #pragma mark - 错误 22 | static const char MJErrorKey = '\0'; 23 | + (NSError *)mj_error 24 | { 25 | return objc_getAssociatedObject(self, &MJErrorKey); 26 | } 27 | 28 | + (void)setMj_error:(NSError *)error 29 | { 30 | objc_setAssociatedObject(self, &MJErrorKey, error, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 31 | } 32 | 33 | #pragma mark - 模型 -> 字典时的参考 34 | /** 模型转字典时,字典的key是否参考replacedKeyFromPropertyName等方法(父类设置了,子类也会继承下来) */ 35 | static const char MJReferenceReplacedKeyWhenCreatingKeyValuesKey = '\0'; 36 | 37 | + (void)mj_referenceReplacedKeyWhenCreatingKeyValues:(BOOL)reference 38 | { 39 | objc_setAssociatedObject(self, &MJReferenceReplacedKeyWhenCreatingKeyValuesKey, @(reference), OBJC_ASSOCIATION_ASSIGN); 40 | } 41 | 42 | + (BOOL)mj_isReferenceReplacedKeyWhenCreatingKeyValues 43 | { 44 | __block id value = objc_getAssociatedObject(self, &MJReferenceReplacedKeyWhenCreatingKeyValuesKey); 45 | if (!value) { 46 | [self mj_enumerateAllClasses:^(__unsafe_unretained Class c, BOOL *stop) { 47 | value = objc_getAssociatedObject(c, &MJReferenceReplacedKeyWhenCreatingKeyValuesKey); 48 | 49 | if (value) *stop = YES; 50 | }]; 51 | } 52 | return [value boolValue]; 53 | } 54 | 55 | #pragma mark - --常用的对象-- 56 | static NSNumberFormatter *numberFormatter_; 57 | + (void)load 58 | { 59 | numberFormatter_ = [[NSNumberFormatter alloc] init]; 60 | 61 | // 默认设置 62 | [self mj_referenceReplacedKeyWhenCreatingKeyValues:YES]; 63 | } 64 | 65 | #pragma mark - --公共方法-- 66 | #pragma mark - 字典 -> 模型 67 | - (instancetype)mj_setKeyValues:(id)keyValues 68 | { 69 | return [self mj_setKeyValues:keyValues context:nil]; 70 | } 71 | 72 | /** 73 | 核心代码: 74 | */ 75 | - (instancetype)mj_setKeyValues:(id)keyValues context:(NSManagedObjectContext *)context 76 | { 77 | // 获得JSON对象 78 | keyValues = [keyValues mj_JSONObject]; 79 | 80 | MJExtensionAssertError([keyValues isKindOfClass:[NSDictionary class]], self, [self class], @"keyValues参数不是一个字典"); 81 | 82 | Class clazz = [self class]; 83 | NSArray *allowedPropertyNames = [clazz mj_totalAllowedPropertyNames]; 84 | NSArray *ignoredPropertyNames = [clazz mj_totalIgnoredPropertyNames]; 85 | 86 | //通过封装的方法回调一个通过运行时编写的,用于返回属性列表的方法。 87 | [clazz mj_enumerateProperties:^(MJProperty *property, BOOL *stop) { 88 | @try { 89 | // 0.检测是否被忽略 90 | if (allowedPropertyNames.count && ![allowedPropertyNames containsObject:property.name]) return; 91 | if ([ignoredPropertyNames containsObject:property.name]) return; 92 | 93 | // 1.取出属性值 94 | id value; 95 | NSArray *propertyKeyses = [property propertyKeysForClass:clazz]; 96 | for (NSArray *propertyKeys in propertyKeyses) { 97 | value = keyValues; 98 | for (MJPropertyKey *propertyKey in propertyKeys) { 99 | value = [propertyKey valueInObject:value]; 100 | } 101 | if (value) break; 102 | } 103 | 104 | // 值的过滤 105 | id newValue = [clazz mj_getNewValueFromObject:self oldValue:value property:property]; 106 | if (newValue != value) { // 有过滤后的新值 107 | [property setValue:newValue forObject:self]; 108 | return; 109 | } 110 | 111 | // 如果没有值,就直接返回 112 | if (!value || value == [NSNull null]) return; 113 | 114 | // 2.复杂处理 115 | MJPropertyType *type = property.type; 116 | Class propertyClass = type.typeClass; 117 | Class objectClass = [property objectClassInArrayForClass:[self class]]; 118 | 119 | // 不可变 -> 可变处理 120 | if (propertyClass == [NSMutableArray class] && [value isKindOfClass:[NSArray class]]) { 121 | value = [NSMutableArray arrayWithArray:value]; 122 | } else if (propertyClass == [NSMutableDictionary class] && [value isKindOfClass:[NSDictionary class]]) { 123 | value = [NSMutableDictionary dictionaryWithDictionary:value]; 124 | } else if (propertyClass == [NSMutableString class] && [value isKindOfClass:[NSString class]]) { 125 | value = [NSMutableString stringWithString:value]; 126 | } else if (propertyClass == [NSMutableData class] && [value isKindOfClass:[NSData class]]) { 127 | value = [NSMutableData dataWithData:value]; 128 | } 129 | 130 | if (!type.isFromFoundation && propertyClass) { // 模型属性 131 | value = [propertyClass mj_objectWithKeyValues:value context:context]; 132 | } else if (objectClass) { 133 | if (objectClass == [NSURL class] && [value isKindOfClass:[NSArray class]]) { 134 | // string array -> url array 135 | NSMutableArray *urlArray = [NSMutableArray array]; 136 | for (NSString *string in value) { 137 | if (![string isKindOfClass:[NSString class]]) continue; 138 | [urlArray addObject:string.mj_url]; 139 | } 140 | value = urlArray; 141 | } else { // 字典数组-->模型数组 142 | value = [objectClass mj_objectArrayWithKeyValuesArray:value context:context]; 143 | } 144 | } else { 145 | if (propertyClass == [NSString class]) { 146 | if ([value isKindOfClass:[NSNumber class]]) { 147 | // NSNumber -> NSString 148 | value = [value description]; 149 | } else if ([value isKindOfClass:[NSURL class]]) { 150 | // NSURL -> NSString 151 | value = [value absoluteString]; 152 | } 153 | } else if ([value isKindOfClass:[NSString class]]) { 154 | if (propertyClass == [NSURL class]) { 155 | // NSString -> NSURL 156 | // 字符串转码 157 | value = [value mj_url]; 158 | } else if (type.isNumberType) { 159 | NSString *oldValue = value; 160 | 161 | // NSString -> NSNumber 162 | if (type.typeClass == [NSDecimalNumber class]) { 163 | value = [NSDecimalNumber decimalNumberWithString:oldValue]; 164 | } else { 165 | value = [numberFormatter_ numberFromString:oldValue]; 166 | } 167 | 168 | // 如果是BOOL 169 | if (type.isBoolType) { 170 | // 字符串转BOOL(字符串没有charValue方法) 171 | // 系统会调用字符串的charValue转为BOOL类型 172 | NSString *lower = [oldValue lowercaseString]; 173 | if ([lower isEqualToString:@"yes"] || [lower isEqualToString:@"true"]) { 174 | value = @YES; 175 | } else if ([lower isEqualToString:@"no"] || [lower isEqualToString:@"false"]) { 176 | value = @NO; 177 | } 178 | } 179 | } 180 | } 181 | 182 | // value和property类型不匹配 183 | if (propertyClass && ![value isKindOfClass:propertyClass]) { 184 | value = nil; 185 | } 186 | } 187 | 188 | // 3.赋值 189 | [property setValue:value forObject:self]; 190 | } @catch (NSException *exception) { 191 | MJExtensionBuildError([self class], exception.reason); 192 | MJExtensionLog(@"%@", exception); 193 | } 194 | }]; 195 | 196 | // 转换完毕 197 | if ([self respondsToSelector:@selector(mj_keyValuesDidFinishConvertingToObject)]) { 198 | [self mj_keyValuesDidFinishConvertingToObject]; 199 | } 200 | return self; 201 | } 202 | 203 | + (instancetype)mj_objectWithKeyValues:(id)keyValues 204 | { 205 | return [self mj_objectWithKeyValues:keyValues context:nil]; 206 | } 207 | 208 | + (instancetype)mj_objectWithKeyValues:(id)keyValues context:(NSManagedObjectContext *)context 209 | { 210 | // 获得JSON对象 211 | keyValues = [keyValues mj_JSONObject]; 212 | MJExtensionAssertError([keyValues isKindOfClass:[NSDictionary class]], nil, [self class], @"keyValues参数不是一个字典"); 213 | 214 | if ([self isSubclassOfClass:[NSManagedObject class]] && context) { 215 | return [[NSEntityDescription insertNewObjectForEntityForName:NSStringFromClass(self) inManagedObjectContext:context] mj_setKeyValues:keyValues context:context]; 216 | } 217 | return [[[self alloc] init] mj_setKeyValues:keyValues]; 218 | } 219 | 220 | + (instancetype)mj_objectWithFilename:(NSString *)filename 221 | { 222 | MJExtensionAssertError(filename != nil, nil, [self class], @"filename参数为nil"); 223 | 224 | return [self mj_objectWithFile:[[NSBundle mainBundle] pathForResource:filename ofType:nil]]; 225 | } 226 | 227 | + (instancetype)mj_objectWithFile:(NSString *)file 228 | { 229 | MJExtensionAssertError(file != nil, nil, [self class], @"file参数为nil"); 230 | 231 | return [self mj_objectWithKeyValues:[NSDictionary dictionaryWithContentsOfFile:file]]; 232 | } 233 | 234 | #pragma mark - 字典数组 -> 模型数组 235 | + (NSMutableArray *)mj_objectArrayWithKeyValuesArray:(NSArray *)keyValuesArray 236 | { 237 | return [self mj_objectArrayWithKeyValuesArray:keyValuesArray context:nil]; 238 | } 239 | 240 | + (NSMutableArray *)mj_objectArrayWithKeyValuesArray:(id)keyValuesArray context:(NSManagedObjectContext *)context 241 | { 242 | // 如果是JSON字符串 243 | keyValuesArray = [keyValuesArray mj_JSONObject]; 244 | 245 | // 1.判断真实性 246 | MJExtensionAssertError([keyValuesArray isKindOfClass:[NSArray class]], nil, [self class], @"keyValuesArray参数不是一个数组"); 247 | 248 | // 如果数组里面放的是NSString、NSNumber等数据 249 | if ([MJFoundation isClassFromFoundation:self]) return [NSMutableArray arrayWithArray:keyValuesArray]; 250 | 251 | 252 | // 2.创建数组 253 | NSMutableArray *modelArray = [NSMutableArray array]; 254 | 255 | // 3.遍历 256 | for (NSDictionary *keyValues in keyValuesArray) { 257 | if ([keyValues isKindOfClass:[NSArray class]]){ 258 | [modelArray addObject:[self mj_objectArrayWithKeyValuesArray:keyValues context:context]]; 259 | } else { 260 | id model = [self mj_objectWithKeyValues:keyValues context:context]; 261 | if (model) [modelArray addObject:model]; 262 | } 263 | } 264 | 265 | return modelArray; 266 | } 267 | 268 | + (NSMutableArray *)mj_objectArrayWithFilename:(NSString *)filename 269 | { 270 | MJExtensionAssertError(filename != nil, nil, [self class], @"filename参数为nil"); 271 | 272 | return [self mj_objectArrayWithFile:[[NSBundle mainBundle] pathForResource:filename ofType:nil]]; 273 | } 274 | 275 | + (NSMutableArray *)mj_objectArrayWithFile:(NSString *)file 276 | { 277 | MJExtensionAssertError(file != nil, nil, [self class], @"file参数为nil"); 278 | 279 | return [self mj_objectArrayWithKeyValuesArray:[NSArray arrayWithContentsOfFile:file]]; 280 | } 281 | 282 | #pragma mark - 模型 -> 字典 283 | - (NSMutableDictionary *)mj_keyValues 284 | { 285 | return [self mj_keyValuesWithKeys:nil ignoredKeys:nil]; 286 | } 287 | 288 | - (NSMutableDictionary *)mj_keyValuesWithKeys:(NSArray *)keys 289 | { 290 | return [self mj_keyValuesWithKeys:keys ignoredKeys:nil]; 291 | } 292 | 293 | - (NSMutableDictionary *)mj_keyValuesWithIgnoredKeys:(NSArray *)ignoredKeys 294 | { 295 | return [self mj_keyValuesWithKeys:nil ignoredKeys:ignoredKeys]; 296 | } 297 | 298 | - (NSMutableDictionary *)mj_keyValuesWithKeys:(NSArray *)keys ignoredKeys:(NSArray *)ignoredKeys 299 | { 300 | // 如果自己不是模型类, 那就返回自己 301 | MJExtensionAssertError(![MJFoundation isClassFromFoundation:[self class]], (NSMutableDictionary *)self, [self class], @"不是自定义的模型类") 302 | 303 | id keyValues = [NSMutableDictionary dictionary]; 304 | 305 | Class clazz = [self class]; 306 | NSArray *allowedPropertyNames = [clazz mj_totalAllowedPropertyNames]; 307 | NSArray *ignoredPropertyNames = [clazz mj_totalIgnoredPropertyNames]; 308 | 309 | [clazz mj_enumerateProperties:^(MJProperty *property, BOOL *stop) { 310 | @try { 311 | // 0.检测是否被忽略 312 | if (allowedPropertyNames.count && ![allowedPropertyNames containsObject:property.name]) return; 313 | if ([ignoredPropertyNames containsObject:property.name]) return; 314 | if (keys.count && ![keys containsObject:property.name]) return; 315 | if ([ignoredKeys containsObject:property.name]) return; 316 | 317 | // 1.取出属性值 318 | id value = [property valueForObject:self]; 319 | if (!value) return; 320 | 321 | // 2.如果是模型属性 322 | MJPropertyType *type = property.type; 323 | Class propertyClass = type.typeClass; 324 | if (!type.isFromFoundation && propertyClass) { 325 | value = [value mj_keyValues]; 326 | } else if ([value isKindOfClass:[NSArray class]]) { 327 | // 3.处理数组里面有模型的情况 328 | value = [NSObject mj_keyValuesArrayWithObjectArray:value]; 329 | } else if (propertyClass == [NSURL class]) { 330 | value = [value absoluteString]; 331 | } 332 | 333 | // 4.赋值 334 | if ([clazz mj_isReferenceReplacedKeyWhenCreatingKeyValues]) { 335 | NSArray *propertyKeys = [[property propertyKeysForClass:clazz] firstObject]; 336 | NSUInteger keyCount = propertyKeys.count; 337 | // 创建字典 338 | __block id innerContainer = keyValues; 339 | [propertyKeys enumerateObjectsUsingBlock:^(MJPropertyKey *propertyKey, NSUInteger idx, BOOL *stop) { 340 | // 下一个属性 341 | MJPropertyKey *nextPropertyKey = nil; 342 | if (idx != keyCount - 1) { 343 | nextPropertyKey = propertyKeys[idx + 1]; 344 | } 345 | 346 | if (nextPropertyKey) { // 不是最后一个key 347 | // 当前propertyKey对应的字典或者数组 348 | id tempInnerContainer = [propertyKey valueInObject:innerContainer]; 349 | if (tempInnerContainer == nil || [tempInnerContainer isKindOfClass:[NSNull class]]) { 350 | if (nextPropertyKey.type == MJPropertyKeyTypeDictionary) { 351 | tempInnerContainer = [NSMutableDictionary dictionary]; 352 | } else { 353 | tempInnerContainer = [NSMutableArray array]; 354 | } 355 | if (propertyKey.type == MJPropertyKeyTypeDictionary) { 356 | innerContainer[propertyKey.name] = tempInnerContainer; 357 | } else { 358 | innerContainer[propertyKey.name.intValue] = tempInnerContainer; 359 | } 360 | } 361 | 362 | if ([tempInnerContainer isKindOfClass:[NSMutableArray class]]) { 363 | NSMutableArray *tempInnerContainerArray = tempInnerContainer; 364 | int index = nextPropertyKey.name.intValue; 365 | while (tempInnerContainerArray.count < index + 1) { 366 | [tempInnerContainerArray addObject:[NSNull null]]; 367 | } 368 | } 369 | 370 | innerContainer = tempInnerContainer; 371 | } else { // 最后一个key 372 | if (propertyKey.type == MJPropertyKeyTypeDictionary) { 373 | innerContainer[propertyKey.name] = value; 374 | } else { 375 | innerContainer[propertyKey.name.intValue] = value; 376 | } 377 | } 378 | }]; 379 | } else { 380 | keyValues[property.name] = value; 381 | } 382 | } @catch (NSException *exception) { 383 | MJExtensionBuildError([self class], exception.reason); 384 | MJExtensionLog(@"%@", exception); 385 | } 386 | }]; 387 | 388 | // 转换完毕 389 | if ([self respondsToSelector:@selector(mj_objectDidFinishConvertingToKeyValues)]) { 390 | [self mj_objectDidFinishConvertingToKeyValues]; 391 | } 392 | 393 | return keyValues; 394 | } 395 | #pragma mark - 模型数组 -> 字典数组 396 | + (NSMutableArray *)mj_keyValuesArrayWithObjectArray:(NSArray *)objectArray 397 | { 398 | return [self mj_keyValuesArrayWithObjectArray:objectArray keys:nil ignoredKeys:nil]; 399 | } 400 | 401 | + (NSMutableArray *)mj_keyValuesArrayWithObjectArray:(NSArray *)objectArray keys:(NSArray *)keys 402 | { 403 | return [self mj_keyValuesArrayWithObjectArray:objectArray keys:keys ignoredKeys:nil]; 404 | } 405 | 406 | + (NSMutableArray *)mj_keyValuesArrayWithObjectArray:(NSArray *)objectArray ignoredKeys:(NSArray *)ignoredKeys 407 | { 408 | return [self mj_keyValuesArrayWithObjectArray:objectArray keys:nil ignoredKeys:ignoredKeys]; 409 | } 410 | 411 | + (NSMutableArray *)mj_keyValuesArrayWithObjectArray:(NSArray *)objectArray keys:(NSArray *)keys ignoredKeys:(NSArray *)ignoredKeys 412 | { 413 | // 0.判断真实性 414 | MJExtensionAssertError([objectArray isKindOfClass:[NSArray class]], nil, [self class], @"objectArray参数不是一个数组"); 415 | 416 | // 1.创建数组 417 | NSMutableArray *keyValuesArray = [NSMutableArray array]; 418 | for (id object in objectArray) { 419 | if (keys) { 420 | [keyValuesArray addObject:[object mj_keyValuesWithKeys:keys]]; 421 | } else { 422 | [keyValuesArray addObject:[object mj_keyValuesWithIgnoredKeys:ignoredKeys]]; 423 | } 424 | } 425 | return keyValuesArray; 426 | } 427 | 428 | #pragma mark - 转换为JSON 429 | - (NSData *)mj_JSONData 430 | { 431 | if ([self isKindOfClass:[NSString class]]) { 432 | return [((NSString *)self) dataUsingEncoding:NSUTF8StringEncoding]; 433 | } else if ([self isKindOfClass:[NSData class]]) { 434 | return (NSData *)self; 435 | } 436 | 437 | return [NSJSONSerialization dataWithJSONObject:[self mj_JSONObject] options:kNilOptions error:nil]; 438 | } 439 | 440 | - (id)mj_JSONObject 441 | { 442 | if ([self isKindOfClass:[NSString class]]) { 443 | return [NSJSONSerialization JSONObjectWithData:[((NSString *)self) dataUsingEncoding:NSUTF8StringEncoding] options:kNilOptions error:nil]; 444 | } else if ([self isKindOfClass:[NSData class]]) { 445 | return [NSJSONSerialization JSONObjectWithData:(NSData *)self options:kNilOptions error:nil]; 446 | } 447 | 448 | return self.mj_keyValues; 449 | } 450 | 451 | - (NSString *)mj_JSONString 452 | { 453 | if ([self isKindOfClass:[NSString class]]) { 454 | return (NSString *)self; 455 | } else if ([self isKindOfClass:[NSData class]]) { 456 | return [[NSString alloc] initWithData:(NSData *)self encoding:NSUTF8StringEncoding]; 457 | } 458 | 459 | return [[NSString alloc] initWithData:[self mj_JSONData] encoding:NSUTF8StringEncoding]; 460 | } 461 | @end 462 | 463 | @implementation NSObject (MJKeyValueDeprecated_v_2_5_16) 464 | - (instancetype)setKeyValues:(id)keyValues 465 | { 466 | return [self mj_setKeyValues:keyValues]; 467 | } 468 | 469 | - (instancetype)setKeyValues:(id)keyValues error:(NSError **)error 470 | { 471 | id value = [self mj_setKeyValues:keyValues]; 472 | if (error != NULL) { 473 | *error = [self.class mj_error]; 474 | } 475 | return value; 476 | 477 | } 478 | 479 | - (instancetype)setKeyValues:(id)keyValues context:(NSManagedObjectContext *)context 480 | { 481 | return [self mj_setKeyValues:keyValues context:context]; 482 | } 483 | 484 | - (instancetype)setKeyValues:(id)keyValues context:(NSManagedObjectContext *)context error:(NSError **)error 485 | { 486 | id value = [self mj_setKeyValues:keyValues context:context]; 487 | if (error != NULL) { 488 | *error = [self.class mj_error]; 489 | } 490 | return value; 491 | } 492 | 493 | + (void)referenceReplacedKeyWhenCreatingKeyValues:(BOOL)reference 494 | { 495 | [self mj_referenceReplacedKeyWhenCreatingKeyValues:reference]; 496 | } 497 | 498 | - (NSMutableDictionary *)keyValues 499 | { 500 | return [self mj_keyValues]; 501 | } 502 | 503 | - (NSMutableDictionary *)keyValuesWithError:(NSError **)error 504 | { 505 | id value = [self mj_keyValues]; 506 | if (error != NULL) { 507 | *error = [self.class mj_error]; 508 | } 509 | return value; 510 | } 511 | 512 | - (NSMutableDictionary *)keyValuesWithKeys:(NSArray *)keys 513 | { 514 | return [self mj_keyValuesWithKeys:keys]; 515 | } 516 | 517 | - (NSMutableDictionary *)keyValuesWithKeys:(NSArray *)keys error:(NSError **)error 518 | { 519 | id value = [self mj_keyValuesWithKeys:keys]; 520 | if (error != NULL) { 521 | *error = [self.class mj_error]; 522 | } 523 | return value; 524 | } 525 | 526 | - (NSMutableDictionary *)keyValuesWithIgnoredKeys:(NSArray *)ignoredKeys 527 | { 528 | return [self mj_keyValuesWithIgnoredKeys:ignoredKeys]; 529 | } 530 | 531 | - (NSMutableDictionary *)keyValuesWithIgnoredKeys:(NSArray *)ignoredKeys error:(NSError **)error 532 | { 533 | id value = [self mj_keyValuesWithIgnoredKeys:ignoredKeys]; 534 | if (error != NULL) { 535 | *error = [self.class mj_error]; 536 | } 537 | return value; 538 | } 539 | 540 | + (NSMutableArray *)keyValuesArrayWithObjectArray:(NSArray *)objectArray 541 | { 542 | return [self mj_keyValuesArrayWithObjectArray:objectArray]; 543 | } 544 | 545 | + (NSMutableArray *)keyValuesArrayWithObjectArray:(NSArray *)objectArray error:(NSError **)error 546 | { 547 | id value = [self mj_keyValuesArrayWithObjectArray:objectArray]; 548 | if (error != NULL) { 549 | *error = [self mj_error]; 550 | } 551 | return value; 552 | } 553 | 554 | + (NSMutableArray *)keyValuesArrayWithObjectArray:(NSArray *)objectArray keys:(NSArray *)keys 555 | { 556 | return [self mj_keyValuesArrayWithObjectArray:objectArray keys:keys]; 557 | } 558 | 559 | + (NSMutableArray *)keyValuesArrayWithObjectArray:(NSArray *)objectArray keys:(NSArray *)keys error:(NSError **)error 560 | { 561 | id value = [self mj_keyValuesArrayWithObjectArray:objectArray keys:keys]; 562 | if (error != NULL) { 563 | *error = [self mj_error]; 564 | } 565 | return value; 566 | } 567 | 568 | + (NSMutableArray *)keyValuesArrayWithObjectArray:(NSArray *)objectArray ignoredKeys:(NSArray *)ignoredKeys 569 | { 570 | return [self mj_keyValuesArrayWithObjectArray:objectArray ignoredKeys:ignoredKeys]; 571 | } 572 | 573 | + (NSMutableArray *)keyValuesArrayWithObjectArray:(NSArray *)objectArray ignoredKeys:(NSArray *)ignoredKeys error:(NSError **)error 574 | { 575 | id value = [self mj_keyValuesArrayWithObjectArray:objectArray ignoredKeys:ignoredKeys]; 576 | if (error != NULL) { 577 | *error = [self mj_error]; 578 | } 579 | return value; 580 | } 581 | 582 | + (instancetype)objectWithKeyValues:(id)keyValues 583 | { 584 | return [self mj_objectWithKeyValues:keyValues]; 585 | } 586 | 587 | + (instancetype)objectWithKeyValues:(id)keyValues error:(NSError **)error 588 | { 589 | id value = [self mj_objectWithKeyValues:keyValues]; 590 | if (error != NULL) { 591 | *error = [self mj_error]; 592 | } 593 | return value; 594 | } 595 | 596 | + (instancetype)objectWithKeyValues:(id)keyValues context:(NSManagedObjectContext *)context 597 | { 598 | return [self mj_objectWithKeyValues:keyValues context:context]; 599 | } 600 | 601 | + (instancetype)objectWithKeyValues:(id)keyValues context:(NSManagedObjectContext *)context error:(NSError **)error 602 | { 603 | id value = [self mj_objectWithKeyValues:keyValues context:context]; 604 | if (error != NULL) { 605 | *error = [self mj_error]; 606 | } 607 | return value; 608 | } 609 | 610 | + (instancetype)objectWithFilename:(NSString *)filename 611 | { 612 | return [self mj_objectWithFilename:filename]; 613 | } 614 | 615 | + (instancetype)objectWithFilename:(NSString *)filename error:(NSError **)error 616 | { 617 | id value = [self mj_objectWithFilename:filename]; 618 | if (error != NULL) { 619 | *error = [self mj_error]; 620 | } 621 | return value; 622 | } 623 | 624 | + (instancetype)objectWithFile:(NSString *)file 625 | { 626 | return [self mj_objectWithFile:file]; 627 | } 628 | 629 | + (instancetype)objectWithFile:(NSString *)file error:(NSError **)error 630 | { 631 | id value = [self mj_objectWithFile:file]; 632 | if (error != NULL) { 633 | *error = [self mj_error]; 634 | } 635 | return value; 636 | } 637 | 638 | + (NSMutableArray *)objectArrayWithKeyValuesArray:(id)keyValuesArray 639 | { 640 | return [self mj_objectArrayWithKeyValuesArray:keyValuesArray]; 641 | } 642 | 643 | + (NSMutableArray *)objectArrayWithKeyValuesArray:(id)keyValuesArray error:(NSError **)error 644 | { 645 | id value = [self mj_objectArrayWithKeyValuesArray:keyValuesArray]; 646 | if (error != NULL) { 647 | *error = [self mj_error]; 648 | } 649 | return value; 650 | } 651 | 652 | + (NSMutableArray *)objectArrayWithKeyValuesArray:(id)keyValuesArray context:(NSManagedObjectContext *)context 653 | { 654 | return [self mj_objectArrayWithKeyValuesArray:keyValuesArray context:context]; 655 | } 656 | 657 | + (NSMutableArray *)objectArrayWithKeyValuesArray:(id)keyValuesArray context:(NSManagedObjectContext *)context error:(NSError **)error 658 | { 659 | id value = [self mj_objectArrayWithKeyValuesArray:keyValuesArray context:context]; 660 | if (error != NULL) { 661 | *error = [self mj_error]; 662 | } 663 | return value; 664 | } 665 | 666 | + (NSMutableArray *)objectArrayWithFilename:(NSString *)filename 667 | { 668 | return [self mj_objectArrayWithFilename:filename]; 669 | } 670 | 671 | + (NSMutableArray *)objectArrayWithFilename:(NSString *)filename error:(NSError **)error 672 | { 673 | id value = [self mj_objectArrayWithFilename:filename]; 674 | if (error != NULL) { 675 | *error = [self mj_error]; 676 | } 677 | return value; 678 | } 679 | 680 | + (NSMutableArray *)objectArrayWithFile:(NSString *)file 681 | { 682 | return [self mj_objectArrayWithFile:file]; 683 | } 684 | 685 | + (NSMutableArray *)objectArrayWithFile:(NSString *)file error:(NSError **)error 686 | { 687 | id value = [self mj_objectArrayWithFile:file]; 688 | if (error != NULL) { 689 | *error = [self mj_error]; 690 | } 691 | return value; 692 | } 693 | 694 | - (NSData *)JSONData 695 | { 696 | return [self mj_JSONData]; 697 | } 698 | 699 | - (id)JSONObject 700 | { 701 | return [self mj_JSONObject]; 702 | } 703 | 704 | - (NSString *)JSONString 705 | { 706 | return [self mj_JSONString]; 707 | } 708 | @end -------------------------------------------------------------------------------- /CCLKLineChartViewDemo/CCLKLineChartViewDemo/MJExtension/NSObject+MJProperty.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSObject+MJProperty.h 3 | // MJExtensionExample 4 | // 5 | // Created by MJ Lee on 15/4/17. 6 | // Copyright (c) 2015年 小码哥. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "MJExtensionConst.h" 11 | 12 | @class MJProperty; 13 | 14 | /** 15 | * 遍历成员变量用的block 16 | * 17 | * @param property 成员的包装对象 18 | * @param stop YES代表停止遍历,NO代表继续遍历 19 | */ 20 | typedef void (^MJPropertiesEnumeration)(MJProperty *property, BOOL *stop); 21 | 22 | /** 将属性名换为其他key去字典中取值 */ 23 | typedef NSDictionary * (^MJReplacedKeyFromPropertyName)(); 24 | typedef NSString * (^MJReplacedKeyFromPropertyName121)(NSString *propertyName); 25 | /** 数组中需要转换的模型类 */ 26 | typedef NSDictionary * (^MJObjectClassInArray)(); 27 | /** 用于过滤字典中的值 */ 28 | typedef id (^MJNewValueFromOldValue)(id object, id oldValue, MJProperty *property); 29 | 30 | /** 31 | * 成员属性相关的扩展 32 | */ 33 | @interface NSObject (MJProperty) 34 | #pragma mark - 遍历 35 | /** 36 | * 遍历所有的成员 37 | */ 38 | + (void)mj_enumerateProperties:(MJPropertiesEnumeration)enumeration; 39 | 40 | #pragma mark - 新值配置 41 | /** 42 | * 用于过滤字典中的值 43 | * 44 | * @param newValueFormOldValue 用于过滤字典中的值 45 | */ 46 | + (void)mj_setupNewValueFromOldValue:(MJNewValueFromOldValue)newValueFormOldValue; 47 | + (id)mj_getNewValueFromObject:(__unsafe_unretained id)object oldValue:(__unsafe_unretained id)oldValue property:(__unsafe_unretained MJProperty *)property; 48 | 49 | #pragma mark - key配置 50 | /** 51 | * 将属性名换为其他key去字典中取值 52 | * 53 | * @param replacedKeyFromPropertyName 将属性名换为其他key去字典中取值 54 | */ 55 | + (void)mj_setupReplacedKeyFromPropertyName:(MJReplacedKeyFromPropertyName)replacedKeyFromPropertyName; 56 | /** 57 | * 将属性名换为其他key去字典中取值 58 | * 59 | * @param replacedKeyFromPropertyName121 将属性名换为其他key去字典中取值 60 | */ 61 | + (void)mj_setupReplacedKeyFromPropertyName121:(MJReplacedKeyFromPropertyName121)replacedKeyFromPropertyName121; 62 | 63 | #pragma mark - array model class配置 64 | /** 65 | * 数组中需要转换的模型类 66 | * 67 | * @param objectClassInArray 数组中需要转换的模型类 68 | */ 69 | + (void)mj_setupObjectClassInArray:(MJObjectClassInArray)objectClassInArray; 70 | @end 71 | 72 | @interface NSObject (MJPropertyDeprecated_v_2_5_16) 73 | + (void)enumerateProperties:(MJPropertiesEnumeration)enumeration MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 74 | + (void)setupNewValueFromOldValue:(MJNewValueFromOldValue)newValueFormOldValue MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 75 | + (id)getNewValueFromObject:(__unsafe_unretained id)object oldValue:(__unsafe_unretained id)oldValue property:(__unsafe_unretained MJProperty *)property MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 76 | + (void)setupReplacedKeyFromPropertyName:(MJReplacedKeyFromPropertyName)replacedKeyFromPropertyName MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 77 | + (void)setupReplacedKeyFromPropertyName121:(MJReplacedKeyFromPropertyName121)replacedKeyFromPropertyName121 MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 78 | + (void)setupObjectClassInArray:(MJObjectClassInArray)objectClassInArray MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 79 | @end -------------------------------------------------------------------------------- /CCLKLineChartViewDemo/CCLKLineChartViewDemo/MJExtension/NSObject+MJProperty.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSObject+MJProperty.m 3 | // MJExtensionExample 4 | // 5 | // Created by MJ Lee on 15/4/17. 6 | // Copyright (c) 2015年 小码哥. All rights reserved. 7 | // 8 | 9 | #import "NSObject+MJProperty.h" 10 | #import "NSObject+MJKeyValue.h" 11 | #import "NSObject+MJCoding.h" 12 | #import "NSObject+MJClass.h" 13 | #import "MJProperty.h" 14 | #import "MJFoundation.h" 15 | #import 16 | 17 | #pragma clang diagnostic push 18 | #pragma clang diagnostic ignored "-Wundeclared-selector" 19 | #pragma clang diagnostic ignored "-Warc-performSelector-leaks" 20 | 21 | static const char MJReplacedKeyFromPropertyNameKey = '\0'; 22 | static const char MJReplacedKeyFromPropertyName121Key = '\0'; 23 | static const char MJNewValueFromOldValueKey = '\0'; 24 | static const char MJObjectClassInArrayKey = '\0'; 25 | 26 | static const char MJCachedPropertiesKey = '\0'; 27 | 28 | @implementation NSObject (Property) 29 | 30 | static NSMutableDictionary *replacedKeyFromPropertyNameDict_; 31 | static NSMutableDictionary *replacedKeyFromPropertyName121Dict_; 32 | static NSMutableDictionary *newValueFromOldValueDict_; 33 | static NSMutableDictionary *objectClassInArrayDict_; 34 | static NSMutableDictionary *cachedPropertiesDict_; 35 | 36 | + (void)load 37 | { 38 | replacedKeyFromPropertyNameDict_ = [NSMutableDictionary dictionary]; 39 | replacedKeyFromPropertyName121Dict_ = [NSMutableDictionary dictionary]; 40 | newValueFromOldValueDict_ = [NSMutableDictionary dictionary]; 41 | objectClassInArrayDict_ = [NSMutableDictionary dictionary]; 42 | cachedPropertiesDict_ = [NSMutableDictionary dictionary]; 43 | } 44 | 45 | + (NSMutableDictionary *)dictForKey:(const void *)key 46 | { 47 | if (key == &MJReplacedKeyFromPropertyNameKey) return replacedKeyFromPropertyNameDict_; 48 | if (key == &MJReplacedKeyFromPropertyName121Key) return replacedKeyFromPropertyName121Dict_; 49 | if (key == &MJNewValueFromOldValueKey) return newValueFromOldValueDict_; 50 | if (key == &MJObjectClassInArrayKey) return objectClassInArrayDict_; 51 | if (key == &MJCachedPropertiesKey) return cachedPropertiesDict_; 52 | return nil; 53 | } 54 | 55 | #pragma mark - --私有方法-- 56 | + (NSString *)propertyKey:(NSString *)propertyName 57 | { 58 | MJExtensionAssertParamNotNil2(propertyName, nil); 59 | 60 | __block NSString *key = nil; 61 | // 查看有没有需要替换的key 62 | if ([self respondsToSelector:@selector(mj_replacedKeyFromPropertyName121:)]) { 63 | key = [self mj_replacedKeyFromPropertyName121:propertyName]; 64 | } 65 | // 兼容旧版本 66 | if ([self respondsToSelector:@selector(replacedKeyFromPropertyName121:)]) { 67 | key = [self performSelector:@selector(replacedKeyFromPropertyName121) withObject:propertyName]; 68 | } 69 | 70 | // 调用block 71 | if (!key) { 72 | [self mj_enumerateAllClasses:^(__unsafe_unretained Class c, BOOL *stop) { 73 | MJReplacedKeyFromPropertyName121 block = objc_getAssociatedObject(c, &MJReplacedKeyFromPropertyName121Key); 74 | if (block) { 75 | key = block(propertyName); 76 | } 77 | if (key) *stop = YES; 78 | }]; 79 | } 80 | 81 | // 查看有没有需要替换的key 82 | if (!key && [self respondsToSelector:@selector(mj_replacedKeyFromPropertyName)]) { 83 | key = [self mj_replacedKeyFromPropertyName][propertyName]; 84 | } 85 | // 兼容旧版本 86 | if (!key && [self respondsToSelector:@selector(replacedKeyFromPropertyName)]) { 87 | key = [self performSelector:@selector(replacedKeyFromPropertyName)][propertyName]; 88 | } 89 | 90 | if (!key) { 91 | [self mj_enumerateAllClasses:^(__unsafe_unretained Class c, BOOL *stop) { 92 | NSDictionary *dict = objc_getAssociatedObject(c, &MJReplacedKeyFromPropertyNameKey); 93 | if (dict) { 94 | key = dict[propertyName]; 95 | } 96 | if (key) *stop = YES; 97 | }]; 98 | } 99 | 100 | // 2.用属性名作为key 101 | if (!key) key = propertyName; 102 | 103 | return key; 104 | } 105 | 106 | + (Class)propertyObjectClassInArray:(NSString *)propertyName 107 | { 108 | __block id clazz = nil; 109 | if ([self respondsToSelector:@selector(mj_objectClassInArray)]) { 110 | clazz = [self mj_objectClassInArray][propertyName]; 111 | } 112 | // 兼容旧版本 113 | if ([self respondsToSelector:@selector(objectClassInArray)]) { 114 | clazz = [self performSelector:@selector(objectClassInArray)][propertyName]; 115 | } 116 | 117 | if (!clazz) { 118 | [self mj_enumerateAllClasses:^(__unsafe_unretained Class c, BOOL *stop) { 119 | NSDictionary *dict = objc_getAssociatedObject(c, &MJObjectClassInArrayKey); 120 | if (dict) { 121 | clazz = dict[propertyName]; 122 | } 123 | if (clazz) *stop = YES; 124 | }]; 125 | } 126 | 127 | // 如果是NSString类型 128 | if ([clazz isKindOfClass:[NSString class]]) { 129 | clazz = NSClassFromString(clazz); 130 | } 131 | return clazz; 132 | } 133 | 134 | #pragma mark - --公共方法-- 135 | + (void)mj_enumerateProperties:(MJPropertiesEnumeration)enumeration 136 | { 137 | // 获得成员变量 138 | NSArray *cachedProperties = [self properties]; 139 | 140 | // 遍历成员变量 141 | BOOL stop = NO; 142 | for (MJProperty *property in cachedProperties) { 143 | enumeration(property, &stop); 144 | if (stop) break; 145 | } 146 | } 147 | 148 | #pragma mark - 公共方法 149 | + (NSMutableArray *)properties 150 | { 151 | NSMutableArray *cachedProperties = [self dictForKey:&MJCachedPropertiesKey][NSStringFromClass(self)]; 152 | 153 | if (cachedProperties == nil) { 154 | cachedProperties = [NSMutableArray array]; 155 | 156 | [self mj_enumerateClasses:^(__unsafe_unretained Class c, BOOL *stop) { 157 | // 1.获得所有的成员变量 158 | unsigned int outCount = 0; 159 | objc_property_t *properties = class_copyPropertyList(c, &outCount); 160 | 161 | // 2.遍历每一个成员变量 162 | for (unsigned int i = 0; i 10 | #import "MJExtensionConst.h" 11 | 12 | @interface NSString (MJExtension) 13 | /** 14 | * 驼峰转下划线(loveYou -> love_you) 15 | */ 16 | - (NSString *)mj_underlineFromCamel; 17 | /** 18 | * 下划线转驼峰(love_you -> loveYou) 19 | */ 20 | - (NSString *)mj_camelFromUnderline; 21 | /** 22 | * 首字母变大写 23 | */ 24 | - (NSString *)mj_firstCharUpper; 25 | /** 26 | * 首字母变小写 27 | */ 28 | - (NSString *)mj_firstCharLower; 29 | 30 | - (BOOL)mj_isPureInt; 31 | 32 | - (NSURL *)mj_url; 33 | @end 34 | 35 | @interface NSString (MJExtensionDeprecated_v_2_5_16) 36 | - (NSString *)underlineFromCamel MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 37 | - (NSString *)camelFromUnderline MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 38 | - (NSString *)firstCharUpper MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 39 | - (NSString *)firstCharLower MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 40 | - (BOOL)isPureInt MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 41 | - (NSURL *)url MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 42 | @end 43 | -------------------------------------------------------------------------------- /CCLKLineChartViewDemo/CCLKLineChartViewDemo/MJExtension/NSString+MJExtension.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSString+MJExtension.m 3 | // MJExtensionExample 4 | // 5 | // Created by MJ Lee on 15/6/7. 6 | // Copyright (c) 2015年 小码哥. All rights reserved. 7 | // 8 | 9 | #import "NSString+MJExtension.h" 10 | 11 | @implementation NSString (MJExtension) 12 | - (NSString *)mj_underlineFromCamel 13 | { 14 | if (self.length == 0) return self; 15 | NSMutableString *string = [NSMutableString string]; 16 | for (NSUInteger i = 0; i= 2) [string appendString:[cmp substringFromIndex:1]]; 40 | } else { 41 | [string appendString:cmp]; 42 | } 43 | } 44 | return string; 45 | } 46 | 47 | - (NSString *)mj_firstCharLower 48 | { 49 | if (self.length == 0) return self; 50 | NSMutableString *string = [NSMutableString string]; 51 | [string appendString:[NSString stringWithFormat:@"%c", [self characterAtIndex:0]].lowercaseString]; 52 | if (self.length >= 2) [string appendString:[self substringFromIndex:1]]; 53 | return string; 54 | } 55 | 56 | - (NSString *)mj_firstCharUpper 57 | { 58 | if (self.length == 0) return self; 59 | NSMutableString *string = [NSMutableString string]; 60 | [string appendString:[NSString stringWithFormat:@"%c", [self characterAtIndex:0]].uppercaseString]; 61 | if (self.length >= 2) [string appendString:[self substringFromIndex:1]]; 62 | return string; 63 | } 64 | 65 | - (BOOL)mj_isPureInt 66 | { 67 | NSScanner *scan = [NSScanner scannerWithString:self]; 68 | int val; 69 | return [scan scanInt:&val] && [scan isAtEnd]; 70 | } 71 | 72 | - (NSURL *)mj_url 73 | { 74 | // [self stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet characterSetWithCharactersInString:@"!$&'()*+,-./:;=?@_~%#[]"]]; 75 | 76 | return [NSURL URLWithString:(NSString *)CFBridgingRelease(CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault, (CFStringRef)self, (CFStringRef)@"!$&'()*+,-./:;=?@_~%#[]", NULL,kCFStringEncodingUTF8))]; 77 | } 78 | @end 79 | 80 | @implementation NSString (MJExtensionDeprecated_v_2_5_16) 81 | - (NSString *)underlineFromCamel 82 | { 83 | return self.mj_underlineFromCamel; 84 | } 85 | 86 | - (NSString *)camelFromUnderline 87 | { 88 | return self.mj_camelFromUnderline; 89 | } 90 | 91 | - (NSString *)firstCharLower 92 | { 93 | return self.mj_firstCharLower; 94 | } 95 | 96 | - (NSString *)firstCharUpper 97 | { 98 | return self.mj_firstCharUpper; 99 | } 100 | 101 | - (BOOL)isPureInt 102 | { 103 | return self.mj_isPureInt; 104 | } 105 | 106 | - (NSURL *)url 107 | { 108 | return self.mj_url; 109 | } 110 | @end 111 | -------------------------------------------------------------------------------- /CCLKLineChartViewDemo/CCLKLineChartViewDemo/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // CCLKLineChartViewDemo 4 | // 5 | // Created by Crisps on 16/10/27. 6 | // Copyright © 2016年 来一碗鸭汤. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ViewController : UIViewController 12 | 13 | 14 | @end 15 | 16 | -------------------------------------------------------------------------------- /CCLKLineChartViewDemo/CCLKLineChartViewDemo/ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.m 3 | // CCLKLineChartViewDemo 4 | // 5 | // Created by Crisps on 16/10/27. 6 | // Copyright © 2016年 来一碗鸭汤. All rights reserved. 7 | // 8 | 9 | #import "ViewController.h" 10 | #import "CCLKLineChartView.h" 11 | #import "CCLTimeView.h" 12 | @interface ViewController () 13 | 14 | @end 15 | 16 | @implementation ViewController 17 | 18 | - (void)viewDidLoad { 19 | [super viewDidLoad]; 20 | 21 | CCLKLineChartView *klineChart = [[CCLKLineChartView alloc] initWithFrame:CGRectMake(0, 60, [UIScreen mainScreen].bounds.size.width, 250) 22 | secID:@"22" 23 | andtype:CCLKLineDay]; 24 | [self.view addSubview:klineChart]; 25 | 26 | CCLTimeView *timeView = [[CCLTimeView alloc]initWithFrame:CGRectMake(0, 320, [UIScreen mainScreen].bounds.size.width, 250) 27 | andSecID:@"22"]; 28 | [self.view addSubview:timeView]; 29 | 30 | 31 | } 32 | 33 | 34 | - (void)didReceiveMemoryWarning { 35 | [super didReceiveMemoryWarning]; 36 | // Dispose of any resources that can be recreated. 37 | } 38 | 39 | 40 | @end 41 | -------------------------------------------------------------------------------- /CCLKLineChartViewDemo/CCLKLineChartViewDemo/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // CCLKLineChartViewDemo 4 | // 5 | // Created by Crisps on 16/10/27. 6 | // Copyright © 2016年 来一碗鸭汤. 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 | -------------------------------------------------------------------------------- /CCLKLineChartViewDemo/CCLKLineChartViewDemoTests/CCLKLineChartViewDemoTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // CCLKLineChartViewDemoTests.m 3 | // CCLKLineChartViewDemoTests 4 | // 5 | // Created by Crisps on 16/10/27. 6 | // Copyright © 2016年 来一碗鸭汤. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface CCLKLineChartViewDemoTests : XCTestCase 12 | 13 | @end 14 | 15 | @implementation CCLKLineChartViewDemoTests 16 | 17 | - (void)setUp { 18 | [super setUp]; 19 | // Put setup code here. This method is called before the invocation of each test method in the class. 20 | } 21 | 22 | - (void)tearDown { 23 | // Put teardown code here. This method is called after the invocation of each test method in the class. 24 | [super tearDown]; 25 | } 26 | 27 | - (void)testExample { 28 | // This is an example of a functional test case. 29 | // Use XCTAssert and related functions to verify your tests produce the correct results. 30 | } 31 | 32 | - (void)testPerformanceExample { 33 | // This is an example of a performance test case. 34 | [self measureBlock:^{ 35 | // Put the code you want to measure the time of here. 36 | }]; 37 | } 38 | 39 | @end 40 | -------------------------------------------------------------------------------- /CCLKLineChartViewDemo/CCLKLineChartViewDemoTests/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 | -------------------------------------------------------------------------------- /CCLKLineChartViewDemo/CCLKLineChartViewDemoUITests/CCLKLineChartViewDemoUITests.m: -------------------------------------------------------------------------------- 1 | // 2 | // CCLKLineChartViewDemoUITests.m 3 | // CCLKLineChartViewDemoUITests 4 | // 5 | // Created by Crisps on 16/10/27. 6 | // Copyright © 2016年 来一碗鸭汤. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface CCLKLineChartViewDemoUITests : XCTestCase 12 | 13 | @end 14 | 15 | @implementation CCLKLineChartViewDemoUITests 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 | -------------------------------------------------------------------------------- /CCLKLineChartViewDemo/CCLKLineChartViewDemoUITests/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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ### 介绍: 2 | - 这是以`雪球APP`为原型,基于 `Objective-C`的K线开源项目。 3 | - 该项目整体设计思路已经经过某成熟证券APP的商业认证。 4 | - 本项目将K线业务代码尽可能缩减,保留核心功能,可流畅、高效实现手势交互。 5 | - K线难点在于手势交互和数据动态刷新上,功能并不复杂,关键在于设计思路。 6 | 7 | 8 | ### 建议: 9 | 10 | - 如果搭建K线为公司业务,不建议采用集成度高的开源代码。庞大臃肿,纵然短期匆忙上线,难以应付后期灵活需求变更。 11 | 12 | 13 | `Swift`版请移步 https://github.com/cclion/KLineView 。 14 | 15 | ### 设计思路&&难点: 16 | K线难点在于手势的处理上,`捏合、长按、拖拽`都需要展示不同效果。以下是Z君当时做K线时遇到的问题的解决方案; 17 | 18 | #### 1. 捏合手势需要动态改变K线柱的宽度,对应的增加或减少当前界面K线柱的展示数量,并且根据当前展示的数据计算出当前展示数据的极值。 19 | 20 | 采用`UITableView`类实现,将K线柱封装在`cell`中,在`tableview`中监听捏合手势,对应改变`cell`的高度,同时刷新`cell`中K线柱的布局来实现动态改变K线柱的宽度。 21 | 22 | 采用`UITableView`还有一个好处就是可以采用`cell`的重用机制降低内存。 23 | 24 | 注意:因为`UITableView`默认是上下滑动,而K线柱是左右滑动,Z君这里将`UITableView`做了一个顺时针90°的旋转。 25 | ![tableView旋转90°](https://upload-images.jianshu.io/upload_images/3425250-2b92b7cce0268ee2.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 26 | 27 | #### 2. K线柱绘制 28 | K线柱采用`CAShapeLayer`配合`UIBezierPath`绘制,内存低,效率高,棒棒哒! 29 | 30 | 关于CAShapeLayer的使用大家可以看这篇 https://zsisme.gitbooks.io/ios-/content/chapter6/cashapelayer.html 31 | (现在的google、baidu,好文章都搜不到,一搜全是简单调用两个方法就发的博客,还是翻了两年前的收藏才找到这个网站,强烈推荐大家) 32 | 33 | #### 3. 捏合时保证捏合中心点不变,两边以捏合中间点为中心进行收缩或扩散 34 | 35 | 因为`UITableView`在改变`cell`的高度时,默认时不会改变偏移量,所以不能保证捏合的中心点不变,这里我们的小学知识就会用上了。 36 | ![捏合时状态.png](https://upload-images.jianshu.io/upload_images/3425250-dc74b97e9cc67fc5.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 37 | 38 | 我们可以通过变量定义控件间距离。 39 | ![捏合前后.png](https://upload-images.jianshu.io/upload_images/3425250-eef1802efdddcc61.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 40 | 41 | 保证捏合中心点的中K线柱的中心点还在捏合前,就需要`c1 = c2` ,计算出`O2`,在捏合完,设置偏移量为`O2`即可。 42 | ![计算偏移量.png](https://upload-images.jianshu.io/upload_images/3425250-1e56905ede398f14.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 43 | 44 | #### 4. K线其他线性指标如何绘制 45 | 46 | 在K线中除了K线柱之外,还有其他`均线指标`,连贯整个数据显示区。 47 | ![线性指标](https://upload-images.jianshu.io/upload_images/3425250-fd6d12d2c69a2dbe.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 48 | 49 | 由图可以看出均线指标由每个`cell`中心点的数据连接相邻的`cell`中心点的数据。我们依旧将绘制放在`cell`中,将相连两个`cell`的线分割成两段,分别在各自所属的`cell`中绘制。 50 | 51 | 需要我们做的就是就是在cell中传入相邻的`cell`的`soureData`,计算出相邻中点的位置,分为两段绘制。 52 | ![分割绘制](https://upload-images.jianshu.io/upload_images/3425250-ae67ab2f0435be14.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 53 | 54 | --- 55 | 大家针对K线有什么问题都可以在下面留言,会第一时间解答。 56 | 未完待续 57 | --------------------------------------------------------------------------------