假使你们吗不甘让她一直重绘。Socket函数就是对之文件进行open-&gt

Animation.gif

一. 源:Socket起源于Unix,继承了open->write/read->close基本哲学;
二.
Socket是如出一辙栽特殊文件,Socket函数就是本着斯文件进行open->write/read->close操作;

github__ZXKline

//OC中运用函数的过程:socket->connect->send->recv->close

1.简介篇

  • 蜡烛图和山形图绘制切换
  • 5种指标绘制切换
  • 增长论蜡烛和指标线详情展示
  • 触底加载重多
  • 实时蜡烛绘制实现
  • 二级横屏和蜡烛三级横屏

fullScreen1.png

fullScreen2.png

  • 适配两栽布局

UI1.png

UI2.png

三. Socket函数(操作):{

2.原理篇

  1. 开辟操作:int socket(int domain, int type,int protocol):{
    a. 作用:创建一个socket,用来拓展读写操作;
    b. 参数1:domain协议域:AF_INET(IPV4网络支出);
    c. 参数2:type类型:SOCK_STREAM(TCP)/SOCK_DGRAM(UDP,报文);
    d. 参数3:protocol:协议;
    e. 返回值:>0的早晚表示成功;}
  2. 监听操作:”服务器端”bind()+listen()
  3. 接连操作:”客户端”int connect(int sockfd, const struct sockaddr
    *addr, socklen_t addrlen):{
    a. 作用:连接服务器;
    b. 参数1:要操作的socket;
    c. 参数2:一个结构体,包括’协议簇’/’端口号’/’IP地址'(网络通信三要素);
    d. 参数3:结构体数据长度(可写sizeof(addr))
    e. 返回值:0得逞/非零:错误编号}
    //客户端发送connect()后,服务器会采用accept()函数进行接受请求;
  4. 求操作:send(){
    a. 作用:向服务器发送请求;
    b. 参数1:要操作的socket;
    c. 参数2:发送的求信息;
    d. 参数3:发送内容长度;
    e. 参数4:发送方表明;
    f. 返回值:成功:返回发送的长短/失败:SOCKET_ERROR}
  5. 领响应:recv(){
    a. 作用:接受服务器发送过来的多寡;
    b. 参数1:客户端socket
    c. 参数2:接收内容缓冲区地址
    d. 参数3:接收内容缓存区长度
    e. 参数4:接收方式0表示阻塞必须等服务器返回数据
    f. 返回值:成功:返回读入的字节数/失败:返回SOCKET_ERROR}
  6. 闭馆操作:close()关闭指定的socket;
    }

2.1 tableView作为画布依耐

四.遥相呼应之老三破握手函数:{

怎么选了tableView

  • 品是否能针对绘制出candle的Cell进行复用;
  • 易个思想去轮子;
  1. 客户端:socket()创建->connect(阻塞)建立连接->发送syn = j包;
    2.
    服务器:socket()创建->Bind()给客户端socket赋值->listen()监听->Accpet(阻塞),接受请求->发送syn
    = K,ACK = J+1;
  2. 客户端:connect(返回)->发送ACK = K+1包;
  3. 服务器:Accpet(返回),连接起,可以起来通信;
    客户端:嘿.客户端.我修了单socket要连续,这是syn =
    k,能连的讲话你回复个ack呗;
    服务器:哦.那自己吗建一个socket,这是还你的ack = k+1,给您只syn =
    j,收到了转我一个ack哈;
    客户端:好哒,收到了了,给你ack = j + 1;
    客户端:么么哒可以开始连接了
    }

急需解决的题目:变纵向滚动为纵向滚动

旋转.png

  • 如图所示:在转动时,是绕tableView中心进行盘的,为了使旋转后的tableView的frame能够与superView的尺寸相同,那么快要使旋转前之tableView偏移一定去;

    .
    .
    self.tableView.transform = CGAffineTransformMakeRotation(-M_PI/2);
    .
    .
    [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(height);
        make.height.mas_equalTo(width);
    }];  
  • 利弊:虽然进行到尾,蜡烛都是故CAShapeLayer+UIBeizerPath绘制的,cell的复用并没有打及几近要命之图,并且旋转之后涉及到了tableView的x,y坐标在采用中的换(这点大家小心下),但是会发庆幸之是:使用了cell之后,在测算蜡烛横坐标的时即便是cell.indexPath.row*rowHeight;再者就是当缩放的时段,可以一直修改cell的冲天就足以直达缩放的目的;

五.对诺季差握手函数:{

2.2 缩放

  1. 客户端:Close()主动关闭->发送FIN = M包;
  2. 服务器:Read()返回零,被动关闭->发送ACK = M+1保;
  3. 服务器:Close()主动关闭->发送FIN = N包;
  4. 客户端:发送:ACK = N + 1包;
    }

缩放有度

- (void)pinchAction:(UIPinchGestureRecognizer *)sender
{ 
    static CGFloat oldScale = 1.0f;
    CGFloat difValue = sender.scale - oldScale;
    NSLog(@"difValue=====%f",difValue);
    NSLog(@"oldScale=====%f",oldScale);
    if (ABS(difValue)>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)控制缩放的共同体范围;

永恒缩放

//这句话达到让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)];

2.3 实现原理

母布局

些微单重点参数:

  • 屏幕被显得的首先个蜡烛图的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);  
    
  • 开收高低值从价转移成像素值

炬绘制

CAShapeLayer+UIBeizerPath

2.4 Socket数据结算

详见ZXSocketDataReformer
对服务器返回的数据格式:@”时间穿,实时价格”;我们用运用这一个个之数额好构建蜡烛模型;

  • 首先模型构建:假如同样分钟返回80独数据,
    那么我们得看清这无异于分钟开始的下,并且取出即同样分钟的第一独数据First,构建一个全新的模型A;模型A的开.收.高.低价都是第一数的实时价格;
  • 型替换:第一只模型构建之后,新的多寡Second到来,那么我们于得出高值和低值替换模型A的高低值,并且这模型A的收盘价为多少Second的实时价格;
  • 范结算(重点):
    结算:就是指向只M1\M5\M15..中回到的拥有数据好结算出一个蜡烛模型,也不怕是四独价值:开\收\高\低;
    结算的事件点判断方法:
    1)以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进行数据结算也够用了);

2.5 实时绘制

考虑如下情况:

实时绘制.png

代码大概是这样的 :

- (void)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—
(void)handleNewestCellWhenScrollToBottomWithNewKlineModel:(KlineModel
*)klineModel;打开注释的计,终结了它们;

3.使用篇

3.1 基本使用

  • 主导的k线图的通可以当demo中SecondStepViewController遭逢来看,运行需要于appDelegate中切换rootViewController;
  • JoinUpSocketViewController大凡连着socket实时绘制的demo,为了脱敏,控制器中的socket数据是任意产生的;
  • 切实的联网代码或者接口都可以在demo中看到,这里不开了多描述;

3.2 使用注意

3.2.1 历史数据转模型

(详见ReformerZXCandleDataReformer)
地方历史数据格式为:

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

相应的型转换格式为:

- (NSArray<KlineModel *>*)transformDataWithDataArr:(NSArray *)dataArr currentRequestType:(NSString *)currentRequestType
{
    self.currentRequestType = currentRequestType;
    //修改数据格式  →  ↓↓↓↓↓↓↓终点到啦↓↓↓↓↓↓↓↓↓  ←
    NSMutableArray *tempArr = [NSMutableArray array];
    __weak typeof(self) 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 = @(0);
        }

        model.x = idx;
        [tempArr addObject:model];
        model = nil;
    }];
    return tempArr;
}

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

3.2.2 Socket数据转模型

(详见ZXSocketDataReformer)
以socket结算的当儿,若需要服务器时间成socket返回的时光共同完成一个蜡烛的上,这里要转移吧获服务器时间;

- (void)requestServiceTime:(void(^)(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) {
    //
    //    }];

}

3.2.3 布局修改

(详见ZXHeader.h)

完整布局修改的几只巨大

/**
 * 价格坐标系在右边?YES->右边;NO->左边
 */
#define PriceCoordinateIsInRight YES     

/**
 * 蜡烛的信息配置的位置:YES->单独的view显示在view顶部;NO->弹框覆盖在蜡烛上
 */
#define IsDisplayCandelInfoInTop NO

约束

布局.png

  • 里头CandleChartHeight、QuotaChartHeight、MiddleBlankSpace都是可变的,所以分了横竖屏分别定义;其他尺寸还是定点的。
  • 是因为当其间就对一一控件的UI进行了组装,所以就留给了有关的尺寸约束或颜色宏,可以当ZXHeader文件中开展改动,如一旦发生非可知改改的处,就惟有去ZXAssemblyView.m文件被开展修改了;

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

3.2.4 横竖屏适配

稍稍技巧:因为自这里横屏之后是全屏并且隐藏了状态栏和导航栏的,为了旋转之后和竖屏的外控件互不干扰,可以将assenblyView实例补充加在self.view的太顶层,然后转过去下便一直将其它控件覆盖于脚

4 其他题材

  1. 有关历史k线和socket衔接处暂勿开展处理, 衔接还在误差;
  2. 未知bug?待挖掘;
  3. k线图UI很简单,除了k线没有其它定制,但是接口都是无微不至之,主要是觉得关乎UI部分我开得尤其少,通用性就更是强;
  4. 感谢Star;
  5. 发生其他其它题目欢迎Issues或简书留言;
  6. 超链:

  7. github地址ZXKline

  8. Json转模型Mac版ESJsonFormatForMac

相关文章