├── WX@2x.png ├── Resource ├── 横屏.png ├── 火币.jpg ├── 竖屏.png ├── kline.gif ├── 内存消耗.png └── 宏定义适配.png ├── .gitattributes ├── Podfile ├── AbuKlineView.xcworkspace ├── xcuserdata │ ├── jefferson.zhang.xcuserdatad │ │ ├── xcdebugger │ │ │ └── Breakpoints_v2.xcbkptlist │ │ └── UserInterfaceState.xcuserstate │ ├── haoji.xcuserdatad │ │ ├── UserInterfaceState.xcuserstate │ │ └── xcdebugger │ │ │ └── Breakpoints_v2.xcbkptlist │ └── jefferson.xcuserdatad │ │ ├── UserInterfaceState.xcuserstate │ │ └── xcdebugger │ │ └── Breakpoints_v2.xcbkptlist ├── contents.xcworkspacedata └── xcshareddata │ └── IDEWorkspaceChecks.plist ├── .gitignore ├── AbuKlineView.xcodeproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcuserdata │ │ └── haoji.xcuserdatad │ │ └── UserInterfaceState.xcuserstate └── xcuserdata │ ├── imac.xcuserdatad │ └── xcschemes │ │ └── xcschememanagement.plist │ ├── haoji.xcuserdatad │ └── xcschemes │ │ ├── xcschememanagement.plist │ │ └── AbuKlineView.xcscheme │ ├── jefferson.xcuserdatad │ └── xcschemes │ │ ├── xcschememanagement.plist │ │ └── AbuKlineView.xcscheme │ └── jefferson.zhang.xcuserdatad │ └── xcschemes │ ├── xcschememanagement.plist │ └── AbuKlineView.xcscheme ├── AbuKlineView ├── ViewController.h ├── BaseViewController.h ├── KlineView │ ├── Category │ │ ├── UIColor+Extension.h │ │ ├── UIBezierPath+Extension.h │ │ ├── UIView+Extension.h │ │ ├── UIColor+Extension.m │ │ ├── UIView+Extension.m │ │ └── UIBezierPath+Extension.m │ ├── KLineView │ │ ├── KLineView │ │ │ ├── AbuKlineView.h │ │ │ ├── AbuCharViewProtocol.h │ │ │ ├── AbuCandChartView.h │ │ │ ├── AbuKlineView.m │ │ │ └── AbuCandChartView.m │ │ ├── KlineCommonView │ │ │ ├── KlineVerticalView.h │ │ │ ├── KlineCurrentPriceView.h │ │ │ ├── KlinePriceView.h │ │ │ ├── KlineCurrentPriceView.m │ │ │ ├── KlineVerticalView.m │ │ │ └── KlinePriceView.m │ │ ├── KLineLayer │ │ │ ├── KlineTimeLayer.h │ │ │ ├── KlineQuotaColumnLayer.h │ │ │ ├── KlineCandelLayer.h │ │ │ ├── KlineMALayer.h │ │ │ ├── KlineTimeLayer.m │ │ │ ├── KlineCandelLayer.m │ │ │ ├── KlineQuotaColumnLayer.m │ │ │ └── KlineMALayer.m │ │ ├── KLineCaculate │ │ │ ├── KLineCalculate.h │ │ │ ├── KLineSubCalculate.h │ │ │ ├── KLineSubCalculate.m │ │ │ └── KLineCalculate.m │ │ └── KLineTitleView │ │ │ ├── KlineTitleView.h │ │ │ └── KlineTitleView.m │ ├── KLineModel │ │ ├── KLineModel.m │ │ └── KLineModel.h │ └── APIManage │ │ └── PrefixHeader.pch ├── main.m ├── AppDelegate.h ├── Assets.xcassets │ └── AppIcon.appiconset │ │ └── Contents.json ├── Info.plist ├── BaseViewController.m ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard ├── AppDelegate.m ├── Prefix.pch ├── ViewController.m ├── stock30.json └── stock5.json ├── Podfile.lock └── README.md /WX@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbuIOSDeveloper/KLine/HEAD/WX@2x.png -------------------------------------------------------------------------------- /Resource/横屏.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbuIOSDeveloper/KLine/HEAD/Resource/横屏.png -------------------------------------------------------------------------------- /Resource/火币.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbuIOSDeveloper/KLine/HEAD/Resource/火币.jpg -------------------------------------------------------------------------------- /Resource/竖屏.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbuIOSDeveloper/KLine/HEAD/Resource/竖屏.png -------------------------------------------------------------------------------- /Resource/kline.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbuIOSDeveloper/KLine/HEAD/Resource/kline.gif -------------------------------------------------------------------------------- /Resource/内存消耗.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbuIOSDeveloper/KLine/HEAD/Resource/内存消耗.png -------------------------------------------------------------------------------- /Resource/宏定义适配.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbuIOSDeveloper/KLine/HEAD/Resource/宏定义适配.png -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | *.c linguist-language=Objective-C 4 | -------------------------------------------------------------------------------- /Podfile: -------------------------------------------------------------------------------- 1 | platform :ios,'7.0' 2 | target 'AbuKlineView' do 3 | 4 | pod 'Masonry' 5 | pod 'KVOController' 6 | pod 'MJExtension' 7 | 8 | end 9 | -------------------------------------------------------------------------------- /AbuKlineView.xcworkspace/xcuserdata/jefferson.zhang.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | AbuKlineView.xcworkspace/xcuserdata/jefferson.xcuserdatad/UserInterfaceState.xcuserstate 3 | *.xcuserstate 4 | AbuKlineView.xcworkspace/xcuserdata/jefferson.xcuserdatad/UserInterfaceState.xcuserstate 5 | -------------------------------------------------------------------------------- /AbuKlineView.xcworkspace/xcuserdata/haoji.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbuIOSDeveloper/KLine/HEAD/AbuKlineView.xcworkspace/xcuserdata/haoji.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /AbuKlineView.xcworkspace/xcuserdata/jefferson.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbuIOSDeveloper/KLine/HEAD/AbuKlineView.xcworkspace/xcuserdata/jefferson.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /AbuKlineView.xcworkspace/xcuserdata/jefferson.zhang.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbuIOSDeveloper/KLine/HEAD/AbuKlineView.xcworkspace/xcuserdata/jefferson.zhang.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /AbuKlineView.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /AbuKlineView.xcodeproj/project.xcworkspace/xcuserdata/haoji.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbuIOSDeveloper/KLine/HEAD/AbuKlineView.xcodeproj/project.xcworkspace/xcuserdata/haoji.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /AbuKlineView/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // AbuKlineView 4 | // 5 | // Created by 阿布 on 17/9/6. 6 | // Copyright © 2017年 阿布. All rights reserved. 7 | // 8 | 9 | #import "BaseViewController.h" 10 | 11 | @interface ViewController : BaseViewController 12 | 13 | 14 | @end 15 | 16 | -------------------------------------------------------------------------------- /AbuKlineView.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /AbuKlineView.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /AbuKlineView/BaseViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // BaseViewController.h 3 | // AbuKline 4 | // 5 | // Created by jefferson on 2018/8/27. 6 | // Copyright © 2018年 jefferson. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface BaseViewController : UIViewController 12 | 13 | - (void)supportRotion:(int)rotion; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /AbuKlineView/KlineView/Category/UIColor+Extension.h: -------------------------------------------------------------------------------- 1 | // 2 | // UIColor+Extension.h 3 | // AbuKlineView 4 | // 5 | // Created by 阿布 on 17/9/6. 6 | // Copyright © 2017年 阿布. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface UIColor (Extension) 12 | 13 | + (UIColor *)colorWithHexString:(NSString *)color; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /AbuKlineView/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // AbuKlineView 4 | // 5 | // Created by 阿布 on 17/9/6. 6 | // Copyright © 2017年 阿布. 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 | -------------------------------------------------------------------------------- /AbuKlineView/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // AbuKlineView 4 | // 5 | // Created by 阿布 on 17/9/6. 6 | // Copyright © 2017年 阿布. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface AppDelegate : UIResponder 12 | 13 | @property (strong, nonatomic) UIWindow *window; 14 | 15 | @property (nonatomic,assign)BOOL allowRotation; 16 | 17 | @end 18 | 19 | -------------------------------------------------------------------------------- /AbuKlineView/KlineView/KLineView/KLineView/AbuKlineView.h: -------------------------------------------------------------------------------- 1 | // 2 | // AbuKlineView.h 3 | // AbuKlineView 4 | // 5 | // Created by Jefferson.zhang on 2017/9/6. 6 | // Copyright © 2017年 阿布. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | 12 | @interface AbuKlineView : UIView 13 | 14 | 15 | @property (nonatomic,strong) NSMutableArray<__kindof KLineModel*> *dataArray; 16 | 17 | - (void)refreshFSKlineView:(KLineModel *)model; 18 | 19 | @end 20 | -------------------------------------------------------------------------------- /AbuKlineView.xcodeproj/xcuserdata/imac.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | AbuKlineView.xcscheme_^#shared#^_ 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /AbuKlineView/KlineView/KLineView/KlineCommonView/KlineVerticalView.h: -------------------------------------------------------------------------------- 1 | // 2 | // KlineVerticalView.h 3 | // ABuKLineChartView 4 | // 5 | // Created by jefferson on 2018/9/14. 6 | // Copyright © 2018年 jefferson. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface KlineVerticalView : UIView 12 | - (void)updateTimeString:(NSString *)timeString; 13 | - (void)updateTimeLeft:(CGFloat)leftdistance; 14 | - (void)updateHeight:(CGFloat)candleChartHeight; 15 | @end 16 | -------------------------------------------------------------------------------- /AbuKlineView/KlineView/KLineView/KlineCommonView/KlineCurrentPriceView.h: -------------------------------------------------------------------------------- 1 | // 2 | // KlineCurrentPriceView.h 3 | // ABuKLineChartView 4 | // 5 | // Created by jefferson on 2018/9/14. 6 | // Copyright © 2018年 jefferson. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface KlineCurrentPriceView : UIView 12 | - (instancetype)initWithUpdate:(BOOL)update; 13 | - (void)updateNewPrice:(NSString *)newPrice backgroundColor:(UIColor *)color precision:(int)precision; 14 | @end 15 | -------------------------------------------------------------------------------- /AbuKlineView/KlineView/KLineView/KLineLayer/KlineTimeLayer.h: -------------------------------------------------------------------------------- 1 | // 2 | // KlineTimeLayer.h 3 | // ABuKLineChartView 4 | // 5 | // Created by jefferson on 2018/9/7. 6 | // Copyright © 2018年 jefferson. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface KlineTimeLayer : CAShapeLayer 12 | - (instancetype)initQuotaDataArr:(NSArray *)dataArr candleWidth:(CGFloat)candleWidth height:(CGFloat)height timeHight:(CGFloat)timeHeight bottomMargin:(CGFloat)bottomMargin lineWidth:(CGFloat)lineWidth; 13 | @end 14 | -------------------------------------------------------------------------------- /AbuKlineView/KlineView/KLineView/KLineLayer/KlineQuotaColumnLayer.h: -------------------------------------------------------------------------------- 1 | // 2 | // KlineQuotaColumnLayer.h 3 | // ABuKLineChartView 4 | // 5 | // Created by jefferson on 2018/9/10. 6 | // Copyright © 2018年 jefferson. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface KlineQuotaColumnLayer : CAShapeLayer 12 | - (instancetype)initQuotaDataArr:(NSArray *)dataArr candleWidth:(CGFloat)candleWidth candleSpace:(CGFloat)candleSpace startIndex:(CGFloat)startIndex maxValue:(CGFloat)maxValue heightPerpoint:(CGFloat)heightPerpoint qutoaHeight:(CGFloat)qutoaHeight; 13 | @end 14 | -------------------------------------------------------------------------------- /AbuKlineView/KlineView/KLineView/KLineLayer/KlineCandelLayer.h: -------------------------------------------------------------------------------- 1 | // 2 | // KlineCandelLayer.h 3 | // ABuKLineChartView 4 | // 5 | // Created by jefferson on 2018/9/7. 6 | // Copyright © 2018年 jefferson. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface KlineCandelLayer : CAShapeLayer 12 | 13 | - (instancetype)initQuotaDataArr:(NSArray *)dataArr candleWidth:(CGFloat)candleWidth candleSpace:(CGFloat)candleSpace startIndex:(CGFloat)startIndex maxValue:(CGFloat)maxValue heightPerpoint:(CGFloat)heightPerpoint scrollViewContentWidth:(CGFloat)contentWidth; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - KVOController (1.2.0) 3 | - Masonry (1.0.2) 4 | - MJExtension (3.0.15.1) 5 | 6 | DEPENDENCIES: 7 | - KVOController 8 | - Masonry 9 | - MJExtension 10 | 11 | SPEC REPOS: 12 | https://github.com/cocoapods/specs.git: 13 | - KVOController 14 | - Masonry 15 | - MJExtension 16 | 17 | SPEC CHECKSUMS: 18 | KVOController: d72ace34afea42468329623b3379ab3cd1d286b6 19 | Masonry: 7c429b56da9d4ee0bbb3ed77a5ea710d6a5df39e 20 | MJExtension: 19bc33ce2eee2d319760f5622562cf581a6ff756 21 | 22 | PODFILE CHECKSUM: 4c339d2170dabdb62d78447c3e46d06ce767365a 23 | 24 | COCOAPODS: 1.5.3 25 | -------------------------------------------------------------------------------- /AbuKlineView.xcworkspace/xcuserdata/haoji.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 8 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /AbuKlineView.xcworkspace/xcuserdata/jefferson.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 8 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /AbuKlineView.xcodeproj/xcuserdata/haoji.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | AbuKlineView.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | 3B5D5BBC1F5F10A100C0E68B 16 | 17 | primary 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /AbuKlineView.xcodeproj/xcuserdata/jefferson.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | AbuKlineView.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | 3B5D5BBC1F5F10A100C0E68B 16 | 17 | primary 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /AbuKlineView.xcodeproj/xcuserdata/jefferson.zhang.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | AbuKlineView.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | 3B5D5BBC1F5F10A100C0E68B 16 | 17 | primary 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /AbuKlineView/KlineView/Category/UIBezierPath+Extension.h: -------------------------------------------------------------------------------- 1 | // 2 | // UIBezierPath+Extension.h 3 | // AbuKlineView 4 | // 5 | // Created by 阿布 on 17/9/6. 6 | // Copyright © 2017年 阿布. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface UIBezierPath (Extension) 12 | 13 | + (UIBezierPath*)drawWireLine:(NSMutableArray*)linesArray; 14 | 15 | 16 | + (NSMutableArray<__kindof UIBezierPath*>*)drawLines:(NSMutableArray*)linesArray; 17 | 18 | + (UIBezierPath *)drawKLine:(CGFloat)open close:(CGFloat)close high:(CGFloat)high low:(CGFloat)low candleWidth:(CGFloat)candleWidth xPostion:(CGFloat)xPostion lineWidth:(CGFloat)lineWidth; 19 | 20 | @end 21 | -------------------------------------------------------------------------------- /AbuKlineView/KlineView/KLineView/KLineCaculate/KLineCalculate.h: -------------------------------------------------------------------------------- 1 | // 2 | // KLineCalculate.h 3 | // ABuKLineChartView 4 | // 5 | // Created by jefferson on 2018/7/19. 6 | // Copyright © 2018年 jefferson. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | static NSString *const kMaxValue = @"kMaxValue"; 12 | static NSString *const kMinValue = @"kMinValue"; 13 | 14 | @interface KLineCalculate : NSObject 15 | 16 | + (instancetype)sharedInstance; 17 | 18 | - (NSDictionary *)calculateMaxAndMinValueWithDataArr:(NSArray *)dataArr; 19 | 20 | - (NSInteger)getTimesampIntervalWithRequestType:(NSString *)requestType timesamp:(NSInteger)lastKlineModelTimesamp; 21 | 22 | @end 23 | -------------------------------------------------------------------------------- /AbuKlineView/KlineView/KLineView/KLineLayer/KlineMALayer.h: -------------------------------------------------------------------------------- 1 | // 2 | // KlineMALayer.h 3 | // ABuKLineChartView 4 | // 5 | // Created by jefferson on 2018/9/7. 6 | // Copyright © 2018年 jefferson. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface KlineMALayer : CAShapeLayer 12 | - (instancetype)initQuotaDataArr:(NSArray *)dataArr candleWidth:(CGFloat)candleWidth candleSpace:(CGFloat)candleSpace startIndex:(CGFloat)startIndex maxValue:(CGFloat)maxValue heightPerpoint:(CGFloat)heightPerpoint totalHeight:(CGFloat)totalHeight lineWidth:(CGFloat)lineWidth lineColor:(UIColor *)lineColor wireType:(WIRETYPE)wiretype klineSubOrMain:(KlineWireSubOrMain)klineWiretype; 13 | @end 14 | -------------------------------------------------------------------------------- /AbuKlineView/KlineView/Category/UIView+Extension.h: -------------------------------------------------------------------------------- 1 | // 2 | // UIView+Extension.h 3 | // AbuKlineView 4 | // 5 | // Created by 阿布 on 17/9/6. 6 | // Copyright © 2017年 阿布. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface UIView (Extension) 12 | 13 | @property (nonatomic, assign) CGFloat x; 14 | @property (nonatomic, assign) CGFloat y; 15 | @property (nonatomic, assign) CGFloat centerX; 16 | @property (nonatomic, assign) CGFloat centerY; 17 | @property (nonatomic, assign) CGFloat width; 18 | @property (nonatomic, assign) CGFloat height; 19 | @property (nonatomic, assign) CGFloat scrrenWidth; 20 | @property (nonatomic, assign) CGFloat scrrenHeight; 21 | @property (nonatomic, assign) CGSize size; 22 | 23 | @end 24 | -------------------------------------------------------------------------------- /AbuKlineView/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 | } -------------------------------------------------------------------------------- /AbuKlineView/KlineView/KLineView/KlineCommonView/KlinePriceView.h: -------------------------------------------------------------------------------- 1 | // 2 | // KlinePriceView.h 3 | // ExchangeKline 4 | // 5 | // Created by jefferson on 2018/8/2. 6 | // Copyright © 2018年 jefferson. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface KlinePriceView : UIView 12 | 13 | @property (nonatomic,strong) NSArray *priceArr; 14 | 15 | - (instancetype)initWithFrame:(CGRect)frame PriceArr:(NSArray *)priceArr; 16 | 17 | - (void)reloadPriceWithPriceArr:(NSArray *)priceArr precision:(int)precision; 18 | 19 | - (void)hideZeroLabel:(BOOL)isHide; 20 | 21 | - (void)refreshCurrentPositionPricePositonY:(CGFloat)positionY price:(NSString *)price; 22 | 23 | - (void)refreshCurrentPositionPrice:(NSString *)price; 24 | 25 | - (void)updateFrameWithHeight:(CGFloat)height; 26 | 27 | @end 28 | -------------------------------------------------------------------------------- /AbuKlineView/KlineView/KLineView/KLineCaculate/KLineSubCalculate.h: -------------------------------------------------------------------------------- 1 | // 2 | // KLineSubCalculate.h 3 | // ABuKLineChartView 4 | // 5 | // Created by jefferson on 2018/7/19. 6 | // Copyright © 2018年 jefferson. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | typedef enum { 12 | SubATR = 0, 13 | SubBIAS = 1, 14 | SubCCI = 2, 15 | SubKD = 3, 16 | SubMACD = 4, 17 | SubRSI = 5, 18 | SubW_R = 6, 19 | SubARBR = 7, 20 | SubDKBY = 8, 21 | SubKDJ = 9, 22 | SubLW_R = 10, ///< _ -> & 23 | SubQHLSR = 11, 24 | }SubKPIType; 25 | 26 | @interface KLineSubCalculate : NSObject 27 | 28 | + (instancetype)sharedInstance; 29 | - (void)handleQuotaDataWithDataArr:(NSArray *)dataArr model:(KLineModel *)model index:(NSInteger)idx KPIType:(SubKPIType)type;; 30 | - (NSArray *)initializeQuotaDataWithArray:(NSArray *)dataArray KPIType:(SubKPIType)type;; 31 | 32 | @end 33 | -------------------------------------------------------------------------------- /AbuKlineView/KlineView/KLineView/KLineView/AbuCharViewProtocol.h: -------------------------------------------------------------------------------- 1 | // 2 | // AbuCharViewProtocol.h 3 | // AbuKlineView 4 | // 5 | // Created by 阿布 on 17/9/6. 6 | // Copyright © 2017年 阿布. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "KLineModel.h" 11 | @protocol AbuChartViewProtocol 12 | 13 | @optional 14 | 15 | - (void)displayScreenleftPostion:(CGFloat)leftPostion startIndex:(NSInteger)index count:(NSInteger)count; 16 | 17 | - (void)longPressCandleViewWithIndex:(NSInteger)kLineModeIndex kLineModel:(KLineModel *)kLineModel; 18 | 19 | - (void)displayLastModel:(KLineModel *)kLineModel; 20 | 21 | - (void)refreshCurrentPrice:(KLineModel *)kLineModel candleModel:(KLineModel *)candleModel; 22 | 23 | - (void)reloadPriceViewWithPriceArr:(NSArray *)priceArr; 24 | 25 | - (void)reloadPriceViewWithQuotaMaxValue:(double)maxValue MinValue:(double)minValue; 26 | 27 | - (void)displayMoreData; 28 | 29 | @end 30 | -------------------------------------------------------------------------------- /AbuKlineView/KlineView/Category/UIColor+Extension.m: -------------------------------------------------------------------------------- 1 | // 2 | // UIColor+Extension.m 3 | // AbuKlineView 4 | // 5 | // Created by 阿布 on 17/9/6. 6 | // Copyright © 2017年 阿布. All rights reserved. 7 | // 8 | 9 | #import "UIColor+Extension.h" 10 | 11 | @implementation UIColor (Extension) 12 | 13 | +(UIColor *)colorWithHexString:(NSString *)color 14 | { 15 | NSString *str1 = [NSString stringWithFormat:@"0x%@",[color substringWithRange:NSMakeRange(1, 2)]]; 16 | NSString *str2 = [NSString stringWithFormat:@"0x%@",[color substringWithRange:NSMakeRange(3, 2)]]; 17 | NSString *str3 = [NSString stringWithFormat:@"0x%@",[color substringWithRange:NSMakeRange(5, 2)]]; 18 | 19 | unsigned long f1 = strtoul([str1 UTF8String],0,16); 20 | unsigned long f2 = strtoul([str2 UTF8String],0,16); 21 | unsigned long f3 = strtoul([str3 UTF8String],0,16); 22 | 23 | return [UIColor colorWithRed:f1/255.0 green:f2/255.0 blue:f3/255.0 alpha:1.0]; 24 | } 25 | 26 | @end 27 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # AbuKlineView 2 | (CAShapelayer + UIBezierPath)绘制K线支撑横竖屏切换、刷新、长按、缩放、masonry适配,完美支持金融产品 3 | 4 | K线绘制(火币) 隐藏副图、成交量切换各种指标,logo水印,刷新,横竖屏,喜欢的可以star一波。有问题请添加QQ:2438100263 喜欢的可以添加群:362681337 5 | ![image](https://github.com/AbuIOSDeveloper/KLine/blob/master/Resource/火币.jpg) 6 | 7 | 非常的流畅,占用内存少,使用矢量进行填充K线 8 | 9 | ![image](https://github.com/AbuIOSDeveloper/KLine/blob/master/Resource/kline.gif) 10 | 11 | 通过宏自定义K线,让你随心所欲定义K线高度,宽度,主图、幅图占比 12 | ![image](https://github.com/AbuIOSDeveloper/KLine/blob/master/Resource/宏定义适配.png) 13 | 14 | 横竖屏完美适配(暂时没有适配iPhoneX)、随意切换 15 | ![image](https://github.com/AbuIOSDeveloper/KLine/blob/master/Resource/竖屏.png) 16 | 17 | 18 | 19 | 20 | K线幅图指标计算(能与国际主流平台数据完美吻合,只给出部分指标计算(MACD,KDJ、WR 一般平台K线有这些幅图指标就差不多),由于其他原因暂不公开其他二十几种指标算法) 21 | 22 | 23 | 24 | 后面将继续优化刷新逻辑、添加分时图、各种周期图的切换。 25 | 本GitHub已有GCDAsynSocket与Websocket的demo,行情尽在你的眼前,喜欢的给个star哦,谢谢! 26 | 27 | 喜欢的可以打赏一下,交流炒股心得 28 | ![image](https://github.com/AbuIOSDeveloper/KLine/blob/master/WX%402x.png) 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /AbuKlineView/KlineView/KLineView/KLineView/AbuCandChartView.h: -------------------------------------------------------------------------------- 1 | // 2 | // AbuCandChartView.h 3 | // AbuKlineView 4 | // 5 | // Created by 阿布 on 17/9/7. 6 | // Copyright © 2017年 阿布. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface AbuCandChartView : UIView 12 | @property (nonatomic,strong) NSMutableArray<__kindof KLineModel*> * dataArray; 13 | @property (nonatomic,assign) CGFloat candleSpace; 14 | @property (nonatomic,assign) CGFloat candleWidth; 15 | @property (nonatomic,assign) NSInteger currentStartIndex; 16 | @property (nonatomic,assign) CGFloat contentOffset; 17 | @property (nonatomic,assign) BOOL kvoEnable; 18 | @property (nonatomic,assign) BOOL hollowRise; 19 | 20 | - (CGPoint)getLongPressModelPostionWithXPostion:(CGFloat)xPostion; 21 | - (void)reloadKline; 22 | - (void)calcuteCandleWidth; 23 | - (void)updateWidth; 24 | - (void)drawKLine; 25 | - (void)drawPresetQuota:(KlineSubKPIType)presetQuotaName; 26 | - (void)refreshKLineView:(KLineModel *)model; 27 | @property (nonatomic,weak) id delegate; 28 | @property (nonatomic,assign) DataLineType currentType; 29 | @end 30 | -------------------------------------------------------------------------------- /AbuKlineView/KlineView/KLineView/KLineTitleView/KlineTitleView.h: -------------------------------------------------------------------------------- 1 | // 2 | // KlineTitleView.h 3 | // LGTS2 4 | // 5 | // Created by Jefferson.zhang on 2017/7/18. 6 | // Copyright © 2017年 Mata. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | typedef enum 12 | { 13 | K_LINE_5MIN = 0x01, // 5分钟 14 | K_LINE_15MIN = 0x02, // 15分钟 15 | K_LINE_30MIN = 0x03, // 30分钟 16 | K_LINE_60MIN = 0x04, // 60分钟 17 | K_LINE_DAY = 0x05, //日线 18 | K_LINE_WEEK = 0x06, // 周线 19 | K_LINE_MONTH = 0x07, // 月线 20 | K_LINE_VANE = 0x08, // 风向标 21 | K_LINE_YEAR = 0x09, // 年线 22 | K_LINE_MANY_DAY = 15, // 多日线 23 | K_LINE_MANY_MIN = 16, // 多分钟线 24 | K_LINE_MANY_HOUR = 17, // 多小时线 25 | K_LINE_1MIN = 34, // 1分时图 26 | K_LINE_MIN = 35, // 分钟线 27 | K_LINE_3MIN = 36, // 3分钟 28 | K_LINE_1HOUR = 37, // 1小时 29 | K_LINE_4HOUR = 38, // 4小时 30 | 31 | }KLINETYPE; 32 | 33 | @protocol KlineTitleViewDelegate 34 | - (void)selectIndex:(KLINETYPE)type; 35 | 36 | @end 37 | 38 | @interface KlineTitleView : UIView 39 | 40 | @property (nonatomic, weak) id delegate; 41 | 42 | - (instancetype)initWithklineTitleArray:(NSArray *)titleArray typeArray:(NSArray *)typeArray; 43 | 44 | 45 | 46 | @end 47 | -------------------------------------------------------------------------------- /AbuKlineView/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 | UIInterfaceOrientationLandscapeLeft 34 | UIInterfaceOrientationLandscapeRight 35 | UIInterfaceOrientationPortrait 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /AbuKlineView/BaseViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // BaseViewController.m 3 | // AbuKline 4 | // 5 | // Created by jefferson on 2018/8/27. 6 | // Copyright © 2018年 jefferson. All rights reserved. 7 | // 8 | 9 | #import "BaseViewController.h" 10 | #import "AppDelegate.h" 11 | 12 | @interface BaseViewController () 13 | 14 | @end 15 | 16 | @implementation BaseViewController 17 | 18 | - (void)viewDidLoad { 19 | [super viewDidLoad]; 20 | // Do any additional setup after loading the view. 21 | [self supportRotion:UIInterfaceOrientationPortrait]; 22 | } 23 | - (void)supportRotion:(int)rotion 24 | { 25 | NSNumber *value = [NSNumber numberWithInt:rotion]; 26 | [[UIDevice currentDevice] setValue:value forKey:@"orientation"]; 27 | } 28 | 29 | - (void)viewDidAppear:(BOOL)animated 30 | { 31 | [super viewDidAppear:animated]; 32 | 33 | ((AppDelegate *)[UIApplication sharedApplication].delegate).allowRotation = NO; 34 | 35 | } 36 | 37 | - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation 38 | { 39 | return NO; 40 | } 41 | 42 | #if __IPHONE_OS_VERSION_MAX_ALLOWED < __IPHONE_9_0 43 | - (NSUInteger)supportedInterfaceOrientations 44 | #else 45 | - (UIInterfaceOrientationMask)supportedInterfaceOrientations 46 | #endif 47 | { 48 | return UIInterfaceOrientationMaskPortrait; 49 | } 50 | 51 | 52 | 53 | - (BOOL)prefersStatusBarHidden 54 | { 55 | return NO; 56 | } 57 | 58 | - (BOOL)shouldAutorotate 59 | { 60 | return NO; 61 | } 62 | 63 | 64 | @end 65 | -------------------------------------------------------------------------------- /AbuKlineView/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 | -------------------------------------------------------------------------------- /AbuKlineView/KlineView/KLineModel/KLineModel.m: -------------------------------------------------------------------------------- 1 | // 2 | // KLineModel.m 3 | // ABuKLineChartView 4 | // 5 | // Created by jefferson on 2018/7/19. 6 | // Copyright © 2018年 jefferson. All rights reserved. 7 | // 8 | 9 | #import "KLineModel.h" 10 | 11 | static NSString *const kRise = @"kRise"; 12 | static NSString *const kDrop = @"kDrop"; 13 | 14 | @implementation KLineModel 15 | 16 | - (void)initData 17 | { 18 | 19 | [self EMA12]; 20 | [self EMA26]; 21 | [self DIF]; 22 | [self DEA]; 23 | [self MACD]; 24 | } 25 | 26 | - (void)reInitData 27 | { 28 | NSInteger SHORT = 12; 29 | NSInteger LONG = 26; 30 | NSInteger M = 9; 31 | 32 | self.EMA12 = @((2.0 * self.closePrice + (SHORT - 1) *(self.previousKlineModel.EMA12.doubleValue))/(SHORT + 1)); 33 | self.EMA26 = @((2 * self.closePrice + (LONG -1) * self.previousKlineModel.EMA26.doubleValue)/(LONG + 1)); 34 | self.DIF = @(self.EMA12.doubleValue - self.EMA26.doubleValue); 35 | self.DEA = @(self.previousKlineModel.DEA.doubleValue * (M-1)/(M+1) + 2.0 / (M+1) *self.DIF.doubleValue); 36 | self.MACD = @(2*(self.DIF.doubleValue - self.DEA.doubleValue)); 37 | 38 | } 39 | 40 | - (void)reInitKDJData 41 | { 42 | 43 | self.RSV_9 = @((self.closePrice - self.LNinePrice.doubleValue)/(self.HNinePrice.doubleValue-self.LNinePrice.doubleValue)*100); 44 | 45 | 46 | double previousK = 0; 47 | if (self.xPoint==8) { 48 | 49 | previousK = 50; 50 | }else{ 51 | previousK = self.previousKlineModel.KDJ_K.doubleValue; 52 | } 53 | self.KDJ_K = @(previousK*2/3.0+1/3.0*self.RSV_9.doubleValue); 54 | 55 | 56 | double previousD = 0; 57 | if (self.xPoint==8) { 58 | 59 | previousD = 50; 60 | }else{ 61 | previousD = self.previousKlineModel.KDJ_D.doubleValue; 62 | } 63 | self.KDJ_D = @(previousD*2/3.0+1/3.0*self.KDJ_K.doubleValue); 64 | 65 | 66 | self.KDJ_J = @(3*self.KDJ_K.doubleValue-2*self.KDJ_D.doubleValue); 67 | 68 | if (isnan(self.KDJ_K.doubleValue)) { 69 | self.KDJ_K = self.previousKlineModel.KDJ_K; 70 | 71 | } 72 | if (isnan(self.KDJ_D.doubleValue)) { 73 | 74 | self.KDJ_D = self.previousKlineModel.KDJ_D; 75 | } 76 | if (isnan(self.KDJ_J.doubleValue)) { 77 | 78 | self.KDJ_J = self.previousKlineModel.KDJ_J; 79 | } 80 | } 81 | 82 | 83 | @end 84 | -------------------------------------------------------------------------------- /AbuKlineView/KlineView/Category/UIView+Extension.m: -------------------------------------------------------------------------------- 1 | // 2 | // UIView+Extension.m 3 | // AbuKlineView 4 | // 5 | // Created by 阿布 on 17/9/6. 6 | // Copyright © 2017年 阿布. All rights reserved. 7 | // 8 | 9 | #import "UIView+Extension.h" 10 | 11 | @implementation UIView (Extension) 12 | 13 | -(void)setX:(CGFloat)x 14 | { 15 | CGRect tempF = self.frame; 16 | tempF.origin.x = x; 17 | self.frame = tempF; 18 | } 19 | 20 | -(CGFloat)x 21 | { 22 | return self.frame.origin.x; 23 | } 24 | 25 | -(void)setY:(CGFloat)y{ 26 | CGRect tempF = self.frame; 27 | tempF.origin.y = y ; 28 | self.frame = tempF; 29 | } 30 | 31 | -(CGFloat)y 32 | { 33 | return self.frame.origin.y; 34 | } 35 | 36 | -(void)setWidth:(CGFloat)width 37 | { 38 | CGRect tempF = self.frame; 39 | tempF.size.width = width; 40 | self.frame = tempF; 41 | } 42 | 43 | -(void)setCenterX:(CGFloat)centerX 44 | { 45 | CGPoint tempF = self.center; 46 | tempF.x = centerX; 47 | self.center = tempF; 48 | } 49 | -(CGFloat)centerX 50 | { 51 | return self.center.x; 52 | } 53 | 54 | -(void)setCenterY:(CGFloat)centerY 55 | { 56 | CGPoint tempF = self.center; 57 | tempF.y = centerY; 58 | self.center = tempF; 59 | } 60 | -(CGFloat)centerY 61 | { 62 | return self.center.y; 63 | } 64 | 65 | -(CGFloat)width 66 | { 67 | return self.frame.size.width; 68 | } 69 | 70 | -(void)setHeight:(CGFloat)height 71 | { 72 | CGRect tempF = self.frame; 73 | tempF.size.height = height; 74 | self.frame = tempF; 75 | } 76 | 77 | -(CGFloat)height 78 | { 79 | return self.frame.size.height; 80 | } 81 | 82 | - (void)setScrrenWidth:(CGFloat)scrrenWidth 83 | { 84 | CGRect tempF = self.frame; 85 | tempF.size.width = scrrenWidth; 86 | self.frame = tempF; 87 | } 88 | 89 | - (CGFloat)scrrenWidth 90 | { 91 | return self.frame.size.width; 92 | } 93 | 94 | - (void)setScrrenHeight:(CGFloat)scrrenHeight 95 | { 96 | CGRect tempF = self.frame; 97 | tempF.size.height = scrrenHeight; 98 | self.frame = tempF; 99 | } 100 | 101 | -(CGFloat)scrrenHeight 102 | { 103 | return self.frame.size.height; 104 | } 105 | 106 | -(void)setSize:(CGSize)size 107 | { 108 | CGRect tempF = self.frame; 109 | tempF.size = size; 110 | self.frame = tempF; 111 | } 112 | -(CGSize)size 113 | { 114 | return self.frame.size; 115 | } 116 | 117 | @end 118 | 119 | -------------------------------------------------------------------------------- /AbuKlineView/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // AbuKlineView 4 | // 5 | // Created by 阿布 on 17/9/6. 6 | // Copyright © 2017年 阿布. 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 | - (UIInterfaceOrientationMask)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(nullable UIWindow *)window{ 51 | 52 | if (self.allowRotation) { 53 | 54 | return UIInterfaceOrientationMaskLandscapeLeft | UIInterfaceOrientationMaskLandscapeRight | UIInterfaceOrientationMaskPortrait ; 55 | 56 | } 57 | return UIInterfaceOrientationMaskPortrait; 58 | } 59 | 60 | @end 61 | -------------------------------------------------------------------------------- /AbuKlineView/KlineView/KLineView/KLineCaculate/KLineSubCalculate.m: -------------------------------------------------------------------------------- 1 | // 2 | // KLineSubCalculate.m 3 | // ABuKLineChartView 4 | // 5 | // Created by jefferson on 2018/7/19. 6 | // Copyright © 2018年 jefferson. All rights reserved. 7 | // 8 | 9 | #import "KLineSubCalculate.h" 10 | #import "KLineModel.h" 11 | 12 | static NSString *const kRise = @"kRise"; 13 | static NSString *const kDrop = @"kDrop"; 14 | 15 | @implementation KLineSubCalculate 16 | 17 | static id _instance; 18 | 19 | + (instancetype)sharedInstance 20 | { 21 | static dispatch_once_t onceToken; 22 | dispatch_once(&onceToken, ^{ 23 | _instance = [[self alloc] init]; 24 | }); 25 | return _instance; 26 | } 27 | - (NSArray *)initializeQuotaDataWithArray:(NSArray *)dataArray KPIType:(SubKPIType)type 28 | { 29 | __weak typeof(self) weakSelf = self; 30 | [dataArray enumerateObjectsUsingBlock:^(KLineModel *model, NSUInteger idx, BOOL * _Nonnull stop) { 31 | 32 | [weakSelf handleQuotaDataWithDataArr:dataArray model:model index:idx KPIType:type]; 33 | 34 | }]; 35 | return dataArray; 36 | } 37 | 38 | - (void)handleQuotaDataWithDataArr:(NSArray *)dataArr model:(KLineModel *)model index:(NSInteger)idx KPIType:(SubKPIType)type 39 | { 40 | if (type == SubMACD) { 41 | [self calculateMACDWithDataArr:dataArr model:model index:idx]; 42 | [self calculateMAWithDataArr:dataArr num:5 model:model index:idx]; 43 | [self calculateMAWithDataArr:dataArr num:10 model:model index:idx]; 44 | [self calculateMAWithDataArr:dataArr num:20 model:model index:idx]; 45 | 46 | } 47 | } 48 | 49 | - (void)calculateMACDWithDataArr:(NSArray *)dataArr model:(KLineModel *)model index:(NSInteger)idx 50 | { 51 | KLineModel *previousKlineModel = [KLineModel new]; 52 | if (idx>0&&idx=num-1) { 65 | for (int i = 0; i *)drawLines:(NSMutableArray *)linesArray 32 | { 33 | if (IsArrayNull(linesArray)) 34 | return nil; 35 | NSMutableArray * result = [NSMutableArray array]; 36 | 37 | for (NSMutableArray * lineArray in linesArray) { 38 | UIBezierPath * path = [UIBezierPath drawWireLine:lineArray]; 39 | [result addObject:path]; 40 | } 41 | 42 | return result; 43 | 44 | } 45 | 46 | + (UIBezierPath *)drawKLine:(CGFloat)open close:(CGFloat)close high:(CGFloat)high low:(CGFloat)low candleWidth:(CGFloat)candleWidth xPostion:(CGFloat)xPostion lineWidth:(CGFloat)lineWidth 47 | { 48 | UIBezierPath * candlePath = [UIBezierPath bezierPath]; 49 | candlePath.lineWidth = lineWidth; 50 | CGFloat y = open > close ? close : open; 51 | CGFloat height = fabs(close-open); 52 | [candlePath moveToPoint:CGPointMake(xPostion, y)]; 53 | [candlePath addLineToPoint:CGPointMake(xPostion + candleWidth / 2.0f, y)]; 54 | if (y > high) { 55 | [candlePath addLineToPoint:CGPointMake(xPostion + candleWidth / 2.0f, high)]; 56 | [candlePath addLineToPoint:CGPointMake(xPostion + candleWidth / 2.0f, y)]; 57 | } 58 | 59 | [candlePath addLineToPoint:CGPointMake(xPostion + candleWidth, y)]; 60 | [candlePath addLineToPoint:CGPointMake(xPostion + candleWidth, y + height)]; 61 | [candlePath addLineToPoint:CGPointMake(xPostion + candleWidth / 2.0f, y + height)]; 62 | if ((y + height) < low) 63 | { 64 | [candlePath addLineToPoint:CGPointMake(xPostion + candleWidth / 2.0f, low)]; 65 | [candlePath addLineToPoint:CGPointMake(xPostion + candleWidth / 2.0f, y+height)]; 66 | } 67 | [candlePath addLineToPoint:CGPointMake(xPostion, y + height)]; 68 | [candlePath addLineToPoint:CGPointMake(xPostion, y)]; 69 | [candlePath closePath]; 70 | return candlePath; 71 | } 72 | 73 | @end 74 | -------------------------------------------------------------------------------- /AbuKlineView/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /AbuKlineView/KlineView/KLineView/KLineLayer/KlineTimeLayer.m: -------------------------------------------------------------------------------- 1 | // 2 | // KlineTimeLayer.m 3 | // ABuKLineChartView 4 | // 5 | // Created by jefferson on 2018/9/7. 6 | // Copyright © 2018年 jefferson. All rights reserved. 7 | // 8 | 9 | #import "KlineTimeLayer.h" 10 | 11 | @interface KlineTimeLayer() 12 | @property (nonatomic, strong) NSArray * klineDataSource; 13 | @property (nonatomic,assign) double height; 14 | @property (nonatomic,assign) CGFloat candleWidth; 15 | @property (nonatomic,assign) double timeHeight; 16 | @property (nonatomic,assign) double bottomMargin; 17 | @property (nonatomic,assign) double lineWidth; 18 | 19 | @end 20 | 21 | @implementation KlineTimeLayer 22 | 23 | - (instancetype)initQuotaDataArr:(NSArray *)dataArr candleWidth:(CGFloat)candleWidth height:(CGFloat)height timeHight:(CGFloat)timeHeight bottomMargin:(CGFloat)bottomMargin lineWidth:(CGFloat)lineWidth 24 | { 25 | self = [super init]; 26 | if (self) { 27 | self.klineDataSource = dataArr; 28 | self.candleWidth = candleWidth; 29 | self.height = height; 30 | self.timeHeight = timeHeight; 31 | self.bottomMargin = bottomMargin; 32 | self.lineWidth = lineWidth; 33 | [self drawTimeLayer]; 34 | } 35 | return self; 36 | } 37 | 38 | - (void)drawTimeLayer 39 | { 40 | [self.klineDataSource enumerateObjectsUsingBlock:^(KLineModel *model, NSUInteger idx, BOOL * _Nonnull stop) { 41 | 42 | if (model.isDrawDate) 43 | { 44 | CATextLayer *layer = [self getTextLayer]; 45 | layer.string = model.timeStr; 46 | if (model.highestPoint.x <= 0.00001f) 47 | { 48 | layer.frame = CGRectMake(0, self.height - timeheight, 80, timeheight); 49 | 50 | } 51 | 52 | else 53 | { 54 | layer.position = CGPointMake(model.highestPoint.x + self.candleWidth, self.height - timeheight/2 - self.bottomMargin); 55 | layer.bounds = CGRectMake(0, 0, 80, timeheight); 56 | 57 | } 58 | [self addSublayer:layer]; 59 | 60 | CAShapeLayer *lineLayer = [self getAxispLayer]; 61 | UIBezierPath *path = [UIBezierPath bezierPath]; 62 | path.lineWidth = self.lineWidth; 63 | lineLayer.lineWidth = self.lineWidth; 64 | 65 | [path moveToPoint:CGPointMake(model.highestPoint.x + self.candleWidth/2 - self.lineWidth/2, 1*heightradio)]; 66 | [path addLineToPoint:CGPointMake(model.highestPoint.x + self.candleWidth/2 - self.lineWidth/2 ,self.height - timeheight- self.bottomMargin)]; 67 | lineLayer.path = path.CGPath; 68 | lineLayer.lineDashPattern = @[@6,@10]; 69 | [self addSublayer:lineLayer]; 70 | } 71 | }]; 72 | } 73 | 74 | - (CATextLayer*)getTextLayer 75 | { 76 | CATextLayer *layer = [CATextLayer layer]; 77 | layer.contentsScale = [UIScreen mainScreen].scale; 78 | layer.fontSize = 12.f; 79 | layer.alignmentMode = kCAAlignmentLeft; 80 | layer.foregroundColor = 81 | [UIColor grayColor].CGColor; 82 | return layer; 83 | } 84 | 85 | - (CAShapeLayer*)getAxispLayer 86 | { 87 | CAShapeLayer *layer = [CAShapeLayer layer]; 88 | layer.strokeColor = [UIColor colorWithHexString:@"#ededed"].CGColor; 89 | layer.fillColor = [[UIColor clearColor] CGColor]; 90 | layer.contentsScale = [UIScreen mainScreen].scale; 91 | return layer; 92 | } 93 | 94 | @end 95 | -------------------------------------------------------------------------------- /AbuKlineView.xcodeproj/xcuserdata/haoji.xcuserdatad/xcschemes/AbuKlineView.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 54 | 56 | 62 | 63 | 64 | 65 | 66 | 67 | 73 | 75 | 81 | 82 | 83 | 84 | 86 | 87 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /AbuKlineView.xcodeproj/xcuserdata/jefferson.xcuserdatad/xcschemes/AbuKlineView.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 54 | 56 | 62 | 63 | 64 | 65 | 66 | 67 | 73 | 75 | 81 | 82 | 83 | 84 | 86 | 87 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /AbuKlineView.xcodeproj/xcuserdata/jefferson.zhang.xcuserdatad/xcschemes/AbuKlineView.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 54 | 56 | 62 | 63 | 64 | 65 | 66 | 67 | 73 | 75 | 81 | 82 | 83 | 84 | 86 | 87 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /AbuKlineView/KlineView/KLineView/KlineCommonView/KlineCurrentPriceView.m: -------------------------------------------------------------------------------- 1 | // 2 | // KlineCurrentPriceView.m 3 | // ABuKLineChartView 4 | // 5 | // Created by jefferson on 2018/9/14. 6 | // Copyright © 2018年 jefferson. All rights reserved. 7 | // 8 | 9 | #import "KlineCurrentPriceView.h" 10 | #define CoordinateDisPlayLabelColor [UIColor clearColor] 11 | @interface KlineCurrentPriceView() 12 | @property (nonatomic,strong) UIView * currentPriceLine; 13 | @property (nonatomic,strong) UILabel * priceLabel; 14 | @property (nonatomic,assign) BOOL update; 15 | @property (nonatomic,strong) CAShapeLayer * horizontalLineLayer; 16 | @end 17 | 18 | @implementation KlineCurrentPriceView 19 | 20 | - (instancetype)initWithUpdate:(BOOL)update 21 | { 22 | self = [super init]; 23 | if (self) { 24 | self.update = update; 25 | [self buildUI]; 26 | [self addConstrains]; 27 | } 28 | return self; 29 | } 30 | 31 | - (void)buildUI 32 | { 33 | 34 | self.currentPriceLine = [[UIView alloc] init]; 35 | if (self.update) { 36 | self.currentPriceLine.backgroundColor = [UIColor clearColor]; 37 | [self.currentPriceLine.layer addSublayer:[self creatLayerWithColor:CoordinateDisPlayLabelColor]]; 38 | }else{ 39 | self.currentPriceLine.backgroundColor = CoordinateDisPlayLabelColor; 40 | } 41 | [self addSubview:self.currentPriceLine]; 42 | self.priceLabel = [[UILabel alloc] init]; 43 | self.priceLabel.lineBreakMode = NSLineBreakByWordWrapping; 44 | self.priceLabel.backgroundColor = CoordinateDisPlayLabelColor; 45 | self.priceLabel.text = @""; 46 | self.priceLabel.textColor = [UIColor whiteColor]; 47 | self.priceLabel.textAlignment = NSTextAlignmentCenter; 48 | self.priceLabel.font = [UIFont systemFontOfSize:9]; 49 | [self addSubview:self.priceLabel]; 50 | 51 | } 52 | 53 | - (CAShapeLayer *)creatLayerWithColor:(UIColor *)color 54 | { 55 | [self.horizontalLineLayer removeFromSuperlayer]; 56 | self.horizontalLineLayer = nil; 57 | self.horizontalLineLayer = [CAShapeLayer layer]; 58 | UIBezierPath *topLine = [UIBezierPath bezierPath]; 59 | [topLine moveToPoint:CGPointMake(0,0)]; 60 | if (!Portrait) { 61 | [topLine addLineToPoint:CGPointMake(KSCREEN_HEIGHT - priceWidth,0)]; 62 | }else{ 63 | [topLine addLineToPoint:CGPointMake(KSCREEN_WIDTH - priceWidth,0)]; 64 | } 65 | self.horizontalLineLayer.lineWidth = 1; 66 | self.horizontalLineLayer.lineDashPattern = @[@4, @4]; 67 | self.horizontalLineLayer.path = topLine.CGPath; 68 | self.horizontalLineLayer.strokeColor = color.CGColor; 69 | return self.horizontalLineLayer; 70 | } 71 | 72 | - (void)addConstrains 73 | { 74 | [self.currentPriceLine mas_makeConstraints:^(MASConstraintMaker *make) { 75 | make.right.mas_equalTo(self).offset(-priceWidth); 76 | make.left.mas_equalTo(self); 77 | make.height.equalTo(@0.5); 78 | make.centerY.mas_equalTo(self); 79 | 80 | }]; 81 | [self.priceLabel mas_makeConstraints:^(MASConstraintMaker *make) { 82 | make.left.mas_equalTo(self.currentPriceLine.mas_right); 83 | make.right.mas_equalTo(self.mas_right); 84 | make.height.mas_equalTo(14); 85 | make.centerY.mas_equalTo(self); 86 | }]; 87 | } 88 | 89 | - (void)updateNewPrice:(NSString *)newPrice backgroundColor:(UIColor *)color precision:(int)precision 90 | { 91 | 92 | NSString *priceStrr = [NSString stringWithFormat:@"%.*f",precision,[newPrice doubleValue]]; 93 | 94 | self.priceLabel.text = priceStrr; 95 | if (color) { 96 | 97 | self.priceLabel.backgroundColor = color; 98 | if (self.update) 99 | self.currentPriceLine.backgroundColor = [UIColor clearColor]; 100 | [self.currentPriceLine.layer addSublayer:[self creatLayerWithColor:color]]; 101 | } 102 | } 103 | 104 | @end 105 | -------------------------------------------------------------------------------- /AbuKlineView/KlineView/KLineView/KLineLayer/KlineCandelLayer.m: -------------------------------------------------------------------------------- 1 | // 2 | // KlineCandelLayer.m 3 | // ABuKLineChartView 4 | // 5 | // Created by jefferson on 2018/9/7. 6 | // Copyright © 2018年 jefferson. All rights reserved. 7 | // 8 | 9 | #import "KlineCandelLayer.h" 10 | 11 | @interface KlineCandelLayer() 12 | 13 | @property (nonatomic, strong) NSArray * klineDataSource; 14 | @property (nonatomic,assign) double heightPerpoint; 15 | @property (nonatomic,assign) CGFloat candleWidth; 16 | @property (nonatomic,assign) CGFloat candleSpace; 17 | @property (nonatomic,assign) CGFloat startIndex; 18 | @property (nonatomic,assign) double maxValue; 19 | @property (nonatomic,assign) double contentWidth; 20 | @end 21 | @implementation KlineCandelLayer 22 | - (instancetype)initQuotaDataArr:(NSArray *)dataArr candleWidth:(CGFloat)candleWidth candleSpace:(CGFloat)candleSpace startIndex:(CGFloat)startIndex maxValue:(CGFloat)maxValue heightPerpoint:(CGFloat)heightPerpoint scrollViewContentWidth:(CGFloat)contentWidth 23 | { 24 | self = [super init]; 25 | if (self) { 26 | self.klineDataSource = dataArr; 27 | self.candleWidth = candleWidth; 28 | self.candleSpace = candleSpace; 29 | self.startIndex = startIndex; 30 | self.maxValue = maxValue; 31 | self.heightPerpoint = heightPerpoint; 32 | self.contentWidth = contentWidth; 33 | [self convertToKlinePositionDataArr:self.klineDataSource]; 34 | [self drawCandleSublayers]; 35 | } 36 | return self; 37 | } 38 | 39 | - ( void)convertToKlinePositionDataArr:(NSArray *)dataArr 40 | { 41 | for (NSInteger i = 0 ; i < dataArr.count; i++) 42 | { 43 | KLineModel *model = [dataArr objectAtIndex:i]; 44 | CGFloat open = ((self.maxValue - model.openPrice) * self.heightPerpoint); 45 | CGFloat close = ((self.maxValue - model.closePrice) * self.heightPerpoint); 46 | CGFloat high = ((self.maxValue - model.highPrice) * self.heightPerpoint); 47 | CGFloat low = ((self.maxValue - model.lowPrice) * self.heightPerpoint); 48 | CGFloat left = self.startIndex + ((self.candleWidth + self.candleSpace) * i); 49 | 50 | if (left >= self.contentWidth) 51 | { 52 | left = self.contentWidth - self.candleWidth/2.f; 53 | } 54 | model.opensPoint = CGPointMake(left, open); 55 | model.closesPoint = CGPointMake(left, close); 56 | model.highestPoint = CGPointMake(left, high); 57 | model.lowestPoint = CGPointMake(left, low); 58 | } 59 | } 60 | 61 | - (CAShapeLayer*)getShaperLayer:(KLineModel*)postion 62 | { 63 | CGFloat openPrice = postion.opensPoint.y + topDistance; 64 | CGFloat closePrice = postion.closesPoint.y + topDistance; 65 | CGFloat hightPrice = postion.highestPoint.y + topDistance; 66 | CGFloat lowPrice = postion.lowestPoint.y + topDistance; 67 | CGFloat x = postion.opensPoint.x; 68 | UIBezierPath *path = [UIBezierPath drawKLine:openPrice close:closePrice high:hightPrice low:lowPrice candleWidth:self.candleWidth xPostion:x lineWidth:self.lineWidth]; 69 | 70 | CAShapeLayer *subLayer = [CAShapeLayer layer]; 71 | subLayer.fillColor = [UIColor clearColor].CGColor; 72 | if (postion.opensPoint.y >= postion.closesPoint.y) 73 | { 74 | subLayer.strokeColor = RISECOLOR.CGColor; 75 | // subLayer.fillColor = RISECOLOR.CGColor; 76 | } 77 | else 78 | { 79 | subLayer.strokeColor = DROPCOLOR.CGColor; 80 | // subLayer.fillColor = DROPCOLOR.CGColor; 81 | } 82 | subLayer.path = path.CGPath; 83 | [path removeAllPoints]; 84 | return subLayer; 85 | } 86 | 87 | - (void)drawCandleSublayers 88 | { 89 | WS(weakSelf); 90 | [self.klineDataSource enumerateObjectsUsingBlock:^(KLineModel *obj, NSUInteger idx, BOOL * _Nonnull stop) { 91 | CAShapeLayer *subLayer = [weakSelf getShaperLayer:obj]; 92 | [weakSelf addSublayer:subLayer]; 93 | }]; 94 | } 95 | 96 | @end 97 | -------------------------------------------------------------------------------- /AbuKlineView/KlineView/KLineView/KLineLayer/KlineQuotaColumnLayer.m: -------------------------------------------------------------------------------- 1 | // 2 | // KlineQuotaColumnLayer.m 3 | // ABuKLineChartView 4 | // 5 | // Created by jefferson on 2018/9/10. 6 | // Copyright © 2018年 jefferson. All rights reserved. 7 | // 8 | 9 | #import "KlineQuotaColumnLayer.h" 10 | 11 | static inline bool macdIsEqualZero(float value) 12 | { 13 | return fabsf(value) <= 0.00001f; 14 | } 15 | 16 | static NSString *const macdStartX = @"macdStartX"; 17 | static NSString *const macdStartY = @"macdStartY"; 18 | static NSString *const macdEndX = @"macdEndX"; 19 | static NSString *const macdEndY = @"macdEndY"; 20 | 21 | @interface KlineQuotaColumnLayer() 22 | 23 | @property (nonatomic, strong) NSArray * klineDataSource; 24 | @property (nonatomic,assign) double heightPerpoint; 25 | @property (nonatomic,assign) CGFloat candleWidth; 26 | @property (nonatomic,assign) CGFloat candleSpace; 27 | @property (nonatomic,assign) CGFloat startIndex; 28 | @property (nonatomic,assign) CGFloat maxValue; 29 | @property (nonatomic,assign) CGFloat qutoaHeight; 30 | @end 31 | 32 | @implementation KlineQuotaColumnLayer 33 | 34 | - (instancetype)initQuotaDataArr:(NSArray *)dataArr candleWidth:(CGFloat)candleWidth candleSpace:(CGFloat)candleSpace startIndex:(CGFloat)startIndex maxValue:(CGFloat)maxValue heightPerpoint:(CGFloat)heightPerpoint qutoaHeight:(CGFloat)qutoaHeight 35 | { 36 | self = [super init]; 37 | if (self) { 38 | self.klineDataSource = dataArr; 39 | self.heightPerpoint = heightPerpoint; 40 | self.candleWidth = candleWidth; 41 | self.candleSpace = candleSpace; 42 | self.maxValue = maxValue; 43 | self.startIndex = startIndex; 44 | self.qutoaHeight = qutoaHeight; 45 | [self drawMacdLayer:[self coverMacdPostions]]; 46 | } 47 | return self; 48 | } 49 | 50 | - (NSMutableArray *)coverMacdPostions 51 | { 52 | NSMutableArray * postions = [NSMutableArray array]; 53 | for (int i = 0; i < self.klineDataSource.count; ++i) { 54 | NSMutableDictionary *dictionary = [NSMutableDictionary dictionary]; 55 | KLineModel *model = [self.klineDataSource objectAtIndex:i]; 56 | CGFloat xPosition = self.startIndex + ((self.candleSpace+self.candleWidth) * i); 57 | CGFloat yPosition = ABS((self.maxValue - [model.MACD floatValue])/self.heightPerpoint); 58 | dictionary[macdEndX] = @(xPosition); 59 | dictionary[macdEndY] = @(yPosition); 60 | CGPoint strartPoint = CGPointMake(xPosition,self.maxValue/self.heightPerpoint); 61 | dictionary[macdStartX] = @(strartPoint.x); 62 | dictionary[macdStartY] = @(strartPoint.y); 63 | float x = strartPoint.y - yPosition; 64 | if (macdIsEqualZero(x)) 65 | { 66 | dictionary[macdEndY] = @(self.maxValue/self.heightPerpoint+1); 67 | } 68 | [postions addObject:dictionary]; 69 | } 70 | return postions; 71 | } 72 | 73 | - (void)drawMacdLayer:(NSMutableArray *)postions 74 | { 75 | [postions enumerateObjectsUsingBlock:^(NSDictionary* dic, NSUInteger idx, BOOL * _Nonnull stop) { 76 | CGFloat strartX = [dic[macdStartX] floatValue]; 77 | CGFloat strartY = [dic[macdStartY] floatValue]; 78 | CGFloat endY = [dic[macdEndY] floatValue]; 79 | CGRect rect = CGRectMake(strartX, strartY + self.qutoaHeight + midDistance, self.candleWidth, endY- strartY); 80 | UIBezierPath *path = [UIBezierPath bezierPathWithRect:rect]; 81 | CAShapeLayer * subLayer = [CAShapeLayer layer]; 82 | subLayer.path = path.CGPath; 83 | 84 | if (endY > strartY) 85 | { 86 | subLayer.strokeColor = DROPCOLOR.CGColor; 87 | 88 | subLayer.fillColor = DROPCOLOR.CGColor; 89 | } 90 | else 91 | { 92 | subLayer.strokeColor = RISECOLOR.CGColor; 93 | subLayer.fillColor = RISECOLOR.CGColor; 94 | } 95 | [self addSublayer:subLayer]; 96 | }]; 97 | } 98 | 99 | @end 100 | -------------------------------------------------------------------------------- /AbuKlineView/KlineView/KLineView/KLineCaculate/KLineCalculate.m: -------------------------------------------------------------------------------- 1 | // 2 | // KLineCalculate.m 3 | // ABuKLineChartView 4 | // 5 | // Created by jefferson on 2018/7/19. 6 | // Copyright © 2018年 jefferson. All rights reserved. 7 | // 8 | 9 | #import "KLineCalculate.h" 10 | 11 | @implementation KLineCalculate 12 | 13 | static id _instance; 14 | 15 | + (instancetype)sharedInstance 16 | { 17 | static dispatch_once_t onceToken; 18 | dispatch_once(&onceToken, ^{ 19 | _instance = [[self alloc] init]; 20 | }); 21 | return _instance; 22 | } 23 | - (NSDictionary *)calculateMaxAndMinValueWithDataArr:(NSArray *)dataArr 24 | { 25 | NSMutableArray *calculateArr = [NSMutableArray array]; 26 | for (NSObject *item in dataArr) { 27 | if ([item isKindOfClass:[NSArray class]]) { 28 | 29 | [calculateArr addObjectsFromArray:(NSArray *)item]; 30 | }else{ 31 | 32 | [calculateArr addObject:item]; 33 | } 34 | } 35 | __block double maxValue = 0; 36 | __block double minValue = 0; 37 | NSMutableDictionary *resultDic = [NSMutableDictionary dictionary]; 38 | [calculateArr enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { 39 | 40 | double value = [obj doubleValue]; 41 | if (idx==0) { 42 | 43 | maxValue = value; 44 | minValue = value; 45 | }else{ 46 | 47 | if (value>maxValue) { 48 | maxValue = value; 49 | }else if (value 15 | #import 16 | #import 17 | 18 | #import "KlineTitleView.h" 19 | #import "AbuCharViewProtocol.h" 20 | #import "AbuKlineView.h" 21 | #import "KLineModel.h" 22 | #import "KLineCalculate.h" 23 | #import "KLineSubCalculate.h" 24 | #import "KLineSocketDataRefersh.h" 25 | #import "UIColor+Extension.h" 26 | #import "UIView+Extension.h" 27 | #import "UIBezierPath+Extension.h" 28 | #define ContentOffSet @"contentOffset" 29 | 30 | #define IsDictionaryNull(dict) (nil == dict || ![dict isKindOfClass:[NSDictionary class]]\ 31 | || [dict isKindOfClass:[NSNull class]] || [dict allKeys].count <= 0) 32 | #define IsArrayNull(array) ((nil == array || ![array isKindOfClass:[NSArray class]]\ 33 | || [array isKindOfClass:[NSNull class]] || array.count <= 0)) 34 | #define IsStringNull(string) (nil == string || [string isKindOfClass:[NSNull class]] \ 35 | || string.length <= 0) 36 | #define IsObjectNull(object) (nil == object || [object isKindOfClass:[NSNull class]]) 37 | 38 | #define WS(weakSelf) __weak __typeof(&*self)weakSelf = self; 39 | 40 | #define SCREENHEIGHT [UIScreen mainScreen].bounds.size.height 41 | #define SCREENWIDTH [UIScreen mainScreen].bounds.size.width 42 | 43 | #define widthradio SCREENWIDTH/375 44 | #define heightradio SCREENHEIGHT/667 45 | 46 | #define midDistance 40 47 | #define topDistance 10 48 | #define leftDistance 10 49 | #define rightDistance 10 50 | #define bottomDistance 10 51 | 52 | #define timeheight 15 53 | 54 | #define MinCount 10 55 | #define MaxCount 30 56 | 57 | #define KlineCount 88 58 | 59 | #define maxCandelWith 20 60 | 61 | #define minCandelWith 4 62 | 63 | #define ChartViewHigh 400 64 | 65 | #define LandscapeChartViewHigh 300 66 | 67 | #define orignScale 1.0 68 | #define orignChartScale 3.0f/2.0f 69 | #define orignIndicatorScale 1.0f / 3.0f 70 | #define orignIndicatorOrignY 2.0f * orignIndicatorScale 71 | 72 | #define priceWidth 60 73 | 74 | #define RISECOLOR [UIColor colorWithHexString:@"#fb463e"] 75 | #define DROPCOLOR [UIColor colorWithHexString:@"#30b840"] 76 | #define Orientation [[UIApplication sharedApplication] statusBarOrientation] 77 | #define Portrait (Orientation==UIDeviceOrientationPortrait||Orientation==UIDeviceOrientationPortraitUpsideDown) 78 | #define LandscapeLeft (Orientation == UIDeviceOrientationLandscapeLeft) 79 | #define TotalWidth (Portrait ? (PortraitTotalWidth) : (LanscapeTotalWidth)) 80 | 81 | #define PortraitTotalWidth KSCREEN_WIDTH 82 | #define LanscapeTotalWidth KSCREEN_HEIGHT 83 | #define KSCREEN_WIDTH MIN([UIScreen mainScreen].bounds.size.width,[UIScreen mainScreen].bounds.size.height) 84 | #define KSCREEN_HEIGHT MAX([UIScreen mainScreen].bounds.size.width,[UIScreen mainScreen].bounds.size.height) 85 | 86 | typedef enum 87 | { 88 | MACD = 1, 89 | KDJ, 90 | WR 91 | }DataLineType; 92 | 93 | typedef NS_ENUM(NSUInteger, ColumnWidthType) { 94 | ColumnWidthTypeEqualCandle = 0, 95 | ColumnWidthTypeEqualLine, 96 | }; 97 | 98 | typedef NS_ENUM(NSUInteger, KlineWireSubOrMain) { 99 | Main = 0, 100 | Sub, 101 | }; 102 | 103 | typedef NS_ENUM(NSUInteger, WIRETYPE) { 104 | MA5TYPE = 0, 105 | MA10TYPE, 106 | MA20TYPE, 107 | MACDDIFF, 108 | MACDDEA, 109 | KDJ_K, 110 | KDJ_D, 111 | KDJ_J, 112 | KLINE_WR, 113 | }; 114 | 115 | typedef NS_ENUM(NSInteger, KlineSubKPIType) { 116 | KlineSubKPITypeATR = 0, 117 | KlineSubKPITypeBIAS = 1, 118 | KlineSubKPITypeCCI = 2, 119 | KlineSubKPITypeKD = 3, 120 | KlineSubKPITypeMACD = 4, 121 | KlineSubKPITypeRSI = 5, 122 | KlineSubKPITypeW_R = 6, 123 | KlineSubKPITypeARBR = 7, 124 | KlineSubKPITypeDKBY = 8, 125 | KlineSubKPITypeKDJ = 9, 126 | KlineSubKPITypeLW_R = 10, 127 | KlineSubKPITypeQHLSR = 11, 128 | KlineSubKPITypeKD5 = 12, 129 | KlineSubKPITypeMACD24 = 13, 130 | }; 131 | 132 | #endif 133 | 134 | #endif /* PrefixHeader_h */ 135 | -------------------------------------------------------------------------------- /AbuKlineView/Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix.pch 3 | // AbuKlineView 4 | // 5 | // Created by Jefferson.zhang on 2017/9/6. 6 | // Copyright © 2017年 阿布. All rights reserved. 7 | // 8 | 9 | #ifndef Prefix_h 10 | #define Prefix_h 11 | 12 | #ifdef __OBJC__ 13 | 14 | #import 15 | #import 16 | 17 | #import "KlineTitleView.h" 18 | #import "AbuCharViewProtocol.h" 19 | #import "AbuKlineView.h" 20 | 21 | 22 | /** ********************数据模型******************** */ 23 | #import "KLineModel.h" 24 | 25 | 26 | 27 | /** ********************单例******************** */ 28 | #import "KLineCalculate.h" 29 | #import "KLineSubCalculate.h" 30 | 31 | /** ********************扩展类******************** */ 32 | #import "UIColor+Extension.h" 33 | #import "UIView+Extension.h" 34 | #import "UIBezierPath+Extension.h" 35 | 36 | 37 | /** ********************宏定义******************** */ 38 | 39 | #define ContentOffSet @"contentOffset" 40 | 41 | 42 | #define IsDictionaryNull(dict) (nil == dict || ![dict isKindOfClass:[NSDictionary class]]\ 43 | || [dict isKindOfClass:[NSNull class]] || [dict allKeys].count <= 0) 44 | #define IsArrayNull(array) ((nil == array || ![array isKindOfClass:[NSArray class]]\ 45 | || [array isKindOfClass:[NSNull class]] || array.count <= 0)) 46 | #define IsStringNull(string) (nil == string || [string isKindOfClass:[NSNull class]] \ 47 | || string.length <= 0) 48 | #define IsObjectNull(object) (nil == object || [object isKindOfClass:[NSNull class]]) 49 | 50 | #define WS(weakSelf) __weak __typeof(&*self)weakSelf = self; 51 | 52 | #define SCREENHEIGHT [UIScreen mainScreen].bounds.size.height 53 | #define SCREENWIDTH [UIScreen mainScreen].bounds.size.width 54 | 55 | #define widthradio SCREENWIDTH/375 56 | #define heightradio SCREENHEIGHT/667 57 | 58 | #define midDistance 40 59 | #define topDistance 10 60 | #define leftDistance 10 61 | #define rightDistance 10 62 | #define bottomDistance 10 63 | 64 | #define timeheight 15 65 | 66 | #define MinCount 10 67 | #define MaxCount 30 68 | 69 | #define KlineCount 88 70 | 71 | #define maxCandelWith 20 72 | 73 | #define minCandelWith 4 74 | 75 | #define ChartViewHigh 400 76 | 77 | #define LandscapeChartViewHigh 300 78 | 79 | #define orignScale 1.0 80 | #define orignChartScale 3.0f/2.0f 81 | #define orignIndicatorScale 1.0f / 3.0f 82 | #define orignIndicatorOrignY 2.0f * orignIndicatorScale 83 | 84 | #define priceWidth 60 85 | 86 | #define RISECOLOR [UIColor colorWithHexString:@"#fb463e"] 87 | 88 | #define DROPCOLOR [UIColor colorWithHexString:@"#30b840"] 89 | 90 | #define Orientation [[UIApplication sharedApplication] statusBarOrientation] 91 | 92 | #define Portrait (Orientation==UIDeviceOrientationPortrait||Orientation==UIDeviceOrientationPortraitUpsideDown) 93 | #define LandscapeLeft (Orientation == UIDeviceOrientationLandscapeLeft) 94 | 95 | #define TotalWidth (Portrait ? (PortraitTotalWidth) : (LanscapeTotalWidth)) 96 | 97 | #define PortraitTotalWidth KSCREEN_WIDTH 98 | #define LanscapeTotalWidth KSCREEN_HEIGHT 99 | 100 | #define KSCREEN_WIDTH MIN([UIScreen mainScreen].bounds.size.width,[UIScreen mainScreen].bounds.size.height) 101 | #define KSCREEN_HEIGHT MAX([UIScreen mainScreen].bounds.size.width,[UIScreen mainScreen].bounds.size.height) 102 | 103 | typedef enum 104 | { 105 | MACD = 1, 106 | KDJ, 107 | WR 108 | }DataLineType; 109 | 110 | /** 111 | * 线状柱或者柱状柱 112 | */ 113 | typedef NS_ENUM(NSUInteger, ColumnWidthType) { 114 | ColumnWidthTypeEqualCandle = 0, 115 | ColumnWidthTypeEqualLine, 116 | }; 117 | 118 | /** 119 | * 主图或副图 120 | */ 121 | typedef NS_ENUM(NSUInteger, KlineWireSubOrMain) { 122 | Main = 0, 123 | Sub, 124 | }; 125 | 126 | /** 127 | * 线状类型 128 | */ 129 | typedef NS_ENUM(NSUInteger, WIRETYPE) { 130 | MA5TYPE = 0, 131 | MA10TYPE, 132 | MA20TYPE, 133 | MACDDIFF, 134 | MACDDEA, 135 | KDJ_K, 136 | KDJ_D, 137 | KDJ_J, 138 | KLINE_WR, 139 | }; 140 | 141 | typedef NS_ENUM(NSInteger, KlineSubKPIType) { 142 | KlineSubKPITypeATR = 0, 143 | KlineSubKPITypeBIAS = 1, 144 | KlineSubKPITypeCCI = 2, 145 | KlineSubKPITypeKD = 3, 146 | KlineSubKPITypeMACD = 4, 147 | KlineSubKPITypeRSI = 5, 148 | KlineSubKPITypeW_R = 6, 149 | KlineSubKPITypeARBR = 7, 150 | KlineSubKPITypeDKBY = 8, 151 | KlineSubKPITypeKDJ = 9, 152 | KlineSubKPITypeLW_R = 10, ///< _ -> & 153 | KlineSubKPITypeQHLSR = 11, 154 | KlineSubKPITypeKD5 = 12, 155 | KlineSubKPITypeMACD24 = 13, 156 | }; 157 | 158 | #endif 159 | 160 | #endif /* Prefix_h */ 161 | -------------------------------------------------------------------------------- /AbuKlineView/KlineView/KLineView/KLineLayer/KlineMALayer.m: -------------------------------------------------------------------------------- 1 | // 2 | // KlineMALayer.m 3 | // ABuKLineChartView 4 | // 5 | // Created by jefferson on 2018/9/7. 6 | // Copyright © 2018年 jefferson. All rights reserved. 7 | // 8 | 9 | #import "KlineMALayer.h" 10 | 11 | @interface KlineMALayer() 12 | @property (nonatomic, strong) NSArray * klineDataSource; 13 | @property (nonatomic,assign) double heightPerpoint; 14 | @property (nonatomic,assign) CGFloat candleWidth; 15 | @property (nonatomic,assign) CGFloat candleSpace; 16 | @property (nonatomic,assign) CGFloat startIndex; 17 | @property (nonatomic,assign) CGFloat maxValue; 18 | @property (nonatomic,strong) UIColor * color; 19 | @property (nonatomic,assign) CGFloat totalHeight; 20 | @property (nonatomic,assign) double lineWidth; 21 | @property (nonatomic,assign) WIRETYPE wiretype; 22 | @property (nonatomic,assign) KlineWireSubOrMain klineWiretype; 23 | @end 24 | 25 | @implementation KlineMALayer 26 | 27 | - (instancetype)initQuotaDataArr:(NSArray *)dataArr candleWidth:(CGFloat)candleWidth candleSpace:(CGFloat)candleSpace startIndex:(CGFloat)startIndex maxValue:(CGFloat)maxValue heightPerpoint:(CGFloat)heightPerpoint totalHeight:(CGFloat)totalHeight lineWidth:(CGFloat)lineWidth lineColor:(UIColor *)lineColor wireType:(WIRETYPE)wiretype klineSubOrMain:(KlineWireSubOrMain)klineWiretype 28 | { 29 | self = [super init]; 30 | if (self) { 31 | self.klineDataSource = dataArr; 32 | self.candleWidth = candleWidth; 33 | self.candleSpace = candleSpace; 34 | self.startIndex = startIndex; 35 | self.maxValue = maxValue; 36 | self.heightPerpoint = heightPerpoint; 37 | self.totalHeight = totalHeight; 38 | self.lineWidth = lineWidth; 39 | self.color = lineColor; 40 | self.wiretype = wiretype; 41 | self.klineWiretype = klineWiretype; 42 | [self drawWireLine:[self coverPostionModel]]; 43 | } 44 | return self; 45 | } 46 | 47 | - (NSMutableArray *)coverPostionModel 48 | { 49 | NSMutableArray * postions = [NSMutableArray array]; 50 | for (int i = 0; i< self.klineDataSource.count; i++) { 51 | KLineModel *model = self.klineDataSource[i]; 52 | NSMutableDictionary *dictionary = [NSMutableDictionary dictionary]; 53 | CGPoint postionPoint; 54 | NSNumber * value = @0.0; 55 | CGFloat yPosition = 0.0; 56 | if (self.wiretype == MA5TYPE) { 57 | value = model.MA5; 58 | } 59 | if (self.wiretype == MA10TYPE) 60 | { 61 | value = model.MA10; 62 | } 63 | if (self.wiretype == MA20TYPE) 64 | { 65 | value = model.MA20; 66 | } 67 | if (self.wiretype == MACDDIFF) { 68 | value = model.DIF; 69 | } 70 | if (self.wiretype == MACDDEA) { 71 | value = model.DEA; 72 | } 73 | if (self.wiretype == KDJ_K) { 74 | if (model.K > 0) { 75 | value = @(model.K); 76 | } 77 | } 78 | if (self.wiretype == KDJ_D) { 79 | if (model.D > 0) { 80 | value = @(model.D); 81 | } 82 | } 83 | if (self.wiretype == KLINE_WR) { 84 | value = @(model.WR); 85 | } 86 | if (!IsObjectNull(value)) { 87 | CGFloat xPosition = self.startIndex + ((self.candleWidth + self.candleSpace) * i) + self.candleWidth/2; 88 | if (self.klineWiretype == Main) { 89 | yPosition = ((self.maxValue - [value floatValue]) * self.heightPerpoint) + topDistance; 90 | } 91 | else if (self.klineWiretype == Sub) 92 | { 93 | yPosition = ABS((self.maxValue - [value floatValue])/self.heightPerpoint) + self.totalHeight + midDistance; 94 | } 95 | 96 | postionPoint = CGPointMake(xPosition, yPosition); 97 | [dictionary setValue:[NSString stringWithFormat:@"%f",postionPoint.x] forKey:@"x"]; 98 | [dictionary setValue:[NSString stringWithFormat:@"%f",postionPoint.y] forKey:@"y"]; 99 | [postions addObject:dictionary]; 100 | } 101 | } 102 | return postions; 103 | } 104 | 105 | - (void)drawWireLine:(NSMutableArray *)postions 106 | { 107 | UIBezierPath * path = [UIBezierPath drawWireLine:postions]; 108 | self.path = path.CGPath; 109 | struct CGColor *strokeColor = nil; 110 | strokeColor = self.color.CGColor; 111 | self.strokeColor = strokeColor; 112 | self.fillColor = [UIColor clearColor].CGColor; 113 | } 114 | 115 | @end 116 | -------------------------------------------------------------------------------- /AbuKlineView/KlineView/KLineView/KlineCommonView/KlineVerticalView.m: -------------------------------------------------------------------------------- 1 | // 2 | // KlineVerticalView.m 3 | // ABuKLineChartView 4 | // 5 | // Created by jefferson on 2018/9/14. 6 | // Copyright © 2018年 jefferson. All rights reserved. 7 | // 8 | 9 | #import "KlineVerticalView.h" 10 | 11 | #define timeLableheight 20 12 | 13 | @interface KlineVerticalView() 14 | 15 | @property (nonatomic,assign) UIInterfaceOrientation orientation; 16 | 17 | @property (nonatomic,strong) UILabel * timeLabel; 18 | 19 | @property (nonatomic,strong) CAShapeLayer * vereticalLineLayer; 20 | 21 | @end 22 | 23 | @implementation KlineVerticalView 24 | 25 | - (instancetype)init 26 | { 27 | self = [super init]; 28 | if (self) { 29 | [self buildUI]; 30 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(statusBarOrientationChange:) name:UIApplicationDidChangeStatusBarOrientationNotification object:nil]; 31 | } 32 | return self; 33 | } 34 | 35 | - (void)buildUI 36 | { 37 | [self buildVerticalLineWithCandleHeight:self.candleChartHeight]; 38 | [self addSubview:self.timeLabel]; 39 | [self.timeLabel mas_makeConstraints:^(MASConstraintMaker *make) { 40 | make.top.mas_equalTo(self).offset(self.candleChartHeight+(timeLableheight-14)/2); 41 | make.height.mas_equalTo(14); 42 | make.centerX.mas_equalTo(self); 43 | }]; 44 | self.timeLabel.text = @"00:00"; 45 | } 46 | 47 | - (void)buildVerticalLineWithCandleHeight:(CGFloat)candleChartHeight 48 | { 49 | [self.vereticalLineLayer removeFromSuperlayer]; 50 | self.vereticalLineLayer = nil; 51 | self.vereticalLineLayer = [CAShapeLayer layer]; 52 | UIBezierPath *topLine = [UIBezierPath bezierPath]; 53 | [topLine moveToPoint:CGPointMake(0,0)]; 54 | [topLine addLineToPoint:CGPointMake(0,candleChartHeight)]; 55 | self.vereticalLineLayer.lineWidth = 1; 56 | self.vereticalLineLayer.path = topLine.CGPath; 57 | self.vereticalLineLayer.lineDashPattern = @[@4, @4]; 58 | self.vereticalLineLayer.strokeColor = [UIColor orangeColor].CGColor; 59 | [self.layer addSublayer:self.vereticalLineLayer]; 60 | } 61 | 62 | #pragma mark - PublicMethods 63 | - (void)updateTimeString:(NSString *)timeString 64 | { 65 | NSLog(@"%@",timeString); 66 | self.timeLabel.text = timeString; 67 | } 68 | 69 | - (void)updateTimeLeft:(CGFloat)leftdistance 70 | { 71 | [self.timeLabel mas_updateConstraints:^(MASConstraintMaker *make) { 72 | make.centerX.equalTo(self).offset(leftdistance); 73 | }]; 74 | } 75 | 76 | - (void)updateHeight:(CGFloat)candleChartHeight 77 | { 78 | [self.timeLabel mas_updateConstraints:^(MASConstraintMaker *make) { 79 | 80 | make.top.mas_equalTo(self).offset(candleChartHeight+(timeLableheight-14)/2); 81 | }]; 82 | [self buildVerticalLineWithCandleHeight:candleChartHeight]; 83 | } 84 | - (void)statusBarOrientationChange:(NSNotification *)notification 85 | { 86 | 87 | self.orientation = [[UIApplication sharedApplication] statusBarOrientation]; 88 | 89 | if (self.orientation == UIDeviceOrientationPortrait || self.orientation == UIDeviceOrientationPortraitUpsideDown) { 90 | 91 | [self.timeLabel mas_updateConstraints:^(MASConstraintMaker *make) { 92 | 93 | make.top.mas_equalTo(self).offset(self.candleChartHeight+(timeLableheight-14)/2); 94 | }]; 95 | [self buildVerticalLineWithCandleHeight:self.candleChartHeight]; 96 | } 97 | if (self.orientation==UIDeviceOrientationLandscapeLeft || self.orientation == UIDeviceOrientationLandscapeRight) { 98 | 99 | [self.timeLabel mas_updateConstraints:^(MASConstraintMaker *make) { 100 | 101 | make.top.mas_equalTo(self).offset(self.candleChartHeight+(timeLableheight-14)/2); 102 | }]; 103 | [self buildVerticalLineWithCandleHeight:self.candleChartHeight]; 104 | } 105 | } 106 | 107 | - (UILabel *)timeLabel 108 | { 109 | if (!_timeLabel) { 110 | _timeLabel = [[UILabel alloc] init]; 111 | _timeLabel.font = [UIFont systemFontOfSize:9]; 112 | _timeLabel.textColor = [UIColor whiteColor]; 113 | _timeLabel.backgroundColor = [UIColor colorWithHexString:@"#608fd8"]; 114 | } 115 | return _timeLabel; 116 | } 117 | - (UIInterfaceOrientation)orientation 118 | { 119 | return [[UIApplication sharedApplication] statusBarOrientation]; 120 | } 121 | - (CGFloat)candleChartHeight 122 | { 123 | CGFloat HEIGH = 0; 124 | if (self.orientation == UIDeviceOrientationPortrait || self.orientation == UIDeviceOrientationPortraitUpsideDown) { 125 | 126 | HEIGH = ChartViewHigh - 20; 127 | } 128 | if (self.orientation==UIDeviceOrientationLandscapeLeft || self.orientation == UIDeviceOrientationLandscapeRight) { 129 | 130 | HEIGH = LandscapeChartViewHigh - 20; 131 | } 132 | return HEIGH; 133 | } 134 | 135 | -(void)dealloc 136 | { 137 | [[NSNotificationCenter defaultCenter] removeObserver:self]; 138 | } 139 | 140 | @end 141 | -------------------------------------------------------------------------------- /AbuKlineView/KlineView/KLineView/KLineTitleView/KlineTitleView.m: -------------------------------------------------------------------------------- 1 | // 2 | // KlineTitleView.m 3 | // LGTS2 4 | // 5 | // Created by Jefferson.zhang on 2017/7/18. 6 | // Copyright © 2017年 Mata. All rights reserved. 7 | // 8 | 9 | #import "KlineTitleView.h" 10 | 11 | #define kHeight [UIScreen mainScreen].bounds.size.height 12 | #define lineWidth 30.0f 13 | #define lineHight 3.0f 14 | 15 | @interface KlineTitleView() 16 | @property (nonatomic, strong) UIView * line; 17 | 18 | @property (nonatomic, strong) UIView * titleView; 19 | 20 | @property (nonatomic, assign) NSInteger selectIndex; 21 | 22 | @property (nonatomic, strong) NSArray * titleArray; 23 | 24 | @property (nonatomic, strong) NSArray * typeArray; 25 | 26 | @property (nonatomic, assign) NSInteger count; 27 | 28 | @end 29 | 30 | 31 | @implementation KlineTitleView 32 | 33 | - (instancetype)initWithklineTitleArray:(NSArray *)titleArray typeArray:(NSArray *)typeArray 34 | { 35 | self = [super init]; 36 | if (self) { 37 | self.selectIndex = 0; 38 | self.titleArray = titleArray; 39 | self.typeArray = typeArray; 40 | self.count = typeArray.count; 41 | [self buildTitleView]; 42 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(statusBarOrientationChange:) name:UIApplicationDidChangeStatusBarOrientationNotification object:nil]; 43 | } 44 | return self; 45 | } 46 | 47 | - (void)statusBarOrientationChange:(NSNotification *)notification 48 | { 49 | NSInteger inde = self.selectIndex; 50 | for (UIView * view in self.titleView.subviews) { 51 | if ([view isKindOfClass:[UIButton class]]) { 52 | [view removeFromSuperview]; 53 | } 54 | } 55 | 56 | if (self.orientation == UIDeviceOrientationPortrait || self.orientation == UIDeviceOrientationPortraitUpsideDown) { 57 | self.titleView.frame = CGRectMake(0, 0, TotalWidth, 33); 58 | [self buildTitleButton]; 59 | }else if (self.orientation==UIDeviceOrientationLandscapeLeft || self.orientation == UIDeviceOrientationLandscapeRight) { 60 | self.titleView.frame = CGRectMake(0, 0, TotalWidth, 33); 61 | self.count = self.typeArray.count; 62 | [self buildTitleButton]; 63 | } 64 | 65 | [self changeLineFrame:inde]; 66 | } 67 | 68 | - (void)buildTitleView 69 | { 70 | [self addConstrains]; 71 | [self addSubview:self.titleView]; 72 | [self.titleView addSubview:self.line]; 73 | } 74 | 75 | - (void)addConstrains 76 | { 77 | if (self.orientation == UIDeviceOrientationPortrait || self.orientation == UIDeviceOrientationPortraitUpsideDown) { 78 | self.titleView.frame = CGRectMake(0, 0, TotalWidth, 33); 79 | [self buildTitleButton]; 80 | 81 | 82 | }else if (self.orientation==UIDeviceOrientationLandscapeLeft || self.orientation == UIDeviceOrientationLandscapeRight) { 83 | self.titleView.frame = CGRectMake(0, 0, TotalWidth, 33); 84 | [self buildTitleButton]; 85 | } 86 | } 87 | 88 | - (void)buildTitleButton 89 | { 90 | for (NSInteger i = 0; i < self.typeArray.count; i++) { 91 | UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom]; 92 | button.frame = CGRectMake(TotalWidth / self.count * i, 0, TotalWidth / self.count, 30); 93 | [button setTitle:_titleArray[i] forState:UIControlStateNormal]; 94 | if (i == self.selectIndex) { 95 | button.selected = YES; 96 | } 97 | [button setTitleColor:[UIColor colorWithHexString:@"#608fdb"] forState:UIControlStateSelected]; 98 | [button setTitleColor:[UIColor colorWithHexString:@"#7b8396"] forState:UIControlStateNormal]; 99 | button.titleLabel.font = [UIFont systemFontOfSize:12]; 100 | button.titleLabel.textAlignment = NSTextAlignmentCenter; 101 | button.userInteractionEnabled = YES; 102 | [button addTarget:self action:@selector(buttonAction:) forControlEvents:UIControlEventTouchUpInside]; 103 | button.tag = [self.typeArray[i] integerValue]; 104 | [self.titleView addSubview:button]; 105 | } 106 | } 107 | 108 | - (void)buttonAction:(UIButton *)btn 109 | { 110 | KLINETYPE type = (KLINETYPE)btn.tag; 111 | for (int i = 0; i < self.typeArray.count; i++) { 112 | if (btn.tag == [self.typeArray[i] integerValue]) { 113 | self.selectIndex = i; 114 | } 115 | } 116 | for (UIButton *bt in self.titleView.subviews) { 117 | if ([bt isKindOfClass:[UIButton class]]) { 118 | bt.selected = NO; 119 | if (self.selectIndex 10 | 11 | @interface KLineModel : NSObject 12 | 13 | /** 最高价 */ 14 | @property (assign, nonatomic) CGFloat highPrice; 15 | /** 最低价 */ 16 | @property (assign, nonatomic) CGFloat lowPrice; 17 | /** 开盘价 */ 18 | @property (assign, nonatomic) CGFloat openPrice; 19 | /** 收盘价 */ 20 | @property (assign, nonatomic) CGFloat closePrice; 21 | /** 日期 */ 22 | @property (copy, nonatomic) NSString * date; 23 | /** 成交量 */ 24 | @property (copy, nonatomic) NSString * volumn; 25 | /** 是否需要绘制日期 */ 26 | @property (assign, nonatomic) BOOL isDrawDate; 27 | 28 | @property (nonatomic,assign) double currentPrice; 29 | @property (nonatomic,assign) NSInteger timestamp; 30 | 31 | 32 | @property (nonatomic,assign) BOOL isNew; 33 | 34 | 35 | //model中应该最后处理成坐标数据 36 | @property (nonatomic,copy) NSString *timeStr; 37 | 38 | @property (nonatomic,assign) NSUInteger xPoint; 39 | /** 40 | *蜡烛矩形起点 41 | */ 42 | @property (nonatomic,assign) CGFloat y; 43 | /** 44 | *蜡烛矩形高度 45 | */ 46 | @property (nonatomic,assign) CGFloat h; 47 | /** 48 | *蜡烛开盘点 49 | */ 50 | @property (nonatomic,assign) CGPoint opensPoint; 51 | /** 52 | *蜡烛收盘点 53 | */ 54 | @property (nonatomic,assign) CGPoint closesPoint; 55 | /** 56 | *蜡烛最高点 57 | */ 58 | @property (nonatomic,assign) CGPoint highestPoint; 59 | /** 60 | *蜡烛最低点 61 | */ 62 | @property (nonatomic,assign) CGPoint lowestPoint; 63 | /** 64 | *填充颜色 65 | */ 66 | @property (nonatomic,strong) UIColor *fillColor; 67 | /** 68 | *边线绘制颜色 69 | */ 70 | @property (nonatomic,strong) UIColor *strokeColor; 71 | /** 72 | *判断是否是NSTimer推送的数据 73 | */ 74 | @property (nonatomic,assign) BOOL isFakeData; 75 | /** 76 | *当数据不足以显示一屏的时候的判断处理 77 | */ 78 | @property (nonatomic,assign) BOOL isPlaceHolder; 79 | 80 | 81 | 82 | 83 | //MACD 84 | //这里由于是使用懒加载的,所以必须声明为对象类型才能保存在模型中 85 | //previousKlineModel 86 | @property (nonatomic,strong) KLineModel *previousKlineModel; 87 | @property (nonatomic,strong) NSNumber *EMA12; 88 | @property (nonatomic,strong) NSNumber *EMA26; 89 | @property (nonatomic,strong) NSNumber *DIF; 90 | @property (nonatomic,strong) NSNumber *DEA; 91 | @property (nonatomic,strong) NSNumber *MACD; 92 | - (void)reInitData; 93 | 94 | 95 | 96 | 97 | //KDJ 98 | //KDJ(9,3.3),下面以该参数为例说明计算方法。 99 | //9,3,3代表指标分析周期为9天,K值D值为3天 100 | //RSV(9)=(今日收盘价-9日内最低价)÷(9日内最高价-9日内最低价)×100 101 | //K(3日)=(当日RSV值+2*前一日K值)÷3 102 | //D(3日)=(当日K值+2*前一日D值)÷3 103 | //J=3K-2D 104 | @property (nonatomic,strong) NSNumber *HNinePrice; 105 | @property (nonatomic,strong) NSNumber *LNinePrice; 106 | @property (nonatomic, copy) NSNumber *RSV_9; 107 | @property (nonatomic, copy) NSNumber *KDJ_K; 108 | @property (nonatomic, copy) NSNumber *KDJ_D; 109 | @property (nonatomic, copy) NSNumber *KDJ_J; 110 | - (void)reInitKDJData; 111 | 112 | 113 | //BOLL 114 | /* 115 | 20日BOLL指标的计算过程 116 |   (1)计算MA 117 |   MA=N日内的收盘价之和÷N 118 |   (2)计算标准差MD 119 |   MD=平方根(N日的(C-MA)的两次方之和除以N) 120 |   (3)计算MB、UP、DN线 121 |   MB=(N-1)日的MA 122 |   UP=MB+2×MD 123 |   DN=MB-2×MD 124 |   在股市分析软件中,BOLL指标一共由四条线组成,即上轨线UP 、中轨线MB、下轨线DN和价格线。其中上轨线UP是UP数值的连线,用黄色线表示;中轨线MB是MB数值的连线,用白色线表示;下轨线DN是DN数值的连线,用紫色线表示;价格线是以美国线表示,颜色为浅蓝色。和其他技术指标一样,在实战中,投资者不需要进行BOLL指标的计算,主要是了解BOLL的计算方法和过程,以便更加深入地掌握BOLL指标的实质,为运用指标打下基础。 125 | 126 | */ 127 | @property (nonatomic, copy) NSNumber *BOLL_MA; 128 | @property (nonatomic, copy) NSNumber *BOLL_MD; 129 | @property (nonatomic, copy) NSNumber *BOLL_MB; 130 | @property (nonatomic, copy) NSNumber *BOLL_UP; 131 | @property (nonatomic, copy) NSNumber *BOLL_DN; 132 | 133 | - (void)reInitBOLLData; 134 | 135 | 136 | 137 | 138 | //RSI 139 | /* 140 | RSI的计算公式 141 | RSI=100×RS/(1+RS) 或者,RSI=100-100÷(1+RS) 142 | 其中 RS=14天内收市价上涨数之和的平均值/14天内收市价下跌数之和的平均值 143 | 举例说明: 144 | 如果最近14天涨跌情形是:第一天升2元,第二天跌2元,第三至第五天各升3元;第六天跌4元 第七天升2元,第八天跌5元;第九天跌6元,第十至十二天各升1元;第十三至十四天各跌3元。 145 | 那么,计算RSI的步骤如下: 146 | (一)将14天上升的数目相加,除以14,上例中总共上升16元除以14得1.143(精确到小数点后三位); 147 | (二)将14天下跌的数目相加,除以14,上例中总共下跌23元除以14得1.643(精确到小数点后三位); 148 | (三)求出相对强度RS,即RS=1.143/1.643=0.696(精确到小数点后三位); 149 | (四)1+RS=1+0.696=1.696; 150 | (五)以100除以1+RS,即100/1.696=58.962; 151 | (六)100-58.962=41.038。 结果14天的强弱指标RS1为41.038。 不同日期的14天RSI值当然是不同的,连接不同的点,即成RSI的轨迹。 152 | 153 | */ 154 | 155 | @property (nonatomic, copy) NSNumber *RSI_6; 156 | @property (nonatomic, copy) NSNumber *RSI_12; 157 | @property (nonatomic, copy) NSNumber *RSI_14; 158 | @property (nonatomic, copy) NSNumber *RSI_21; 159 | @property (nonatomic, copy) NSNumber *RSI_24; 160 | @property (nonatomic, assign)double SMAMAX; 161 | @property (nonatomic, assign)double SMAABS; 162 | - (void)judgeRSIIsNan; 163 | 164 | //CGFloat UPDM = 0; 165 | //CGFloat DNDM = 0; 166 | //CGFloat TR = 0; 167 | //CGFloat SUNMDX = 0; 168 | //CGFloat ADMUP = 0; 169 | //CGFloat ADMDN = 0; 170 | //CGFloat ATR = 0; 171 | //CGFloat DIUP = 0; 172 | //CGFloat DIDN = 0; 173 | //CGFloat DX = 0; 174 | @property (nonatomic, assign)double UPDM; 175 | @property (nonatomic, assign)double DNDM; 176 | @property (nonatomic, assign)double ADXTR; 177 | @property (nonatomic, assign)double ADMUP; 178 | @property (nonatomic, assign)double ADMDN; 179 | @property (nonatomic, assign)double ADXATR; 180 | @property (nonatomic, assign)double DIUP; 181 | @property (nonatomic, assign)double DIDN; 182 | @property (nonatomic, assign)double DX; 183 | @property (nonatomic, assign)double ADX; 184 | 185 | //VOL 186 | @property (nonatomic, copy) NSNumber *volumn_MA5; 187 | @property (nonatomic, copy) NSNumber *volumn_MA10; 188 | @property (nonatomic, copy) NSNumber *volumn_MA20; 189 | 190 | 191 | 192 | //MA 193 | @property (nonatomic, copy) NSNumber *MA5; 194 | @property (nonatomic, copy) NSNumber *MA10; 195 | @property (nonatomic, copy) NSNumber *MA14; 196 | @property (nonatomic, copy) NSNumber *MA20; 197 | 198 | //ARBP 199 | @property (nonatomic, assign) NSInteger N; 200 | @property (nonatomic, copy) NSNumber * AR; 201 | @property (nonatomic, copy) NSNumber * BR; 202 | - (void)reInitARBPData; 203 | 204 | //ATR 205 | @property (nonatomic, assign) double TR; 206 | @property (nonatomic, assign) double ATR; 207 | 208 | //BIAS 209 | @property (nonatomic, assign) double BIAS1; 210 | @property (nonatomic, assign) double BIAS2; 211 | @property (nonatomic, assign) double BIAS3; 212 | - (void)reInitBIASData; 213 | 214 | //CCI 215 | @property (nonatomic, assign) double TP; 216 | @property (nonatomic, assign) double CCI; 217 | 218 | //DKBY 219 | @property (nonatomic, assign) double SMA; 220 | 221 | @property (nonatomic, assign) double SELL; 222 | @property (nonatomic, assign) double BUY; 223 | @property (nonatomic, assign) double ENE1; 224 | @property (nonatomic, assign) double ENE2; 225 | 226 | //KD 227 | @property (nonatomic, assign) double RSV; 228 | @property (nonatomic, assign) double K; 229 | @property (nonatomic, assign) double D; 230 | 231 | 232 | //LWR 233 | @property (nonatomic, assign) double LWR1; 234 | @property (nonatomic, assign) double LWR2; 235 | 236 | //WR 237 | @property (nonatomic, assign) double WR; 238 | 239 | //QHLSR 240 | @property (nonatomic, assign) double DD; 241 | @property (nonatomic, assign) double GG; 242 | 243 | @property (nonatomic, assign) double EMA1; 244 | @property (nonatomic, assign) double EMA2; 245 | @property (nonatomic, assign) double EMA3; 246 | @property (nonatomic, assign) double EMA4; 247 | @property (nonatomic, assign) double EMA5; 248 | @property (nonatomic, assign) double EMA6; 249 | 250 | @property (nonatomic, assign) double PBX1; 251 | @property (nonatomic, assign) double PBX2; 252 | @property (nonatomic, assign) double PBX3; 253 | @property (nonatomic, assign) double PBX4; 254 | @property (nonatomic, assign) double PBX5; 255 | @property (nonatomic, assign) double PBX6; 256 | 257 | @end 258 | -------------------------------------------------------------------------------- /AbuKlineView/ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.m 3 | // AbuKlineView 4 | // 5 | // Created by 阿布 on 17/9/6. 6 | // Copyright © 2017年 阿布. All rights reserved. 7 | // 8 | 9 | #import "ViewController.h" 10 | #import "AppDelegate.h" 11 | #import "KLineSubCalculate.h" 12 | @interface ViewController () 13 | 14 | @property (nonatomic, strong) NSString * currentRequestType; 15 | 16 | @property (nonatomic, strong) KlineTitleView * klineTitleView; 17 | 18 | @property (nonatomic, assign) KLINETYPE currentType; 19 | 20 | @property (nonatomic, strong) AbuKlineView * kLineView; 21 | 22 | @property (nonatomic,strong) KLineModel * model; 23 | 24 | @property (nonatomic,strong) NSMutableArray * dataSource; 25 | 26 | @property (nonatomic,assign) UIInterfaceOrientation orientation; 27 | 28 | @end 29 | 30 | @implementation ViewController 31 | 32 | - (void)viewDidAppear:(BOOL)animated 33 | { 34 | [super viewDidAppear:animated]; 35 | 36 | ((AppDelegate *)[UIApplication sharedApplication].delegate).allowRotation = YES; 37 | 38 | } 39 | 40 | - (void)viewDidDisappear:(BOOL)animated 41 | { 42 | [super viewDidDisappear:animated]; 43 | 44 | ((AppDelegate *)[UIApplication sharedApplication].delegate).allowRotation = NO; 45 | [self supportRotion:UIInterfaceOrientationPortrait]; 46 | } 47 | 48 | - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation 49 | { 50 | return YES; 51 | } 52 | 53 | #if __IPHONE_OS_VERSION_MAX_ALLOWED < __IPHONE_9_0 54 | - (NSUInteger)supportedInterfaceOrientations 55 | #else 56 | - (UIInterfaceOrientationMask)supportedInterfaceOrientations 57 | #endif 58 | { 59 | return UIInterfaceOrientationMaskPortrait | UIInterfaceOrientationLandscapeLeft; 60 | } 61 | 62 | - (void)viewDidLoad { 63 | [super viewDidLoad]; 64 | self.view.backgroundColor = [UIColor blackColor]; 65 | self.currentType = K_LINE_1MIN; 66 | [self buildKLineTitleView]; 67 | [self.view addSubview:self.kLineView]; 68 | [self addKLineSubView]; 69 | [self requestKLineViewHistoryListWithType:self.currentType]; 70 | } 71 | 72 | - (void)buildKLineTitleView 73 | { 74 | [self.view addSubview:self.klineTitleView]; 75 | 76 | } 77 | 78 | - (void)addKLineSubView 79 | { 80 | WS(weakSelf); 81 | self.orientation = [[UIApplication sharedApplication] statusBarOrientation]; 82 | 83 | if (self.orientation == UIDeviceOrientationPortrait || self.orientation == UIDeviceOrientationPortraitUpsideDown) { 84 | [self.klineTitleView mas_makeConstraints:^(MASConstraintMaker *make) { 85 | make.left.right.equalTo(weakSelf.view); 86 | make.top.equalTo(weakSelf.view).offset(100); 87 | make.height.mas_offset(30); 88 | }]; 89 | [self.kLineView mas_makeConstraints:^(MASConstraintMaker *make) { 90 | make.top.equalTo(weakSelf.view).offset(132); 91 | make.right.equalTo(weakSelf.view.mas_right); 92 | make.left.equalTo(weakSelf.view.mas_left); 93 | make.height.mas_offset(ChartViewHigh); 94 | }]; 95 | } 96 | if (self.orientation==UIDeviceOrientationLandscapeLeft || self.orientation == UIDeviceOrientationLandscapeRight) { 97 | [self.klineTitleView mas_makeConstraints:^(MASConstraintMaker *make) { 98 | make.left.right.equalTo(weakSelf.view); 99 | make.top.equalTo(weakSelf.view).offset(40); 100 | make.height.mas_offset(30); 101 | }]; 102 | [self.kLineView mas_makeConstraints:^(MASConstraintMaker *make) { 103 | make.top.equalTo(weakSelf.view).offset(72); 104 | make.right.equalTo(weakSelf.view.mas_right); 105 | make.left.equalTo(weakSelf.view.mas_left); 106 | make.height.mas_offset(LandscapeChartViewHigh); 107 | }]; 108 | } 109 | } 110 | 111 | - (void)selectIndex:(KLINETYPE)type 112 | { 113 | if (type == self.currentType) { 114 | return; 115 | } 116 | [self requestKLineViewHistoryListWithType:type]; 117 | self.currentType = type; 118 | } 119 | 120 | 121 | 122 | - (void)requestKLineViewHistoryListWithType:(KLINETYPE)type 123 | { 124 | if (type == K_LINE_1MIN) 125 | { 126 | [self loadStockDataWithJson:@"stock1"]; 127 | self.currentRequestType = @"M1"; 128 | } 129 | else if (type == K_LINE_5MIN) 130 | { 131 | [self loadStockDataWithJson:@"stock5"]; 132 | self.currentRequestType = @"M5"; 133 | } 134 | else if (type == K_LINE_15MIN) 135 | { 136 | [self loadStockDataWithJson:@"stock15"]; 137 | self.currentRequestType = @"M15"; 138 | } 139 | else if (type == K_LINE_30MIN) 140 | { 141 | [self loadStockDataWithJson:@"stock30"]; 142 | self.currentRequestType = @"M30"; 143 | } 144 | else if (type == K_LINE_1HOUR || type == K_LINE_60MIN) 145 | { 146 | [self loadStockDataWithJson:@"stock60"]; 147 | self.currentRequestType = @"H1"; 148 | } 149 | else if (type == K_LINE_DAY) 150 | { 151 | [self loadStockDataWithJson:@"stockDay"]; 152 | self.currentRequestType = @"D1"; 153 | } 154 | } 155 | 156 | - (void)loadStockDataWithJson:(NSString *)json 157 | { 158 | NSString *Path = [[NSBundle mainBundle] pathForResource:json ofType:@"json"]; 159 | NSData *data = [[NSData alloc] initWithContentsOfFile:Path]; 160 | 161 | NSDictionary * stockDict = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil]; 162 | if (IsDictionaryNull(stockDict)) { 163 | return; 164 | } 165 | if (self.dataSource) { 166 | [self.dataSource removeAllObjects]; 167 | } 168 | NSArray * stockData = stockDict[@"d"][@"p"]; 169 | for (int i = 0; i < stockData.count; i++) 170 | { 171 | NSDictionary * dic = stockData[i]; 172 | KLineModel * model = [[KLineModel alloc]init]; 173 | model.closePrice = [dic[@"c"] floatValue]; 174 | model.openPrice = [dic[@"o"] floatValue]; 175 | model.highPrice = [dic[@"h"] floatValue]; 176 | model.lowPrice = [dic[@"l"] floatValue]; 177 | model.date = [self changeDtaForMatMmDd:dic[@"t"] range:NSMakeRange(14, 2)]; 178 | model.timestamp = (NSInteger)[self changeTime:model.date]; 179 | model.timeStr = [self updateTime:[NSString stringWithFormat:@"%ld",(long)model.timestamp]]; 180 | model.volumn = dic[@"v"]; 181 | if (i % 16 == 0) 182 | { 183 | model.isDrawDate = YES; 184 | } 185 | [self.dataSource addObject:model]; 186 | } 187 | self.dataSource = [[[KLineSubCalculate sharedInstance] initializeQuotaDataWithArray:self.dataSource KPIType:4] mutableCopy]; 188 | dispatch_async(dispatch_get_main_queue(), ^{ 189 | self.kLineView.dataArray = self.dataSource; 190 | }); 191 | } 192 | 193 | - (NSString *)changeDtaForMatMmDd:(NSString *)data range:(NSRange)range 194 | { 195 | NSArray * timeArray = [data componentsSeparatedByString:@"T"]; 196 | NSString * time = [timeArray.firstObject stringByAppendingString:[NSString stringWithFormat:@" %@",timeArray.lastObject]]; 197 | return time; 198 | } 199 | 200 | - (long)changeTime:(NSString *)time 201 | { 202 | NSDateFormatter *formatter = [[NSDateFormatter alloc] init]; 203 | [formatter setDateFormat:@"YYYY-MM-dd HH:mm:ss"]; 204 | NSDate *lastDate = [formatter dateFromString:time]; 205 | long firstStamp = [lastDate timeIntervalSince1970]; 206 | return firstStamp; 207 | } 208 | 209 | -(NSString*)updateTime:(NSString*)time{ 210 | 211 | NSString *format = nil; 212 | if ([self.currentRequestType containsString:@"D"]||[self.currentRequestType containsString:@"W"]||[self.currentRequestType isEqualToString:@"MN"]) { 213 | format = @"MM-dd"; 214 | }else if ([self.currentRequestType containsString:@"M"]||[self.currentRequestType containsString:@"H"]) 215 | { 216 | format = @"MM-dd HH:mm"; 217 | } 218 | NSDateFormatter*formatter = [[NSDateFormatter alloc]init]; 219 | [formatter setDateStyle:NSDateFormatterMediumStyle]; 220 | [formatter setTimeStyle:NSDateFormatterShortStyle]; 221 | [formatter setDateFormat:format]; 222 | int timeval = [time intValue]; 223 | NSDate*confromTimesp = [NSDate dateWithTimeIntervalSince1970:timeval]; 224 | NSString *confromTimespStr = [formatter stringFromDate:confromTimesp]; 225 | return confromTimespStr; 226 | } 227 | 228 | - (KlineTitleView *)klineTitleView 229 | { 230 | if (!_klineTitleView) { 231 | _klineTitleView = [[KlineTitleView alloc] initWithklineTitleArray:@[@"1分",@"5分",@"15分",@"30分",@"1小时",@"日K"] typeArray:@[@34,@1,@2,@3,@37,@5]]; 232 | _klineTitleView.delegate = self; 233 | } 234 | return _klineTitleView; 235 | } 236 | 237 | - (UIInterfaceOrientation)orientation 238 | { 239 | return [[UIApplication sharedApplication] statusBarOrientation]; 240 | } 241 | 242 | - (NSMutableArray *)dataSource 243 | { 244 | if (!_dataSource) { 245 | _dataSource = [NSMutableArray array]; 246 | } 247 | return _dataSource; 248 | } 249 | 250 | - (AbuKlineView *)kLineView 251 | { 252 | if (!_kLineView) { 253 | _kLineView = [[AbuKlineView alloc]init]; 254 | } 255 | return _kLineView; 256 | } 257 | 258 | 259 | 260 | 261 | @end 262 | -------------------------------------------------------------------------------- /AbuKlineView/KlineView/KLineView/KLineView/AbuKlineView.m: -------------------------------------------------------------------------------- 1 | // 2 | // AbuKlineView.m 3 | // AbuKlineView 4 | // 5 | // Created by Jefferson.zhang on 2017/9/6. 6 | // Copyright © 2017年 阿布. All rights reserved. 7 | // 8 | 9 | #import "AbuKlineView.h" 10 | #import "AbuCandChartView.h" 11 | #import "KlineCurrentPriceView.h" 12 | #import "KlineVerticalView.h" 13 | #import "KlinePriceView.h" 14 | @interface AbuKlineView() 15 | @property (nonatomic, strong) UIScrollView * scrollView; 16 | @property (nonatomic, strong) AbuCandChartView * candleChartView; 17 | @property (nonatomic, strong) KLineModel * model; 18 | @property (nonatomic,strong) KlinePriceView * priceView; 19 | @property (nonatomic, strong) KlinePriceView * quotaPriceView; 20 | @property (nonatomic, strong) UILongPressGestureRecognizer * longPressGesture; 21 | @property (nonatomic, strong) UIPinchGestureRecognizer * pinchPressGesture; 22 | @property (nonatomic, strong) UITapGestureRecognizer * tapGesture; 23 | @property (nonatomic, strong) KlineVerticalView * verticalView; 24 | @property (nonatomic, strong) KlineCurrentPriceView * leavView; 25 | @property (nonatomic, strong) UILabel * dataLable; 26 | @property (nonatomic, strong) KlineCurrentPriceView * currentPriceView; 27 | @property (nonatomic, assign) NSInteger curentIndex; 28 | @property (nonatomic,assign) UIInterfaceOrientation orientation; 29 | @property (nonatomic, assign) BOOL ishow; 30 | @end 31 | 32 | @implementation AbuKlineView 33 | 34 | - (instancetype)init 35 | { 36 | self = [super init]; 37 | if (self) { 38 | [self setUI]; 39 | } 40 | return self; 41 | } 42 | 43 | - (instancetype)initWithFrame:(CGRect)frame 44 | { 45 | self = [super initWithFrame:frame]; 46 | if (self) { 47 | [self setUI]; 48 | } 49 | return self; 50 | } 51 | 52 | - (void)setUI{ 53 | 54 | _curentIndex = 0; 55 | self.ishow = YES; 56 | [self addSubview:self.scrollView]; 57 | [self initChartView]; 58 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(statusBarOrientationChange:) name:UIApplicationDidChangeStatusBarOrientationNotification object:nil]; 59 | } 60 | 61 | - (void)statusBarOrientationChange:(NSNotification *)notification 62 | { 63 | WS(weakSelf); 64 | 65 | self.orientation = [[UIApplication sharedApplication] statusBarOrientation]; 66 | if (self.orientation == UIDeviceOrientationPortrait || self.orientation == UIDeviceOrientationPortraitUpsideDown) { 67 | [self.scrollView mas_updateConstraints:^(MASConstraintMaker *make) { 68 | make.height.mas_offset(ChartViewHigh); 69 | }]; 70 | [self.candleChartView mas_updateConstraints:^(MASConstraintMaker *make) { 71 | make.height.mas_offset(ChartViewHigh); 72 | }]; 73 | } 74 | if (self.orientation==UIDeviceOrientationLandscapeLeft || self.orientation == UIDeviceOrientationLandscapeRight) { 75 | [self.scrollView mas_updateConstraints:^(MASConstraintMaker *make) { 76 | make.height.mas_offset(LandscapeChartViewHigh); 77 | }]; 78 | [self.candleChartView mas_updateConstraints:^(MASConstraintMaker *make) { 79 | make.height.mas_offset(LandscapeChartViewHigh); 80 | }]; 81 | } 82 | if (self.ishow) { 83 | [self.priceView mas_remakeConstraints:^(MASConstraintMaker *make) { 84 | make.right.top.equalTo(weakSelf); 85 | make.left.equalTo(weakSelf.scrollView.mas_right); 86 | make.height.mas_equalTo(weakSelf.candleChartView.mas_height).multipliedBy(2.0f / 3.0f); 87 | }]; 88 | [self.priceView updateFrameWithHeight:weakSelf.scrollView.height * orignIndicatorOrignY]; 89 | } 90 | else 91 | { 92 | [self.priceView mas_remakeConstraints:^(MASConstraintMaker *make) { 93 | make.bottom.right.top.equalTo(weakSelf); 94 | make.left.equalTo(weakSelf.scrollView.mas_right); 95 | }]; 96 | [self.priceView updateFrameWithHeight:weakSelf.scrollView.height]; 97 | } 98 | } 99 | -(void)initChartView{ 100 | [self addSubview:self.priceView]; 101 | [self addSubview:self.quotaPriceView]; 102 | [self.scrollView addSubview:self.candleChartView]; 103 | [self addConstrains]; 104 | [self addGesture]; 105 | [self initCrossLine]; 106 | [self initdataLabel]; 107 | [self initCurrentPrice]; 108 | } 109 | 110 | - (void)addConstrains 111 | { 112 | WS(weakSelf); 113 | 114 | self.orientation = [[UIApplication sharedApplication] statusBarOrientation]; 115 | CGFloat height = 0.0; 116 | if (self.orientation == UIDeviceOrientationPortrait || self.orientation == UIDeviceOrientationPortraitUpsideDown) { 117 | [_scrollView mas_makeConstraints:^(MASConstraintMaker *make) { 118 | make.top.equalTo(weakSelf); 119 | make.left.equalTo(weakSelf); 120 | make.right.equalTo(weakSelf.mas_right).offset(-priceWidth); 121 | make.height.mas_offset(ChartViewHigh); 122 | }]; 123 | 124 | [self.candleChartView mas_makeConstraints:^(MASConstraintMaker *make) { 125 | make.top.equalTo(weakSelf); 126 | make.left.equalTo(weakSelf.scrollView.mas_left); 127 | make.right.equalTo(weakSelf).offset(-5); 128 | make.height.mas_offset(ChartViewHigh); 129 | }]; 130 | height = ChartViewHigh; 131 | } 132 | if (self.orientation==UIDeviceOrientationLandscapeLeft || self.orientation == UIDeviceOrientationLandscapeRight) { 133 | [_scrollView mas_makeConstraints:^(MASConstraintMaker *make) { 134 | make.top.equalTo(weakSelf); 135 | make.left.equalTo(weakSelf).offset(5); 136 | make.right.equalTo(weakSelf.mas_right).offset(-priceWidth); 137 | make.height.mas_offset(LandscapeChartViewHigh); 138 | }]; 139 | [self.candleChartView mas_makeConstraints:^(MASConstraintMaker *make) { 140 | make.top.equalTo(weakSelf); 141 | make.left.equalTo(weakSelf.scrollView.mas_left); 142 | make.right.equalTo(weakSelf).offset(-5); 143 | make.height.mas_offset(LandscapeChartViewHigh); 144 | }]; 145 | height = LandscapeChartViewHigh; 146 | } 147 | 148 | [self.priceView mas_makeConstraints:^(MASConstraintMaker *make) { 149 | make.top.equalTo(weakSelf.mas_top); 150 | make.right.equalTo(weakSelf); 151 | make.left.equalTo(weakSelf.scrollView.mas_right); 152 | make.height.mas_equalTo(weakSelf.candleChartView.mas_height).multipliedBy(2.0f / 3.0f); 153 | }]; 154 | 155 | [self.quotaPriceView mas_makeConstraints:^(MASConstraintMaker *make) { 156 | make.top.equalTo(weakSelf.priceView.mas_bottom); 157 | make.right.equalTo(weakSelf); 158 | make.left.equalTo(weakSelf.scrollView.mas_right); 159 | make.bottom.equalTo(weakSelf); 160 | }]; 161 | [self.priceView updateFrameWithHeight:height * 2.0f / 3.0f]; 162 | [self.quotaPriceView updateFrameWithHeight:height * 1.0f / 3.0f]; 163 | } 164 | - (void)addGesture 165 | { 166 | _longPressGesture = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(longGesture:)]; 167 | [self.candleChartView addGestureRecognizer:_longPressGesture]; 168 | 169 | _pinchPressGesture = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:@selector(pinchGesture:)]; 170 | [self.scrollView addGestureRecognizer:_pinchPressGesture]; 171 | } 172 | 173 | - (void)initCrossLine 174 | { 175 | WS(weakSelf); 176 | self.verticalView = [[KlineVerticalView alloc] init]; 177 | self.verticalView.clipsToBounds = YES; 178 | [self.scrollView addSubview:self.verticalView]; 179 | [self.verticalView mas_makeConstraints:^(MASConstraintMaker *make) { 180 | make.top.equalTo(weakSelf.scrollView.mas_top); 181 | make.width.equalTo(@(2)); 182 | make.bottom.equalTo(weakSelf.candleChartView.mas_bottom).offset(-20); 183 | make.left.equalTo(@(0)); 184 | }]; 185 | self.leavView = [[KlineCurrentPriceView alloc] initWithUpdate:NO]; 186 | self.leavView.clipsToBounds = YES; 187 | [self addSubview:self.leavView]; 188 | [self.leavView mas_makeConstraints:^(MASConstraintMaker *make) { 189 | make.top.equalTo(@(0)); 190 | make.left.equalTo(weakSelf); 191 | make.right.equalTo(weakSelf.candleChartView); 192 | make.height.equalTo(@(15)); 193 | }]; 194 | 195 | self.leavView.hidden = YES; 196 | self.verticalView.hidden = YES; 197 | } 198 | 199 | - (void)initdataLabel 200 | { 201 | _dataLable = [[UILabel alloc]init]; 202 | _dataLable.font = [UIFont systemFontOfSize:10]; 203 | _dataLable.textColor = [UIColor whiteColor]; 204 | _dataLable.backgroundColor = [UIColor orangeColor]; 205 | [self.candleChartView addSubview:self.dataLable]; 206 | WS(weakSelf); 207 | [self.dataLable mas_makeConstraints:^(MASConstraintMaker *make) { 208 | make.bottom.equalTo(weakSelf.candleChartView.mas_bottom); 209 | make.width.mas_offset(priceWidth); 210 | make.height.mas_offset(15); 211 | make.left.equalTo(@0); 212 | }]; 213 | self.dataLable.hidden = YES; 214 | } 215 | 216 | - (void)initCurrentPrice 217 | { 218 | WS(weakSelf); 219 | _currentPriceView = [[KlineCurrentPriceView alloc]initWithUpdate:YES]; 220 | [self addSubview:self.currentPriceView]; 221 | [self.currentPriceView mas_makeConstraints:^(MASConstraintMaker *make) { 222 | make.top.equalTo(@(0)); 223 | make.left.equalTo(weakSelf); 224 | make.right.equalTo(weakSelf.candleChartView); 225 | make.height.equalTo(@(14)); 226 | }]; 227 | self.currentPriceView.hidden = YES; 228 | } 229 | 230 | - (void)longGesture:(UILongPressGestureRecognizer*)longPress 231 | { 232 | static CGFloat oldPositionX = 0; 233 | if(UIGestureRecognizerStateChanged == longPress.state || UIGestureRecognizerStateBegan == longPress.state) 234 | { 235 | CGPoint location = [longPress locationInView:self.candleChartView]; 236 | if(ABS(oldPositionX - location.x) < (self.candleChartView.candleWidth + self.candleChartView.candleSpace)/2) 237 | { 238 | return; 239 | } 240 | self.scrollView.scrollEnabled = NO; 241 | oldPositionX = location.x; 242 | CGPoint point = [self.candleChartView getLongPressModelPostionWithXPostion:location.x]; 243 | CGFloat xPositoin = point.x + (self.candleChartView.candleWidth)/2.f - self.candleChartView.candleSpace/2.f ; 244 | CGFloat yPositoin = point.y; 245 | [self.verticalView mas_updateConstraints:^(MASConstraintMaker *make) { 246 | make.left.equalTo(@(xPositoin)); 247 | }]; 248 | [self.leavView mas_updateConstraints:^(MASConstraintMaker *make) { 249 | make.top.mas_equalTo(yPositoin); 250 | }]; 251 | [self.dataLable mas_updateConstraints:^(MASConstraintMaker *make) { 252 | make.left.equalTo(@(xPositoin - 35)); 253 | }]; 254 | self.verticalView.hidden = NO; 255 | self.leavView.hidden = NO; 256 | self.dataLable.hidden = NO; 257 | } 258 | if(longPress.state == UIGestureRecognizerStateEnded) 259 | { 260 | if(self.verticalView) 261 | { 262 | self.verticalView.hidden = YES; 263 | } 264 | 265 | if(self.leavView) 266 | { 267 | self.leavView.hidden = YES; 268 | } 269 | oldPositionX = 0; 270 | self.scrollView.scrollEnabled = YES; 271 | self.dataLable.hidden = YES; 272 | } 273 | } 274 | 275 | float oldScale = 0; 276 | 277 | - (void)pinchGesture:(UIPinchGestureRecognizer*)pinchPress 278 | { 279 | if (pinchPress.numberOfTouches < 2) 280 | { 281 | _candleChartView.kvoEnable = YES; 282 | _scrollView.scrollEnabled = YES; 283 | return; 284 | } 285 | 286 | switch (pinchPress.state) { 287 | case UIGestureRecognizerStateBegan: 288 | { 289 | _scrollView.scrollEnabled = NO; 290 | _candleChartView.kvoEnable = NO; 291 | oldScale = pinchPress.scale; 292 | }break; 293 | case UIGestureRecognizerStateEnded: 294 | { 295 | _scrollView.scrollEnabled = YES; 296 | _candleChartView.kvoEnable = YES; 297 | }break; 298 | default: 299 | break; 300 | } 301 | CGFloat minScale = 0.03; 302 | CGFloat diffScale = pinchPress.scale - oldScale; 303 | 304 | if (fabs(diffScale) > minScale) 305 | { 306 | CGFloat oldKlineWidth = self.candleChartView.candleWidth; 307 | 308 | self.candleChartView.candleWidth = (1 + diffScale) * oldKlineWidth; 309 | 310 | if (self.candleChartView.candleWidth > maxCandelWith) { 311 | self.candleChartView.candleWidth = maxCandelWith; 312 | } 313 | if (self.candleChartView.candleWidth < minCandelWith) { 314 | self.candleChartView.candleWidth = minCandelWith; 315 | } 316 | [_candleChartView updateWidth]; 317 | CGPoint point1 = [pinchPress locationOfTouch:0 inView:self.scrollView]; 318 | CGPoint point2 = [pinchPress locationOfTouch:1 inView:self.scrollView]; 319 | CGFloat pinCenterX = (point1.x + point2.x) / 2; 320 | CGFloat scrollViewPinCenterX = pinCenterX; 321 | NSInteger pinCenterLeftCount = scrollViewPinCenterX / (_candleChartView.candleWidth + _candleChartView.candleSpace); 322 | pinCenterLeftCount = _candleChartView.currentStartIndex; 323 | CGFloat newPinCenterX = pinCenterLeftCount * _candleChartView.candleWidth + (pinCenterLeftCount) * _candleChartView.candleSpace; 324 | CGFloat newOffsetX = newPinCenterX; 325 | _scrollView.contentOffset = CGPointMake(newOffsetX > 0 ? newOffsetX : 0, 0); 326 | _candleChartView.contentOffset = _scrollView.contentOffset.x; 327 | [_candleChartView drawKLine]; 328 | } 329 | } 330 | 331 | 332 | - (void)setDataArray:(NSMutableArray<__kindof KLineModel *> *)dataArray 333 | { 334 | _dataArray = dataArray; 335 | self.candleChartView.dataArray = dataArray; 336 | dispatch_async(dispatch_get_main_queue(), ^{ 337 | [self.candleChartView reloadKline]; 338 | }); 339 | } 340 | 341 | 342 | - (void)refreshFSKlineView:(KLineModel *)model 343 | { 344 | [self.candleChartView refreshKLineView:model]; 345 | [self.candleChartView layoutIfNeeded]; 346 | [self setNeedsDisplay]; 347 | } 348 | 349 | - (void)reloadPriceViewWithPriceArr:(NSArray *)priceArr 350 | { 351 | [self.priceView reloadPriceWithPriceArr:priceArr precision:2]; 352 | } 353 | 354 | - (void)reloadPriceViewWithQuotaMaxValue:(double)maxValue MinValue:(double)minValue 355 | { 356 | [self.quotaPriceView reloadPriceWithPriceArr:@[@(maxValue),@(minValue)] precision:2]; 357 | } 358 | - (void)showIndexLineView:(CGFloat)leftPostion startIndex:(NSInteger)index count:(NSInteger)count 359 | { 360 | 361 | } 362 | 363 | - (void)displayScreenleftPostion:(CGFloat)leftPostion startIndex:(NSInteger)index count:(NSInteger)count 364 | { 365 | [self showIndexLineView:leftPostion startIndex:index count:count]; 366 | } 367 | 368 | - (void)longPressCandleViewWithIndex:(NSInteger)kLineModeIndex kLineModel:(KLineModel *)kLineModel 369 | { 370 | self.dataLable.text = kLineModel.timeStr; 371 | [self.leavView updateNewPrice:[NSString stringWithFormat:@"%.2lf",kLineModel.openPrice] backgroundColor:[UIColor orangeColor] precision:2]; 372 | } 373 | 374 | - (void)refreshCurrentPrice:(KLineModel *)kLineModel candleModel:(KLineModel *)candleModel 375 | { 376 | CGFloat close = kLineModel.closesPoint.y; 377 | CGFloat y = close; 378 | [self.currentPriceView mas_updateConstraints:^(MASConstraintMaker *make) { 379 | make.top.mas_equalTo(y + topDistance); 380 | }]; 381 | self.currentPriceView.hidden = NO; 382 | [self.currentPriceView updateNewPrice:[NSString stringWithFormat:@"%.2lf",candleModel.closePrice] backgroundColor:[UIColor orangeColor] precision:2]; 383 | } 384 | 385 | - (void)displayLastModel:(KLineModel *)kLineModel 386 | { 387 | 388 | } 389 | 390 | - (void)displayMoreData 391 | { 392 | NSLog(@"1233333"); 393 | 394 | } 395 | 396 | - (UIInterfaceOrientation)orientation 397 | { 398 | return [[UIApplication sharedApplication] statusBarOrientation]; 399 | } 400 | 401 | - (UIScrollView *)scrollView 402 | { 403 | if (!_scrollView) { 404 | _scrollView = [[UIScrollView alloc]init]; 405 | _scrollView.showsHorizontalScrollIndicator = NO; 406 | _scrollView.bounces = NO; 407 | _scrollView.layer.borderWidth = 1.0f; 408 | _scrollView.backgroundColor = [UIColor blackColor]; 409 | _scrollView.layer.borderColor = [UIColor blackColor].CGColor; 410 | } 411 | return _scrollView; 412 | } 413 | - (AbuCandChartView *)candleChartView 414 | { 415 | if (!_candleChartView) { 416 | _candleChartView = [[AbuCandChartView alloc]init]; 417 | _candleChartView.delegate = self; 418 | _candleChartView.candleSpace = 2; 419 | } 420 | return _candleChartView; 421 | } 422 | 423 | - (KlinePriceView *)priceView 424 | { 425 | if (!_priceView) { 426 | _priceView = [[KlinePriceView alloc] initWithFrame:CGRectMake(0,0,0,0) PriceArr:@[@"",@"",@"",@"",@"",@""]]; 427 | _priceView.backgroundColor = [UIColor blackColor]; 428 | 429 | } 430 | return _priceView; 431 | } 432 | 433 | - (KlinePriceView *)quotaPriceView 434 | { 435 | if (!_quotaPriceView) { 436 | _quotaPriceView = [[KlinePriceView alloc]initWithFrame:CGRectMake(0,0,0,0) PriceArr:@[@"",@""]]; 437 | _quotaPriceView.backgroundColor = [UIColor blackColor]; 438 | } 439 | return _quotaPriceView; 440 | } 441 | 442 | -(void)dealloc 443 | { 444 | [[NSNotificationCenter defaultCenter] removeObserver:self]; 445 | } 446 | 447 | @end 448 | -------------------------------------------------------------------------------- /AbuKlineView/KlineView/KLineView/KLineView/AbuCandChartView.m: -------------------------------------------------------------------------------- 1 | // 2 | // AbuCandChartView.m 3 | // AbuKlineView 4 | // 5 | // Created by 阿布 on 17/9/7. 6 | // Copyright © 2017年 阿布. All rights reserved. 7 | // 8 | 9 | #import "AbuCandChartView.h" 10 | #import "KlineQuotaColumnLayer.h" 11 | #import "KlineCandelLayer.h" 12 | #import "KlineTimeLayer.h" 13 | #import "KlineMALayer.h" 14 | 15 | #define MINDISPLAYCOUNT 6 16 | @interface AbuCandChartView() 17 | @property (nonatomic,strong) UIScrollView * scrollView; 18 | @property (nonatomic, strong) FBKVOController * KVOController; 19 | @property (nonatomic,assign) CGFloat leftPostion; 20 | @property (nonatomic,strong) NSMutableArray * currentDisplayArray; 21 | @property (nonatomic,assign) CGFloat previousOffsetX; 22 | @property (nonatomic,assign) CGFloat maxAssert; 23 | @property (nonatomic,assign) CGFloat minAssert; 24 | @property (nonatomic,assign) CGFloat heightPerpoint; 25 | @property (nonatomic, assign) BOOL isRefresh; 26 | @property (nonatomic, strong) KlineTimeLayer * timeLayer; 27 | @property (nonatomic, strong) KlineCandelLayer * candelLayer; 28 | @property (nonatomic, strong) KlineMALayer * ma5LineLayer; 29 | @property (nonatomic, strong) KlineMALayer * ma10LineLayer; 30 | @property (nonatomic, strong) KlineMALayer * ma20LineLayer; 31 | @property (nonatomic,assign) double quotaMinAssert; 32 | @property (nonatomic,assign) double quotaMaxAssert; 33 | @property (nonatomic,assign) CGFloat quotaHeightPerPoint; 34 | @property (nonatomic, strong) KlineQuotaColumnLayer * macdLayer; 35 | @property (nonatomic, strong) KlineMALayer * deaLayer; 36 | @property (nonatomic, strong) KlineMALayer * diffLayer; 37 | @property (nonatomic,strong) KlineMALayer * kLineLayer; 38 | @property (nonatomic,strong) KlineMALayer * dLineLayer; 39 | @property (nonatomic,strong) KlineMALayer * wrLineLayer; 40 | @property (nonatomic,assign) float chartSize; 41 | @property (nonatomic,assign) BOOL isShowSubView; 42 | @property (nonatomic, assign) CGFloat contentHeight; 43 | @end 44 | @implementation AbuCandChartView 45 | 46 | - (instancetype)init 47 | { 48 | self = [super init]; 49 | if (self) { 50 | [self initConfig]; 51 | [self calcuteCandleWidth]; 52 | } 53 | return self; 54 | } 55 | 56 | -(void)initConfig 57 | { 58 | self.kvoEnable = YES; 59 | self.isRefresh = YES; 60 | self.chartSize = orignChartScale; 61 | self.isShowSubView = YES; 62 | } 63 | 64 | - (void)reloadKline 65 | { 66 | [self updateWidth]; 67 | [self drawKLine]; 68 | } 69 | 70 | #pragma mark KVO 71 | 72 | -(void)didMoveToSuperview 73 | { 74 | [super didMoveToSuperview]; 75 | _scrollView = (UIScrollView*)self.superview; 76 | _scrollView.delegate = self; 77 | [self addListener]; 78 | } 79 | 80 | - (void)addListener 81 | { 82 | FBKVOController *KVOController = [FBKVOController controllerWithObserver:self]; 83 | self.KVOController = KVOController; 84 | WS(weakSelf); 85 | [self.KVOController observe:_scrollView keyPath:ContentOffSet options:NSKeyValueObservingOptionNew block:^(id _Nullable observer, id _Nonnull object, NSDictionary * _Nonnull change) { 86 | if (self.kvoEnable) 87 | { 88 | weakSelf.contentOffset = weakSelf.scrollView.contentOffset.x; 89 | weakSelf.previousOffsetX = weakSelf.scrollView.contentSize.width -weakSelf.scrollView.contentOffset.x; 90 | [weakSelf drawKLine]; 91 | } 92 | }]; 93 | } 94 | - (void)setKvoEnable:(BOOL)kvoEnable 95 | { 96 | _kvoEnable = kvoEnable; 97 | } 98 | 99 | - (NSInteger)currentStartIndex 100 | { 101 | CGFloat scrollViewOffsetX = self.leftPostion < 0 ? 0 : self.leftPostion; 102 | 103 | NSInteger leftArrCount = (scrollViewOffsetX) / (self.candleWidth+self.candleSpace); 104 | 105 | if (leftArrCount > self.dataArray.count) 106 | { 107 | _currentStartIndex = self.dataArray.count - 1; 108 | } 109 | 110 | else if (leftArrCount == 0) 111 | { 112 | _currentStartIndex = 0; 113 | } 114 | 115 | else 116 | { 117 | _currentStartIndex = leftArrCount ; 118 | } 119 | return _currentStartIndex; 120 | } 121 | 122 | - (CGFloat)leftPostion 123 | { 124 | CGFloat scrollViewOffsetX = _contentOffset < 0 ? 0 : _contentOffset; 125 | if (scrollViewOffsetX == 0) { 126 | scrollViewOffsetX = 10; 127 | } 128 | if (_contentOffset + self.scrollView.width >= self.scrollView.contentSize.width) 129 | { 130 | scrollViewOffsetX = self.scrollView.contentSize.width - self.scrollView.width; 131 | } 132 | return scrollViewOffsetX; 133 | } 134 | 135 | - (void)initCurrentDisplayModels 136 | { 137 | NSInteger needDrawKLineCount = self.scrollView.width / self.candleWidth; 138 | NSInteger currentStartIndex = self.currentStartIndex; 139 | NSInteger count = (currentStartIndex + needDrawKLineCount) >self.dataArray.count ? self.dataArray.count :currentStartIndex+needDrawKLineCount; 140 | [self.currentDisplayArray removeAllObjects]; 141 | 142 | if (currentStartIndex < count) 143 | { 144 | for (NSInteger i = currentStartIndex; i < count ; i++) 145 | { 146 | KLineModel *model = self.dataArray[i]; 147 | [self.currentDisplayArray addObject:model]; 148 | } 149 | } 150 | } 151 | 152 | - (void)calcuteMaxAndMinValue 153 | { 154 | self.maxAssert = CGFLOAT_MIN; 155 | self.minAssert = CGFLOAT_MAX; 156 | NSInteger idx = 0; 157 | for (NSInteger i = idx; i < self.currentDisplayArray.count; i++) 158 | { 159 | KLineModel * entity = [self.currentDisplayArray objectAtIndex:i]; 160 | self.minAssert = self.minAssert < entity.lowPrice ? self.minAssert : entity.lowPrice; 161 | self.maxAssert = self.maxAssert > entity.highPrice ? self.maxAssert : entity.highPrice; 162 | 163 | if (self.maxAssert - self.minAssert < 0.5) 164 | { 165 | self.maxAssert += 0.5; 166 | self.minAssert -= 0.5; 167 | } 168 | } 169 | CGFloat contentHeigh = self.scrollView.height / self.chartSize - topDistance - timeheight- topDistance; 170 | self.contentHeight = contentHeigh; 171 | self.heightPerpoint = contentHeigh / (self.maxAssert-self.minAssert); 172 | } 173 | 174 | - (void)drawMALineLayer 175 | { 176 | if (IsArrayNull(self.currentDisplayArray)) { 177 | return; 178 | } 179 | if (self.ma5LineLayer) 180 | { 181 | [self.ma5LineLayer removeFromSuperlayer]; 182 | } 183 | self.ma5LineLayer = [[KlineMALayer alloc] initQuotaDataArr:self.currentDisplayArray candleWidth:self.candleWidth candleSpace:self.candleSpace startIndex:self.leftPostion maxValue:self.maxAssert heightPerpoint:self.heightPerpoint totalHeight:0 lineWidth:1 lineColor:[UIColor greenColor] wireType:MA5TYPE klineSubOrMain:Main]; 184 | [self.layer addSublayer:self.ma5LineLayer]; 185 | 186 | 187 | if (self.ma10LineLayer) 188 | { 189 | [self.ma10LineLayer removeFromSuperlayer]; 190 | } 191 | self.ma10LineLayer = [[KlineMALayer alloc] initQuotaDataArr:self.currentDisplayArray candleWidth:self.candleWidth candleSpace:self.candleSpace startIndex:self.leftPostion maxValue:self.maxAssert heightPerpoint:self.heightPerpoint totalHeight:0 lineWidth:1 lineColor:[UIColor redColor] wireType:MA10TYPE klineSubOrMain:Main]; 192 | [self.layer addSublayer:self.ma10LineLayer]; 193 | 194 | if (self.ma20LineLayer) 195 | { 196 | [self.ma20LineLayer removeFromSuperlayer]; 197 | } 198 | self.ma20LineLayer = [[KlineMALayer alloc] initQuotaDataArr:self.currentDisplayArray candleWidth:self.candleWidth candleSpace:self.candleSpace startIndex:self.leftPostion maxValue:self.maxAssert heightPerpoint:self.heightPerpoint totalHeight:0 lineWidth:1 lineColor:[UIColor blueColor] wireType:MA20TYPE klineSubOrMain:Main]; 199 | [self.layer addSublayer:self.ma20LineLayer]; 200 | 201 | } 202 | 203 | 204 | 205 | #pragma mark publicMethod 206 | 207 | - (CAShapeLayer*)getAxispLayer 208 | { 209 | CAShapeLayer *layer = [CAShapeLayer layer]; 210 | layer.strokeColor = [UIColor colorWithHexString:@"#ededed"].CGColor; 211 | layer.fillColor = [[UIColor clearColor] CGColor]; 212 | layer.contentsScale = [UIScreen mainScreen].scale; 213 | return layer; 214 | } 215 | 216 | - (void)calcuteCandleWidth 217 | { 218 | self.candleWidth = minCandelWith; 219 | } 220 | 221 | - (void)drawKLine 222 | { 223 | [self drawMainKline]; 224 | if (self.delegate && [self.delegate respondsToSelector:@selector(reloadPriceViewWithPriceArr:)]) { 225 | NSArray * priceArray = [self calculatePrcieArrWhenScroll]; 226 | [self.delegate reloadPriceViewWithPriceArr:priceArray]; 227 | } 228 | [self drawPresetQuota:KlineSubKPITypeMACD]; 229 | if (self.delegate && [self.delegate respondsToSelector:@selector(refreshCurrentPrice:candleModel:)]) { 230 | KLineModel * model = self.currentDisplayArray.lastObject; 231 | KLineModel *candelModel = self.currentDisplayArray.lastObject; 232 | [self.delegate refreshCurrentPrice:model candleModel:candelModel]; 233 | } 234 | } 235 | 236 | - (void)drawMainKline 237 | { 238 | [self drawCandelLayer]; 239 | [self drawMALineLayer]; 240 | [self drawTimeLayer]; 241 | } 242 | 243 | - (void)drawCandelLayer 244 | { 245 | 246 | [self initCurrentDisplayModels]; 247 | [self calcuteMaxAndMinValue]; 248 | if (self.delegate && [self.delegate respondsToSelector: @selector(displayScreenleftPostion:startIndex:count:)]) 249 | { 250 | [_delegate displayScreenleftPostion:self.leftPostion startIndex:self.currentStartIndex count:self.currentDisplayArray.count]; 251 | } 252 | if (self.delegate && [self.delegate respondsToSelector:@selector(displayLastModel:)]) 253 | { 254 | KLineModel *lastModel = self.currentDisplayArray.lastObject; 255 | [_delegate displayLastModel:lastModel]; 256 | } 257 | if (self.candelLayer) 258 | { 259 | [self.candelLayer removeFromSuperlayer]; 260 | } 261 | self.candelLayer = [[KlineCandelLayer alloc] initQuotaDataArr:self.currentDisplayArray candleWidth:self.candleWidth candleSpace:self.candleSpace startIndex:self.leftPostion maxValue:self.maxAssert heightPerpoint:self.heightPerpoint scrollViewContentWidth:self.scrollView.contentSize.width]; 262 | [self.layer addSublayer:self.candelLayer]; 263 | } 264 | 265 | - (void)drawTimeLayer 266 | { 267 | if (self.timeLayer) 268 | { 269 | [self.timeLayer removeFromSuperlayer]; 270 | self.timeLayer = nil; 271 | } 272 | self.timeLayer = [[KlineTimeLayer alloc] initQuotaDataArr:self.currentDisplayArray candleWidth:self.candleWidth height:self.scrollView.height / self.chartSize timeHight:timeheight bottomMargin:0 lineWidth:0]; 273 | [self.layer addSublayer:self.timeLayer]; 274 | } 275 | 276 | - (NSArray *)calculatePrcieArrWhenScroll 277 | { 278 | NSMutableArray *priceArr = [NSMutableArray array]; 279 | 280 | for (int i = 5; i>=0; i--) { 281 | 282 | if (i==5) { 283 | 284 | [priceArr addObject:@(self.maxAssert)]; 285 | continue; 286 | } 287 | if (i==0) { 288 | 289 | [priceArr addObject:@(self.minAssert)]; 290 | continue; 291 | } 292 | [priceArr addObject:@((self.minAssert + ((self.contentHeight/5)*i/self.heightPerpoint)))]; 293 | } 294 | return [priceArr copy]; 295 | } 296 | 297 | - (void)updateWidth 298 | { 299 | CGFloat klineWidth = self.dataArray.count * (self.candleWidth + self.candleSpace) + 5; 300 | 301 | if(klineWidth < self.scrollView.width) 302 | { 303 | klineWidth = self.scrollView.width; 304 | } 305 | 306 | [self mas_updateConstraints:^(MASConstraintMaker *make) { 307 | make.width.equalTo(@(klineWidth)); 308 | }]; 309 | 310 | self.scrollView.contentSize = CGSizeMake(klineWidth,0); 311 | if (self.isRefresh) { 312 | self.scrollView.contentOffset = CGPointMake(klineWidth - self.scrollView.width, 0); 313 | } 314 | 315 | } 316 | 317 | 318 | 319 | -(CGPoint)getLongPressModelPostionWithXPostion:(CGFloat)xPostion 320 | { 321 | CGFloat localPostion = xPostion; 322 | NSInteger startIndex = (NSInteger)((localPostion - self.leftPostion) / (self.candleSpace + self.candleWidth)); 323 | NSInteger arrCount = self.currentDisplayArray.count; 324 | for (NSInteger index = startIndex > 0 ? startIndex - 1 : 0; index < arrCount; ++index) { 325 | KLineModel *kLinePositionModel = self.currentDisplayArray[index]; 326 | CGFloat minX = kLinePositionModel.highestPoint.x - (self.candleSpace + self.candleWidth/2); 327 | CGFloat maxX = kLinePositionModel.highestPoint.x + (self.candleSpace + self.candleWidth/2); 328 | if(localPostion > minX && localPostion < maxX) 329 | { 330 | if(self.delegate && [self.delegate respondsToSelector:@selector(longPressCandleViewWithIndex:kLineModel:)]) 331 | { 332 | [self.delegate longPressCandleViewWithIndex:index kLineModel:self.currentDisplayArray[index]]; 333 | } 334 | return CGPointMake(kLinePositionModel.highestPoint.x, kLinePositionModel.opensPoint.y); 335 | } 336 | } 337 | 338 | KLineModel *lastPositionModel = self.currentDisplayArray.lastObject; 339 | 340 | if (localPostion >= lastPositionModel.closesPoint.x) 341 | { 342 | return CGPointMake(lastPositionModel.highestPoint.x, lastPositionModel.opensPoint.y); 343 | } 344 | KLineModel *firstPositionModel = self.currentDisplayArray.firstObject; 345 | if (firstPositionModel.closesPoint.x >= localPostion) 346 | { 347 | return CGPointMake(firstPositionModel.highestPoint.x, firstPositionModel.opensPoint.y); 348 | } 349 | return CGPointZero; 350 | } 351 | 352 | - (void)drawPresetQuota:(KlineSubKPIType)presetQuotaName; 353 | { 354 | if (self.isShowSubView) { 355 | [self calcuteQuotaMaxAssertAndMinAssert:presetQuotaName]; 356 | switch (presetQuotaName) { 357 | case KlineSubKPITypeMACD: 358 | [self drawPresetMACD]; 359 | break; 360 | case KlineSubKPITypeKD: 361 | [self drawPresetKD]; 362 | break; 363 | case KlineSubKPITypeW_R: 364 | [self drawPresetWR]; 365 | break; 366 | default: 367 | break; 368 | } 369 | } 370 | else 371 | { 372 | [self removeSubSubLayers]; 373 | } 374 | 375 | } 376 | 377 | - (void)drawPresetMACD 378 | { 379 | if (self.macdLayer) { 380 | [self.macdLayer removeFromSuperlayer]; 381 | } 382 | self.macdLayer = [[KlineQuotaColumnLayer alloc] initQuotaDataArr:self.currentDisplayArray candleWidth:self.candleWidth candleSpace:self.candleSpace startIndex:self.leftPostion maxValue:self.quotaMaxAssert heightPerpoint:self.quotaHeightPerPoint qutoaHeight:self.scrollView.height * orignIndicatorOrignY]; 383 | [self.layer addSublayer:self.macdLayer]; 384 | if (self.diffLayer) { 385 | [self.diffLayer removeFromSuperlayer]; 386 | } 387 | self.diffLayer = [[KlineMALayer alloc] initQuotaDataArr:self.currentDisplayArray candleWidth:self.candleWidth candleSpace:self.candleSpace startIndex:self.leftPostion maxValue:self.quotaMaxAssert heightPerpoint:self.quotaHeightPerPoint totalHeight:self.scrollView.height *orignIndicatorOrignY lineWidth:1 lineColor:[UIColor yellowColor] wireType:MACDDIFF klineSubOrMain:Sub]; 388 | [self.layer addSublayer:self.diffLayer]; 389 | if (self.deaLayer) { 390 | [self.deaLayer removeFromSuperlayer]; 391 | } 392 | self.deaLayer = [[KlineMALayer alloc] initQuotaDataArr:self.currentDisplayArray candleWidth:self.candleWidth candleSpace:self.candleSpace startIndex:self.leftPostion maxValue:self.quotaMaxAssert heightPerpoint:self.quotaHeightPerPoint totalHeight:self.scrollView.height *orignIndicatorOrignY lineWidth:1 lineColor:[UIColor purpleColor] wireType:MACDDEA klineSubOrMain:Sub]; 393 | [self.layer addSublayer:self.deaLayer]; 394 | } 395 | - (void)drawPresetKD 396 | { 397 | if (self.kLineLayer) { 398 | [self.kLineLayer removeFromSuperlayer]; 399 | } 400 | self.kLineLayer = [[KlineMALayer alloc] initQuotaDataArr:self.currentDisplayArray candleWidth:self.candleWidth candleSpace:self.candleSpace startIndex:self.leftPostion maxValue:self.quotaMaxAssert heightPerpoint:self.quotaHeightPerPoint totalHeight:self.scrollView.height *orignIndicatorOrignY lineWidth:1 lineColor:[UIColor yellowColor] wireType:KDJ_K klineSubOrMain:Sub]; 401 | [self.layer addSublayer:self.kLineLayer]; 402 | if (self.dLineLayer) { 403 | [self.dLineLayer removeFromSuperlayer]; 404 | } 405 | self.dLineLayer = [[KlineMALayer alloc] initQuotaDataArr:self.currentDisplayArray candleWidth:self.candleWidth candleSpace:self.candleSpace startIndex:self.leftPostion maxValue:self.quotaMaxAssert heightPerpoint:self.quotaHeightPerPoint totalHeight:self.scrollView.height *orignIndicatorOrignY lineWidth:1 lineColor:[UIColor orangeColor] wireType:KDJ_D klineSubOrMain:Sub]; 406 | [self.layer addSublayer:self.dLineLayer]; 407 | } 408 | - (void)drawPresetWR 409 | { 410 | if (self.wrLineLayer) { 411 | [self.wrLineLayer removeFromSuperlayer]; 412 | } 413 | self.wrLineLayer = [[KlineMALayer alloc] initQuotaDataArr:self.currentDisplayArray candleWidth:self.candleWidth candleSpace:self.candleSpace startIndex:self.leftPostion maxValue:self.quotaMaxAssert heightPerpoint:self.quotaHeightPerPoint totalHeight:self.scrollView.height *orignIndicatorOrignY lineWidth:1 lineColor:[UIColor orangeColor] wireType:KLINE_WR klineSubOrMain:Sub]; 414 | [self.layer addSublayer:self.wrLineLayer]; 415 | } 416 | - (void)calcuteQuotaMaxAssertAndMinAssert:(KlineSubKPIType)subType 417 | { 418 | NSDictionary *resultDic; 419 | CGFloat contentHeigh = self.scrollView.height * orignIndicatorOrignY - midDistance; 420 | if (subType == KlineSubKPITypeMACD) { 421 | NSMutableArray * DEAArray = [NSMutableArray array]; 422 | NSMutableArray * DIFArray = [NSMutableArray array]; 423 | NSMutableArray * MACDArray = [NSMutableArray array]; 424 | for (NSInteger i = 0;i