Blog / 阅读

如何实现动态图表曲线?可以动态切换曲线,使用UICollectionView

by admin on 2014-03-25 12:23:27 in ,



需要实现的效果:

1、头部的月份部分要能够根据滑动的位置显示。即使一个月中的一天在显示区域,月份也要显示出来。如果整屏都是同一个月的,那么月份居中显示。准确的说,月份的Label要显示在所在区域的居中位置。

2、切换数据源(dataSource)的时候要动态调整曲线。


github地址:https://github.com/ganlv/GLLineChartView

详细说明:

1、自定义UICollectionView
从iOS6开始,苹果引入了UICollectionView。功能非常强大,可以用户自定义布局。自定义UICollectionView 只需要自己实现一个UICollectionViewLayout即可。具体的可以看苹果的官方文档:
https://developer.apple.com/library/ios/documentation/WindowsViews/Conceptual/CollectionViewPGforIOS/Introduction/Introduction.html


大概是需要实现几个方法:


[objc] view plaincopy
- (void)prepareLayout ; //初始化,这个会最先被执行  
  
- (CGSize)collectionViewContentSize;//确定contentSize,CollectionView本质上还是一个UIScrollView  
  
- (NSArray*)layoutAttributesForElementsInRect:(CGRect)rect;//最关键的需要实现的方法  
  
//cell用的layout属性  
- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath;  
//头部区域的Layout  
- (UICollectionViewLayoutAttributes *)preLayoutAttributesForSupplementaryViewOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath;  


这里就不说明所有方法的实现了,说说核心方法的实现:


prepareLayout的实现是为了保证能够通过位置信息快速的定位到indexPath,这样能够快速的获得一个区域里面的indexPaths。


[objc] view plaincopy
-(void)prepareLayout  
{  
    [super prepareLayout];  
    _indexDictionary = [NSMutableDictionary dictionary];  
    NSInteger index  =  0;  
    for (NSInteger section = 0;section < [self.collectionView numberOfSections] ; section ++) {  
        for (NSInteger item = 0 ; item < [self.collectionView numberOfItemsInSection:section]; item++) {  
            NSIndexPath *indexPath = [NSIndexPath indexPathForItem:item inSection:section];  
            [_indexDictionary setObject:indexPath forKey:[NSNumber numberWithInteger:index]];  
            index++;  
        }  
    }  
}  


-(NSArray*)layoutAttributesForElementsInRect:(CGRect)rect;
这个是最核心需要实现的方法,获得指定区域内的UICollectionViewLayoutAttribute。这里需要注意的是,由于我们的Header是动态的。每次滚动的时候都需要调整LayoutAttribute。这里需要先指定:




[objc] view plaincopy
- (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds  
{  
    return YES;  
}  


下面是layoutAttributesForElementsInRect:这里其实分3个部分:
① 确定需要处理的indexPath
② 确定Header的Attribute
③ 确定Cell的Attribute


[objc] view plaincopy
-(NSArray*)layoutAttributesForElementsInRect:(CGRect)rect  
{  
    NSMutableArray *attrs = [NSMutableArray array];  
    //start offset  
    CGFloat startXOffset = rect.origin.x;  
    NSInteger startIndex = startXOffset/[self cellWidth] -1;  
    if(startIndex < 0){  
        startIndex = 0;  
    }  
    //end offset  
    CGFloat endXOffset = rect.origin.x + rect.size.width;  
    NSInteger endIndex= endXOffset/[self cellWidth];  
    if(endXOffset < [self.collectionView contentSize].width){  
         endIndex = endIndex + 1;  
    }  
  
    //for update index  
    for (NSInteger index  = startIndex; index <= endIndex  ;  ++index) {  
        NSIndexPath *indexPath = [_indexDictionary objectForKey:[NSNumber numberWithInteger:index]];  
        if(indexPath == nil){  
            continue;  
        }  
        if(indexPath.item == 0 || index == startIndex){  
            UICollectionViewLayoutAttributes *sectionAttr = [self layoutAttributesForSupplementaryViewOfKind:GLCollectionViewLayoutTop atIndexPath:indexPath];  
            [attrs addObject:sectionAttr];  
        }  
        UICollectionViewLayoutAttributes *itemAttr = [self layoutAttributesForItemAtIndexPath:indexPath];  
        [attrs addObject:itemAttr];  
    }  
    return attrs;  
}  


2、让曲线能够动态的变化
先说明原理,整个iOS的animation是由一个animation service来完成的。可以用Core Animation来控制位置的移动。但是在我们这个曲线中,无法用Core Animation来实现。Core Animation只能控制简单的位置、旋转以及缩放等。
但是在我们这个Case里面,曲线是由3个点控制,且在变换的过程中,上下变换的方向和大小是不一样的,所以就不能用简单的CoreAnimation来实现了。这里的解决方案可以是CADisplayLink,CADisplayLink的效果是每一帧执行一次。需要指定动画的时间,根据动画的时间来确定每一帧的运动距离,达到动画结束时间的时候就可以调用[CADisaplayLink invalidate];来完成动画。代码框架如下:


[objc] view plaincopy
-(void)setTargetChartLine:(GLChartLine)achartLine  targetOldChartLine:(GLChartLine)aoldChartLine animate:(BOOL)animate  
{  
    if(animate && (chartLine.startPoint != 0 || chartLine.endPoint != 0 || chartLine.mainPoint !=0) ){  
        _targetChartLine = achartLine;  
        _startChartLine = chartLine;  
        _startDate = [NSDate date];  
        _oldTargetChartLine = aoldChartLine;  
        _oldStartChartLine =oldChartLine;  
  
        CADisplayLink *_display =[CADisplayLink displayLinkWithTarget:self selector:@selector(animateChartLine:)];  
        [_display addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];  
    }else{  
        chartLine = achartLine;  
        oldChartLine = aoldChartLine;  
        [self setNeedsDisplay];  
    }  
}  
  
-(void)animateChartLine:(CADisplayLink *)sender  
{  
    NSTimeInterval timeDuration = 0.3;//300毫秒  
    NSTimeInterval duration = [[NSDate date] timeIntervalSinceDate:_startDate];  
    if(duration >= timeDuration){  
        chartLine = _targetChartLine;  
        oldChartLine = _oldTargetChartLine;  
        [self setNeedsDisplay];  
        [sender invalidate];  
        return;  
    }  
    CGFloat nowStart = _startChartLine.startPoint + (duration/timeDuration)*(_targetChartLine.startPoint - _startChartLine.startPoint);  
    CGFloat nowMain = _startChartLine.mainPoint + (duration/timeDuration)*(_targetChartLine.mainPoint - _startChartLine.mainPoint);  
    CGFloat endMain = _startChartLine.endPoint + (duration/timeDuration)*(_targetChartLine.endPoint - _startChartLine.endPoint);  
    chartLine = GLChartLineMake(nowStart, nowMain, endMain);  
  
    CGFloat oldNowStart = _oldStartChartLine.startPoint + (duration/timeDuration)*(_oldTargetChartLine.startPoint - _oldStartChartLine.startPoint);  
    CGFloat oldNowMain = _oldStartChartLine.mainPoint + (duration/timeDuration)*(_oldTargetChartLine.mainPoint - _oldStartChartLine.mainPoint);  
    CGFloat oldEndMain = _oldStartChartLine.endPoint + (duration/timeDuration)*(_oldTargetChartLine.endPoint - _oldStartChartLine.endPoint);  
    oldChartLine = GLChartLineMake(oldNowStart, oldNowMain, oldEndMain);  
  
    [self setNeedsDisplay];  
}  


每次确定好了Cell的3个点之后,就调用一次needDisplay。然后系统会执行drawInContext方法,在drawInContext里面处理具体的绘图逻辑,只是需要使用CoreGraph的画图API即可,具体的可以去看github上的代码。
github地址:https://github.com/ganlv/GLLineChartView
转载自:http://www.ganlvji.com/dynamic_chart/


写评论

相关文章

上一篇:android错误之OnTouchListener只能监听到ACTION_DOWN-----onTouchListener的返回值问题

下一篇:Installation error: INSTALL_PARSE_FAILED_NO_CER...

评论

写评论

* 必填.

分享

栏目

赞助商


热门文章

Tag 云