Charts: autoScaleMinMaxEnabled unstable

Created on 2 Jan 2018  ·  12Comments  ·  Source: danielgindi/Charts

Sorry if this issue duplicates any already opened or closed. I didn't find such issues.

Problem is with auto-scaling - it's unstable and representation of data has some problems. Here's a video of the problem.

https://drive.google.com/open?id=1Li2iWG6BXOR1XwIHM0gU0SV6cRxG0o3R

I will be grateful for any advice that could handle this problem with this pod.

bug

Most helpful comment

@wwwang89
just try
leftAxis.labelPosition = .insideChart
or
rightAxis.labelPosition = .insideChart

learn from https://github.com/danielgindi/Charts/issues/3145#issuecomment-355507291

All 12 comments

Can you provide more details? A reproducable example, what have you tried to debug, etc. (Stack Overflow rules).

@liuxuan30 Might already have experience with this.

it's known issue I remember, but complicated

Thank you for so quick response and sorry for late mine. I cannot share the copy of the project and do not have enough time to create a separate one.
I see that the problem is caused by loop rerendering with different length of Y label axes on each step of rendering (Max was 800(3) and 1000(4) in each state). Different label length makes the chart recalculate the amount of displayed bars and each step is unstable. I fixed that with custom YAxisValueFormatter. For people who will be going after me - here is my code:

//  YAxisValueFormatter.h
//  Copyright © 2018 Anton Komir. All rights reserved.
//

#import <UIKit/UIKit.h>
@import Charts;

@interface YAxisValueFormatter : NSObject <IChartAxisValueFormatter>

- (id)initForChart:(BarLineChartViewBase *)chart;

@end


//  YAxisValueFormatter.m
//  Copyright © 2018 Anton Komir. All rights reserved.
//

#import "YAxisValueFormatter.h"

@implementation YAxisValueFormatter
{
    NSArray *months;
    __weak BarLineChartViewBase *_chart;
}

- (id)initForChart:(BarLineChartViewBase *)chart
{
    self = [super init];
    if (self)
    {
        self->_chart = chart;
    }
    return self;
}

- (NSString *)stringForValue:(double)value
                        axis:(ChartAxisBase *)axis
{
    int maxLenght = 7;
    NSString *result = [NSString stringWithFormat:@"%i", (int)value];
    int valueLenght = [result length];
    int difference = maxLenght - valueLenght;
    for (int i = 0; i < difference; i++) {
        result = [NSString stringWithFormat:@" %@", result];
    }
    return result;
}

@end

I know that not the best solution.

Also, small comment - that bug reproduced also in the Android version of the library.

yes, that's why I said it's kind of complex, as iOS portion just reimplement what Android does. And for autoScaleMinMaxEnabled feature, we don't have people really look into it yet.

@liuxuan30 Are you able to make a ticket describing what exactly the issue is and I will investigate it.

let's aim at current swift and later take a look at this. I will create a project for it

@AntonNix not sure if you have resolved this already, but I was notifying a similar y-axis glitch when doing a real-time chart with many points requiring auto-scaling.

In previous version of my app, I had my update function as following

    func updateChart() {
        data?.notifyDataChanged()
        setVisibleXRange(minXRange: minXRange, maxXRange: maxXRange)
        self.notifyDataSetChanged()
        moveViewToX(Double(sets[0].entryCount) + xAxisOffset)
    }

After updating to the latest charts, I removed self.notifyDataSetChanged as I read somewhere @liuxuan30 mentioned that was no need to do this as data.notifyDataSetChanged should be sufficient. Turns out its not.

@liuxuan30 @danielgindi Hi ,not only my project met the problem, but also ChartsDemo-iOS met the same problem!When I scale and move one candle stick to the edge,the chart was blinking !
I guess the chart redraw went wrong !
in chart demo ,I record a video below:
Chart demo video.zip

Hope any help or reply,Thanks~

Thank you for so quick response and sorry for late mine. I cannot share the copy of the project and do not have enough time to create a separate one.
I see that the problem is caused by loop rerendering with different length of Y label axes on each step of rendering (Max was 800(3) and 1000(4) in each state). Different label length makes the chart recalculate the amount of displayed bars and each step is unstable. I fixed that with custom YAxisValueFormatter. For people who will be going after me - here is my code:

//  YAxisValueFormatter.h
//  Copyright © 2018 Anton Komir. All rights reserved.
//

#import <UIKit/UIKit.h>
@import Charts;

@interface YAxisValueFormatter : NSObject <IChartAxisValueFormatter>

- (id)initForChart:(BarLineChartViewBase *)chart;

@end


//  YAxisValueFormatter.m
//  Copyright © 2018 Anton Komir. All rights reserved.
//

#import "YAxisValueFormatter.h"

@implementation YAxisValueFormatter
{
    NSArray *months;
    __weak BarLineChartViewBase *_chart;
}

- (id)initForChart:(BarLineChartViewBase *)chart
{
    self = [super init];
    if (self)
    {
        self->_chart = chart;
    }
    return self;
}

- (NSString *)stringForValue:(double)value
                        axis:(ChartAxisBase *)axis
{
    int maxLenght = 7;
    NSString *result = [NSString stringWithFormat:@"%i", (int)value];
    int valueLenght = [result length];
    int difference = maxLenght - valueLenght;
    for (int i = 0; i < difference; i++) {
        result = [NSString stringWithFormat:@" %@", result];
    }
    return result;
}

@end

I know that not the best solution.

thank you!

@wwwang89
just try
leftAxis.labelPosition = .insideChart
or
rightAxis.labelPosition = .insideChart

learn from https://github.com/danielgindi/Charts/issues/3145#issuecomment-355507291

@wwwang89
just try
leftAxis.labelPosition = .insideChart
or
rightAxis.labelPosition = .insideChart

learn from #3145 (comment)

This is a beautiful and simple solution. Proves that the sizing of the chart labels is the problem.

I'll eventually look to keep the labels at a constant size but for now this is a quick fix, thanks.

It seems like setting the label formatter to constant length output would work only for monospaced font labels. For normal fonts it will probably work most of the time but it's not guaranteed...

Update: I have another fix which involves overriding the YAxis class. Unfortunately the yAxis in chart view is protected so I had to modify the library to make that public and settable. Works perfectly. While doing that I also found a way to increase scroll performance as label width calculations are concerned.

The base package is extremely inefficient as it runs all axis labels through a formatter, then finds the longest string, then uses that to calculate the required width. On a normal decimal axis as per default, however, the longest string is always the first or last element in the entries list so it needs to do that only once. Also the width only changes when there's a significant change in the order of magnitude of the axis range - applying all this, one can remove about 999 out of 1000 calls into string formatting and sizing. For all practical purposes the width is never recalculated during scrolling.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

newbiebie picture newbiebie  ·  3Comments

JW00332 picture JW00332  ·  4Comments

Bharati555 picture Bharati555  ·  4Comments

PrashantKT picture PrashantKT  ·  3Comments

anhltse03448 picture anhltse03448  ·  3Comments