新浦京81707con > 首页 > K线三方库__ZXKline,tableView之提前计算行高

原标题:K线三方库__ZXKline,tableView之提前计算行高

浏览次数:54 时间:2020-04-27

3.2.2 Socket数据转模型

(详见ZXSocketDataReformer卡塔尔在socket买单的时候,若供给服务器时间结合socket重临的日子共同达成多少个蜡烛的时候,这里需求改为得到服务器时间;

- requestServiceTime:(NSInteger timesamp))success{ //这里Demo使用的本地时间代替;正确的应该取下面的服务器时间 NSDate *date = [NSDate dateWithTimeIntervalSinceNow:0]; NSTimeInterval timestamp = [date timeIntervalSince1970]; success(timestamp); //获取服务器时间 // NSString *urlStr = @"服务器时间校对地址"; // // self.manager.responseSerializer = [AFHTTPResponseSerializer serializer]; // self.manager.responseSerializer.acceptableContentTypes = [self.manager.responseSerializer.acceptableContentTypes setByAddingObject:@"text/html"]; // [self.manager GET:urlStr parameters:nil progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) { // // NSString *time = [[NSString alloc] initWithData:responseObject encoding:NSUTF8StringEncoding]; // success([time integerValue]); // // NSLog(@"ServiceTime=%@",time); // // } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) { // // }]; }

再次安装缩放比例x_scale

前言:

tableView的优化大约正是瞬两种:

  • 1.cell在那之中的层级结构尽量的少 - 使用drawRect画
  • 2.cell里面的数据尽量提前计划好 尽量少的实时总计
  • MVVM的优势 提前管理model,cell直接得到结尾需求的多少
  • 3.有着的控件大小提前总计好,不要每三回都再度计算
  • 4.缓存tableView行高
  • 因为:利用自动布局计算行高很开销cpu,每趟滚动到该cell都要计算.
  • 5.用到cpu去异步绘制cell的layer
  • layer.drawsAsynchronously = true
  • 以此代码能够使layer 提前绘制
  • 6.栅格化 将cel内容渲染成一张图片,在滚动的时候便是一张图纸
  • layer.shouldRasterize = true
  • self.layer.rasterizationScale = UIScreen.main.scale 暗中认可是1x的 设置成荧屏的缩放比例就能够了
  • 7.按需加载 -- 依照客商滚动的快慢去加载哪个cell

本篇随笔讲一下缓存cell行高

3.2.4 横竖屏适配

小手艺:因为本人那边横屏之后是全屏何况隐瞒了状态栏和导航栏的,为了旋转之后和竖屏的其他控件互不干扰,能够将assenblyView实例增添在self.view的最顶层,然后旋转过去过后就径直将其他控件覆盖在底层

  1. 至于历史k线和socket衔接处暂未开展管理, 衔接还留存截断误差;
  2. 未知bug?待挖掘;
  3. k线图UI很简短,除了k线未有任何定制,不过接口都是统筹的,重假若感到关乎UI部分笔者做得越少,通用性就越高;
  4. 感谢Star;
  5. 有其余此外标题接待Issues或许简书留言;
  6. 超链:
  • github地址ZXKline
  • Json转模型Mac版ESJsonFormatForMac

大家定义这一个类专门项目标和外侧人机联作的动作(并不定义更改类变动的动作State of Qatar;

宗意在此以前的饶舌:

最开头的时候tableView的行高我们是让tableView本身依据内容去总括的

//实现这两句代码
tableView.rowHeight = UITableViewAutomaticDimension;
tableView.estimatedRowHeight = 100;
//然后布局cell的时候为最后一个控件设置底部约束 == cell的底部

短处正是使用活动构造计算行高很费用cpu,每一次滚动到该cell都要总计.

  • 蜡烛图和山形图绘制切换
  • 5种目标绘制切换
  • 长按蜡烛和目的线详细的情况体现
  • 触底加载越来越多
  • 实时蜡烛绘制实现
  • 二级横屏和蜡烛三级横屏

1.基本图;

正题:

图片 1

demoPic.png

缓存行高原理其实正是提前成立叁个cell,为其赋值model后将计算好的行高存起来备用

首先为模型加上多个本性 用来囤积行高

@interface Status : NSObject
/**
  这里是其他属性
*/

/**rowHeight  - 存储计算好的行高*/
@property (nonatomic,assign) CGFloat rowHeight;
@end

//重写cell的model属性的set方法  -- 每次的cellForRow都会重新赋值  cell要显示的各种属性
- (void)setModel:(Status *)model {
    _model = model;
    [self.iconView sd_setImageWithURL:[NSURL URLWithString:model.user.profile_image_url]];
    self.nameLabel.text = model.user.name;
    self.sourceLabel.text = model.source;
    self.createdLabel.text = model.created_at;
    self.contentLabel.text = model.text;
}

在cell中添加一个计算行高的方法
- (CGFloat)getMaxY {

    [self layoutIfNeeded];//强制更新约束 让cell子控件重新计算frame

    return CGRectGetMaxY(self.contentLabel.frame)   margin; //这里就是返回的cell的行高了
}

@property(nonatomic,strong) NSArray <Status *>*datas;//这里存着数据
//实现tableVIew的这个代理  返回该模型下的cell的行高
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
    return self.datas[indexPath.row].rowHeight;
}

顺便说一句
cell构造方法重写的是那一个

- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
    if (self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]) {
        [self setupUI];
    }
    return self;
}

setupUI 里各类创造控件和masonry布局详细情况见demo

测算行高:

for (Status *model in self.datas) {
//这个cell只是用来计算 所以随便创建
  ARStatusCell *cell = [[ARStatusCell alloc] initWithStyle:(UITableViewCellStyleDefault) reuseIdentifier:nil];
  NSLog(@"cell's frame = %@",NSStringFromCGRect(cell.frame));
  cell.frame = CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width, 1000);
  cell.model = model;
  model.rowHeight = [cell getMaxY];
}
//

为何要改cell得frame呢?

Pre_CalculateRowHeightDamo[10935:2085237] -[ARHomeViewController loadData] [Line 50] cell's frame = {{0, 0}, {320, 44}}
//初始化的cell的frame 宽度默认是320  所以要改成屏幕的宽度 高度随便改

demo地址:https://github.com/AnnieAri/Pre_CalculateRowHeightDemo

只是简短的落举办高的一种总括 复杂的ui 也得以兑现,笔者在大家的品类中也是运用这种情势.

图片 2

demoPic2.png

  • 适配二种布局

4.缩放效果;

图片 3fullScreen1.png图片 4fullScreen2.png

图片 5

内需解决的主题素材:变纵向滚动为纵向滚动

图片 6旋转.png

  • 如图所示:在旋转时,是绕tableView中央实行旋转的,为了使旋转后的tableView的frame能够和superView的轻重缓急相通,那么将在使旋转前的tableView偏移一定间隔;
 . . self.tableView.transform = CGAffineTransformMakeRotation; . . [self.view addSubview:self.tableView]; . . [self.tableView mas_updateConstraints:^(MASConstraintMaker *make) { make.left.mas_equalTo((width-height)/2); make.top.mas_equalTo(-(width-height)/2); make.width.mas_equalTo; make.height.mas_equalTo; }]; 
  • 利弊:就算进行到背后,蜡烛全部是用CAShapeLayer UIBeizerPath绘制的,cell的复用并从未起到多大的功用,而且旋转之后涉及到了tableView的x,y坐标在使用中的转变,但是能认为庆幸的是:使用了cell之后,在测算蜡烛横坐标的时候就是cell.indexPath.row*rowHeight;再者正是在缩放的时候,可以从来改换cell的万丈就足以高达缩放的目标;

//总计self.h种种点代表的值是某些

3.2.3 结构修正

(详见ZXHeader.h)

/** * 价格坐标系在右边?YES->右边;NO->左边 */#define PriceCoordinateIsInRight YES /** * 蜡烛的信息配置的位置:YES->单独的view显示在view顶部;NO->弹框覆盖在蜡烛上 */#define IsDisplayCandelInfoInTop NO

图片 7布局.png

  • 此中CandleChartHeight、QuotaChartHeight、MiddleBlankSpace都以可变的,所以分了横竖屏分别定义;其余尺寸都以一定的。
  • 出于在内部就对各种控件的UI进行了组装,所以就留给了有关的尺码约束依旧颜色宏,能够在ZXHeader文件中张开改动,即便有不能够修正之处,就只有去ZXAssemblyView.m文件中开展更改了;

从某种角度上来说,很多约束可以不改,但是宏中的TotalHeight必须根据项目需求进行修改

上篇文章中,大家询问了增势图k线图绘制及的某些关键点,基于我们都懂期货(Futures卡塔尔国期货(Futures卡塔尔(قطر‎那么些事物的底工上来说解的,说的也远远不够完美,那篇小说我们以实战为主,一步一步写出能够商业化的k线图。

缩放有度

- pinchAction:(UIPinchGestureRecognizer *)sender{ static CGFloat oldScale = 1.0f; CGFloat difValue = sender.scale - oldScale; NSLog(@"difValue=====%f",difValue); NSLog(@"oldScale=====%f",oldScale); if (ABS>StockChartScaleBound) { CGFloat oldKlineWidth = self.candleWidth; // NSLog(@"原来的index%ld",oldNeedDrawStartIndex); self.candleWidth = oldKlineWidth * ((difValue > 0) ? (1 StockChartScaleFactor):(1-StockChartScaleFactor)); oldScale = sender.scale; if (self.candleWidth < scale_MinValue) { self.candleWidth = scale_MinValue; }else if (self.candleWidth > scale_MaxValue) { self.candleWidth = scale_MaxValue; } }}
  • 在历次缩放的时候,实行判别:1卡塔尔唯有触及的缩放大于某些预定值的时候才开展缩放2)调整每趟缩放的比值;3)调控缩放的完好范围;

- (CAShapeLayer *)GetShapeLayerFromModel:(KlineModel *)model Index:(NSInteger)idx

缘何接受了tableView

  • 品味是不是能对绘制有candle的Cell进行复用;
  • 换个观念造轮子;

因为插入图片之后变得特别卡没有办法打字 所以不插入代码了 作者会把代码放在GitHub里面; 我们能够交到优化版本!

蜡烛绘制

CAShapeLayer UIBeizerPath

详见ZXSocketDataReformer 针对服务器重回的数量格式:@"时间戳,实时价格";大家必要动用那贰个个的数码本身营造蜡烛模型;

  • 先是模子打造:假使一秒钟再次回到八十个数据, 那么大家需求看清这一分钟开首的时候,并且抽出这一分钟的率先个数据First,塑造三个全新的模型A;模型A的开.收.高.实惠都是第一数码的实时价格;
  • 模型替换:第叁个模型塑造之后,新的数额Second到来,那么大家比较得出高值和低值替换模型A的高低值,并且这个时候模型A的收盘价为数据Second的实时价格;
  • 模型买下账单:结账:正是对个M1M5M15..中回到的兼具数据自身结账出七个蜡烛模型,也正是八个值:开收高低;结账的平地风波点判定方法:1State of Qatar以socket再次回到数据的时刻戳结账:那样买单在多少上不会有哪些绝对误差,可是日子上会有相对误差; eg:针对M1来讲,倘诺在6'58''的时候回来此分蜡烛的末段二个值,如果用socket的年华作为买单的话,那么大家必需等到下贰个socket再次来到值的大运戳到来本事买下账单,要是socket在7'00''-7'01''之间回到了数量以来,很好,大家能够直接买单上三个蜡烛,並且及时的始建叁个新的蜡烛模型;不过数量而不是每趟都会扭转如此反复,如若下一个多少的赶到是7'16'';那么中间那18'',k线图会静止18'',那么一定于6'的老大蜡烛会延迟16''进行推动,便招致了岁月上的引用误差;何况当数码涨到封顶也许股票停牌的时候,socket数据还未有改换,便不会回来数据,那么那几个日子k线图也是不会有其它动作;2卡塔尔国以需要服务器时间戳结账:会导致数据上的绝对误差;eg:在7'00''需求付账,但是这么些时刻socket在7'00''的时候回来了八个数据,可是付钱的时候只会取到此中一个数额作为6'的收盘价,其他数据将残存到下个蜡烛;息灭:1卡塔尔国以socket和服务器的年华戳相结合的章程开展买下账单:笔者在ZXSocketDataReformer中也是这么做的,第一回呼吁服务器时间,然后本地安装计时器进行服务器时间同步; 由socket时间戳进行模型布局,到了整点,优先socket举行模型推动,如若整点的时候从不socket再次回到,就由服务器时间展开带动;2卡塔尔国放大计时器由服务器创设,最佳就是在整点延迟1秒的时候,如若在00''-01''的时候已经有socket数据传送到活动端的话,那么就无需推送假数据,若无socket数据爆发,就推送四个假数据到移动端,告诉移动端,数据须求张开付钱,移动端只必要用socket进行付钱; (好呢,自个儿都绕晕了,假若要求不是那么高其实只是遵照socket举行数据结账也够用了卡塔尔(قطر‎;

设想如下情况:

图片 8实时绘制.png

代码大概是那样的 :

- handleNewestCellWhenScrollToBottomWithNewKlineModel:(KlineModel *)klineModel{ //==0的时候需要插入一个新的cell;否则只需要刷新最后一个cell if (self.isNew) { KlineModel *newsDataModel = [self calulatePositionWithKlineModel:klineModel]; [self.kLineModelArr addObject:newsDataModel]; double oldMax = self.maxAssert; double oldMin = self.minAssert; [self calculateNeedDrawKlineArr]; [self calculateMaxAndMinValueWithNeedDrawArr:self.needDrawKlineArr]; //不等的话就重绘 if (oldMax<self.maxAssert||oldMin>self.minAssert) { dispatch_async(dispatch_get_main_queue(), ^{ [self.tableView setContentOffset:CGPointMake(0, (self.kLineModelArr.count-self.needDrawKlineCount)*self.candleWidth (self.needDrawKlineCount*self.candleWidth-self.subViewWidth))]; }); [self drawTopKline]; }else{ //否则就插入 NSIndexPath *indexPath = [NSIndexPath indexPathForRow:self.kLineModelArr.count-1 inSection:0]; dispatch_async(dispatch_get_main_queue(), ^{ //先增加 再偏移 [self.tableView beginUpdates]; [self.tableView insertRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone]; [self.tableView endUpdates]; [self.tableView setContentOffset:CGPointMake(0, (self.kLineModelArr.count-self.needDrawKlineCount)*self.candleWidth (self.needDrawKlineCount*self.candleWidth-self.subViewWidth))]; }); [self delegateToReturnKlieArr]; } }else{ KlineModel *newsDataModel = [self calulatePositionWithKlineModel:klineModel]; NSIndexPath *indexPath = [NSIndexPath indexPathForRow:self.kLineModelArr.count-1 inSection:0]; [self.kLineModelArr replaceObjectAtIndex:self.kLineModelArr.count-1 withObject:newsDataModel]; CGFloat oldMax = self.maxAssert; CGFloat oldMin = self.minAssert; [self calculateNeedDrawKlineArr]; [self calculateMaxAndMinValueWithNeedDrawArr:self.needDrawKlineArr]; //如果计算出来的最新的极值不在上一次计算的极值直接的话就重绘,否则就刷新最后一个即可 if (oldMax<self.maxAssert||oldMin>self.minAssert) { [self drawTopKline]; }else{ dispatch_async(dispatch_get_main_queue(), ^{ [self.tableView beginUpdates]; [self.tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone]; [self.tableView endUpdates]; [self delegateToReturnKlieArr]; }); } }}

其实使用进程中在insert只怕reloadrows的时候,不经常会出现崩溃,临时尚未清除,索性改为了直接重绘全屏了,假如你们也不敢后人让它间接重绘,可到--ZXMainView.m--- handleNewestCellWhenScrollToBottomWithNewKlineModel:(KlineModel *卡塔尔klineModel;打开注释的法子,终结了它;

  • 焦点的k线图的联网能够在demo中SecondStepViewController中看到,运维需在appDelegate中切换rootViewController;
  • JoinUpSocketViewController是连着socket实时绘制的demo,为了脱敏,调控器中的socket数据是随意产生的;
  • 切实的联网代码大概接口都得以在demo中看见,这里不做过多描述;

//通过self.isShowTrackingCross决断是或不是出示十字光标 不然带动新的多寡

3.2.1 历史数据转模型

(详见Reformer---ZXCandleDataReformer卡塔尔国当地历史数据格式为:

/* @[@"时间戳,收盘价,开盘价,最高价,最低价,成交量", @"时间戳,收盘价,开盘价,最高价,最低价,成交量", @"时间戳,收盘价,开盘价,最高价,最低价,成交量", @"...", @"..."]; */ 

对应的模子调换格式为:

- (NSArray<KlineModel *>*)transformDataWithDataArr:(NSArray *)dataArr currentRequestType:(NSString *)currentRequestType{ self.currentRequestType = currentRequestType; //修改数据格式 → ↓↓↓↓↓↓↓终点到啦↓↓↓↓↓↓↓↓↓ ← NSMutableArray *tempArr = [NSMutableArray array]; __weak typeof weakSelf = self; [dataArr enumerateObjectsUsingBlock:^(NSString *dataStr, NSUInteger idx, BOOL * _Nonnull stop) { NSArray *strArr = [dataStr componentsSeparatedByString:@","]; KlineModel *model = [KlineModel new]; model.timestamp = [strArr[0] integerValue]; model.timeStr = [weakSelf setTime:strArr[0]]; model.closePrice = [strArr[1] doubleValue]; model.openPrice = [strArr[2] doubleValue]; model.highestPrice = [strArr[3] doubleValue]; model.lowestPrice = [strArr[4] doubleValue]; if (strArr.count>=6) { model.volumn = @([strArr[5] doubleValue]); }else{ model.volumn = @; } model.x = idx; [tempArr addObject:model]; model = nil; }]; return tempArr;}

历史数据模型转换需要使用者根据请求历史数据的实际格式进行转换;

//替换最终叁个点 逻辑是剔除self.ShapeLayer的最终三个视图 再通过model生成那么些岗位对应的火炬图 增添到父layer

微观布局

  • 显示屏中显得的率先个蜡烛图的X坐标:

    NSUInteger leftArrCount = ABS(scrollViewOffsetX/self.candleWidth);_needDrawStartIndex = leftArrCount; 
    
  • 显示器中能够显得的蜡烛个数:

     - (NSInteger)needDrawKlineCount{ CGFloat width = self.subViewWidth; _needDrawKlineCount = ceil(width/self.candleWidth); return _needDrawKlineCount;} 
    

    基于那三个参数,起源和长短,就足以从数量源数组中精确的抽取当前显示屏呈现的蜡烛图的数码;然后滑动进程中实时计算并开展坐标转变

  • 极值:从当下显示器彰显的数量源数组获得的最大值和最小值

  • 单位价格所表示的像素值

     self.heightPerPoint = self.candleChartHeight/(self.maxAssert-self.minAssert); 
    
  • 开收高低值从价格转移成像素值

3.十字光标,联合浮动数据(显示对应的蜡烛 分时State of Qatar;

稳定缩放

//这句话达到让tableview在缩放的时候能够保持缩放中心点不变;//实现原理:在放大缩小的时候,计算出变化后和变化前中心点的距离,然后为了保持中心点的偏移值始终保持不变,就直接在原来的偏移上加减变换的距离//ceil(centerPoint.y/oldKlineWidth)中心点前面的cell个数//self.rowHeight-oldKlineWidth每个cell的高度的变化CGFloat pinchOffsetY = ceil(centerPoint.y/oldKlineWidth)*(self.candleWidth-oldKlineWidth) oldNeedDrawStartPointY;if (pinchOffsetY<0) { pinchOffsetY = 0;}if (pinchOffsetY self.subViewWidth>self.kLineModelArr.count*self.candleWidth) { pinchOffsetY = self.kLineModelArr.count*self.candleWidth - self.subViewWidth;}[self.tableView setContentOffset:CGPointMake(0, pinchOffsetY)];

6.若是还应该有哪些效果与利益能够做的?;

图片 9Animation.gif

绘图类:

图片 10UI1.png图片 11UI2.png

self.OffsetIndex = index;//初阶化偏移地方

图片 12

咱俩先定义好每八个蜡烛图的幅度 间距 和每一遍的偏移量

倘诺point.x >0 减去偏移量 展现以前的数码 为0的时候显得self.OffsetIndex开首的数额 否则展现之后的多少。

图片 13那个都以绘制k线图须求的数量,除了那么些之外还会有此外部供给要体现给外部的数码(买卖价买卖量时间等)

5.横屏功力;

数据类:必要数据的类,缓存数据或然网络数据;用delegate,当model类被放出数据尚未出生的时候也得以马上放出这么些model类。

//遍历获取展现数据的最高最低值

1.我们的数码是以json数组形式推送过来的。所以我们要有多个数据类,数据转model类,然后用那几个model去和制图类做人机联作及展现; 当然商业用的不单单那么粗略,起码你还得加个总计类总计每秒钟的变化趋势增多到那么些model类里面转产生为可用model,也等于60s内的第一手交换最后贰个蜡烛图,60s过了,那一个要把它步向那么些蜡烛数据(也等于增创二个蜡烛)这里能够相称每组数据推送过来的时刻和数组的终极一个元素的时日差是还是不是高于60s;后台推送过来的数量都以这里已经计算好的每分钟数据;

现今幼功的和外界人机联作的API已经鲜明下来了;我们能够初阶绘图了!

本文由新浦京81707con发布于首页,转载请注明出处:K线三方库__ZXKline,tableView之提前计算行高

关键词: 新浦京81707con 三方 __ZXKline 线图 走势图

上一篇:了不起的WebRTC,使用QUIC协议实现实时视频直播

下一篇:没有了