美文网首页
贝塞尔曲线画简单柱状图的封装

贝塞尔曲线画简单柱状图的封装

作者: 小冰山口 | 来源:发表于2019-01-18 23:56 被阅读0次

    本人有若干成套学习视频, 可试看! 可试看! 可试看, 重要的事情说三遍 包含Java, 数据结构与算法, iOS, 安卓, python, flutter等等, 如有需要, 联系微信tsaievan.

    最近项目里面有个需求, 需要画券商十大买卖和券商持股比例

    十大券商买卖 券商持股比例

    当拿到这个需求的时候, 我就分析了, 其实这两张图是一样的, 可以划归为一个模型:

    需求分析 需求分析

    这个模型里面就包含三个元素, 券商名称, 线, 以及数字, 不一样的就是自己所处的位置, 而这个位置是由在数组中的不同顺序决定的, 那么, 简单来说, 封装好了的话, 只需要将我从网络中请求的数据, 转换成我刚才分析的模型, 然后用模型去画各自的图就行了.

    服务器的数据结构是这样的:

    <__NSArrayM 0x1c1846e10>(
    <__NSArrayM 0x1c564f990>(
    海通国际,
    20180709,
    1097290574,
    42.01
    )
    ,
    <__NSArrayM 0x1c564fba0>(
    华融国际,
    20180709,
    618740000,
    23.69
    )
    ,
    <__NSArrayM 0x1c564fc60>(
    农银国际,
    20180709,
    147109559,
    5.63
    )
    ,
    <__NSArrayM 0x1c564f960>(
    渣打银行,
    20180709,
    127472467,
    4.88
    )
    ,
    <__NSArrayM 0x1c525c1d0>(
    汇丰银行,
    20180709,
    115639000,
    4.42
    )
    ,
    <__NSArrayM 0x1c5440ba0>(
    广发证券,
    20180709,
    62630000,
    2.39
    )
    ,
    <__NSArrayM 0x1c5440ed0>(
    工银亚洲,
    20180709,
    60700000,
    2.32
    )
    ,
    <__NSArrayM 0x1c525c710>(
    中信里昂,
    20180709,
    51934000,
    1.98
    )
    ,
    <__NSArrayM 0x1c54407e0>(
    中国金融,
    20180709,
    41360584,
    1.58
    )
    ,
    <__NSArrayM 0x1c525cef0>(
    招商证券,
    20180709,
    39683000,
    1.51
    )
    ,
    <__NSArrayM 0x1c5440840>(
    兴证国际,
    20180709,
    31383000,
    1.2
    )
    ,
    <__NSArrayM 0x1c5440b10>(
    长江证券,
    20180709,
    27525000,
    1.05
    )
    ,
    <__NSArrayM 0x1c54407b0>(
    建银国际,
    20180709,
    16153000,
    0.61
    )
    ,
    <__NSArrayM 0x1c564f570>(
    中信建投,
    20180709,
    12329000,
    0.47
    )
    ,
    <__NSArrayM 0x1c525c350>(
    申万宏源,
    20180709,
    10000000,
    0.38
    )
    ,
    <__NSArrayM 0x1c525d130>(
    中泰证券,
    20180709,
    9752000,
    0.37
    )
    ,
    <__NSArrayM 0x1c5440f00>(
    UBS HK,
    20180709,
    9400645,
    0.35
    )
    ,
    <__NSArrayM 0x1c525c7d0>(
    太平证券,
    20180709,
    9005000,
    0.34
    )
    ,
    <__NSArrayM 0x1c165b000>(
    银河国际,
    20180709,
    7724000,
    0.29
    )
    ,
    <__NSArrayM 0x1c1847920>(
    德意志银行,
    20180709,
    6570736,
    0.25
    )
    ,
    <__NSArrayM 0x1c1847a10>(
    花旗银行,
    20180709,
    5066000,
    0.19
    )
    ,
    <__NSArrayM 0x1c1853650>(
    摩根大通银行,
    20180709,
    3576000,
    0.13
    )
    ,
    <__NSArrayM 0x1c1a464b0>(
    巴黎证券,
    20180709,
    3052264,
    0.11
    )
    ,
    <__NSArrayM 0x1c165ad90>(
    中信证券,
    20180709,
    2710000,
    0.1
    )
    ,
    <__NSArrayM 0x1c165adc0>(
    国泰君安,
    20180709,
    2339000,
    0.08
    )
    ,
    <__NSArrayM 0x1c165ad60>(
    越秀证券,
    20180709,
    1528000,
    0.05
    )
    ,
    <__NSArrayM 0x1c165acd0>(
    汇丰金融,
    20180709,
    1390000,
    0.05
    )
    ,
    <__NSArrayM 0x1c1658d80>(
    中银香港,
    20180709,
    1076000,
    0.04
    )
    ,
    <__NSArrayM 0x1c165ab80>(
    永隆银行,
    20180709,
    1062000,
    0.04
    )
    ,
    <__NSArrayM 0x1c165ad00>(
    英皇证券,
    20180709,
    331000,
    0.01
    )
    ,
    <__NSArrayM 0x1c1658ed0>(
    工银亚洲,
    20180709,
    313000,
    0.01
    )
    ,
    <__NSArrayM 0x1c0e52d80>(
    中银国际,
    20180709,
    257000,
    0
    )
    ,
    <__NSArrayM 0x1c1843d20>(
    Merrill,
    20180709,
    254000,
    0
    )
    ,
    <__NSArrayM 0x1c1848100>(
    招商银行,
    20180709,
    229000,
    0
    )
    ,
    <__NSArrayM 0x1c1656410>(
    元大证券,
    20180709,
    204000,
    0
    )
    ,
    <__NSArrayM 0x1c0c5afd0>(
    新鸿基,
    20180709,
    91000,
    0
    )
    ,
    <__NSArrayM 0x1c1845df0>(
    高盛亚洲,
    20180709,
    74000,
    0
    )
    ,
    <__NSArrayM 0x1c0e4fea0>(
    世博证券,
    20180709,
    59000,
    0
    )
    ,
    <__NSArrayM 0x1c0e52ff0>(
    恒生证券,
    20180709,
    55000,
    0
    )
    ,
    <__NSArrayM 0x1c18449e0>(
    耀才证券,
    20180709,
    46000,
    0
    )
    ,
    <__NSArrayM 0x1c165a220>(
    东亚证券,
    20180709,
    37000,
    0
    )
    ,
    <__NSArrayM 0x1c0e49240>(
    一通投资,
    20180709,
    32000,
    0
    )
    ,
    <__NSArrayM 0x1c1846cf0>(
    南洋商业银行,
    20180709,
    30000,
    0
    )
    ,
    <__NSArrayM 0x1c1846750>(
    结好证券,
    20180709,
    30000,
    0
    )
    ,
    <__NSArrayM 0x1c18468a0>(
    工银国际,
    20180709,
    30000,
    0
    )
    ,
    <__NSArrayM 0x1c18467e0>(
    交银国际,
    20180709,
    29000,
    0
    )
    ,
    <__NSArrayM 0x1c12593b0>(
    永丰金,
    20180709,
    22000,
    0
    )
    ,
    <__NSArrayM 0x1c1846c90>(
    交银信托,
    20180709,
    20000,
    0
    )
    ,
    <__NSArrayM 0x1c1848220>(
    汇信理财,
    20180709,
    16000,
    0
    )
    ,
    <__NSArrayM 0x1c1846960>(
    摩根香港,
    20180709,
    14000,
    0
    )
    
    )
    

    首先将这个转换成一个模型数组:

                    for (NSArray *holdingInfo in list) {
                        TGHoldingStockModel *holdingStockModel = [[TGHoldingStockModel alloc] initWithDataArray:holdingInfo];
                        if (holdingStockModel != nil) {
                            [mtArr addObject:holdingStockModel];
                        }
                    }
    

    但是这个模型并不是最终画图的模型, 根据我们上面的分析, 画图我分割成了一个一个的单元, 这些单元包含了券商名称, 线, 数字这些元素, 而画图还需要知道的是字体大小, 字的颜色, 线宽, 线所处的位置, 长度, 等等信息, 我们必须将这个模型进行深加工, 转换成最终画图的单元, 即另一个模型:

    - (id <TGPath>)initWithHoldingStockModel:(TGHoldingStockModel *)holdingStockModel index:(NSInteger)index {
        if (self = [super init]) {
            self.strokeColor = TG_HEX(0x3F8FFF);
            self.name = holdingStockModel.brokerName;
            self.lineWidth = 8.0f;
            float holdingRatio = [holdingStockModel.holdingRatio floatValue];
            self.numberString = [NSString stringWithFormat:@"%.2f%%", holdingRatio];
            self.stringAttr = @{
                                NSFontAttributeName : [UIFont systemFontOfSize:12],
                                NSForegroundColorAttributeName : TGTrackContentTextColor
                                };
            self.nameSize = [self.name sizeWithAttributes:self.stringAttr];
            self.numberSize = [self.numberString sizeWithAttributes:self.stringAttr];
            self.pathX = 15;
            CGFloat maxEndPoint = TG_SCREEN_WIDTH - 15 - self.numberSize.width - 5;
            CGFloat maxVolumeWidth = maxEndPoint - self.pathX;
            if (index == 0) { ///< 最大的成交量
                [TGPathToolShared setHoldingStockPathRatio:maxVolumeWidth / holdingRatio];
            }
            self.columnWidth = holdingRatio * [TGPathToolShared holdingStockPathRatio];
            self.pathY = 30 + index * 40;
            self.pathEndX = self.pathX + self.columnWidth;
            self.pathEndY = self.pathY;
            self.nameRect = CGRectMake(self.pathX, self.pathY - self.lineWidth - self.nameSize.height, self.nameSize.width, self.nameSize.height);
            self.numberRect = CGRectMake(self.pathX + self.columnWidth + 5, self.pathY - self.lineWidth, self.numberSize.width, self.numberSize.height);
        }
        return self;
    }
    

    以上就是转换过程, 这个地方我用了一个TGPath的协议, 这个协议就是包含了这样一些属性:

    @protocol TGPath <NSObject>
    
    ///< 画笔颜色
    @property (nonatomic, strong) UIColor *strokeColor;
    
    ///< 券商名称
    @property (nonatomic, copy) NSString *name;
    
    ///< 数字
    @property (nonatomic, copy) NSString *numberString;
    
    ///< 线宽
    @property (nonatomic, assign) CGFloat lineWidth;
    
    ///< 线的起始X值
    @property (nonatomic, assign) CGFloat pathX;
    
    ///< 线的起始Y值
    @property (nonatomic, assign) CGFloat pathY;
    
    ///< 线的终止X值
    @property (nonatomic, assign) CGFloat pathEndX;
    
    ///< 线的终止Y值
    @property (nonatomic, assign) CGFloat pathEndY;
    
    ///< 名字的大小
    @property (nonatomic, assign) CGSize nameSize;
    
    ///< 数字的大小
    @property (nonatomic, assign) CGSize numberSize;
    
    ///< 柱子的长度
    @property (nonatomic, assign) CGFloat columnWidth;
    
    ///< 字符串的
    @property (nonatomic, strong) NSDictionary *stringAttr;
    
    ///< 名字的rect
    @property (nonatomic, assign) CGRect nameRect;
    
    ///< 数字的rect
    @property (nonatomic, assign) CGRect numberRect;
    
    @end
    

    这些东西就基本上包含了画出这样一个单元所需的所有元素, 只要是遵守了这个协议的任何模型, 都可以用来作为参数, 传入画图的方法, 画图也就是最后最简单的事情了:

            [self.response.m_list enumerateObjectsUsingBlock:^(TGHoldingStockModel * _Nonnull ratioModel, NSUInteger idx, BOOL * _Nonnull stop) {
                id <TGPath> path = [[TGHoldingStockPath alloc] initWithHoldingStockModel:ratioModel index:idx];
                [TGPathToolShared drawPath:path];
            }];
    

    思路就是, 遍历模型数组, 将模型转换为最终的画图单元模型, 然后用统一的方法画图:

    - (void)drawPath:(id <TGPath>)path {
        [path.strokeColor setStroke];
        UIBezierPath *ratioPath = [UIBezierPath bezierPath];
        ratioPath.lineWidth = path.lineWidth;
        [ratioPath moveToPoint:CGPointMake(path.pathX, path.pathY)];
        [ratioPath addLineToPoint:CGPointMake(path.pathEndX, path.pathEndY)];
        [ratioPath stroke];
        [path.name drawInRect:path.nameRect withAttributes:path.stringAttr];
        [path.numberString drawInRect:path.numberRect withAttributes:path.stringAttr];
    }
    

    这里我推荐用贝塞尔曲线, 而不是CG框架, 因为我认为贝塞尔曲线更加面向对象, 更加方便理解. 事实上我在github上看到了很多的优秀的画图的开源框架, 都写的很不错, 封装的很优雅, 巧妙, 运用了多种设计模式, 这些都是值得我学习的地方.

    PS. 本人有若干成套学习视频, 包含Java, 数据结构与算法, iOS, 安卓, python, flutter等等, 如有需要, 联系微信tsaievan.

    相关文章

      网友评论

          本文标题:贝塞尔曲线画简单柱状图的封装

          本文链接:https://www.haomeiwen.com/subject/vnowkqtx.html