<p>Gráficos 3.0.1 - Alinhe x rótulos (datas) com gráficos</p>

Criado em 18 jan. 2017  ·  12Comentários  ·  Fonte: danielgindi/Charts

Estou tendo dificuldade para migrar Gráficos da versão 2 (Swift 2.3) para 3 (Swift 3).

Basicamente, não posso ter os rótulos x (datas) alinhados corretamente com os gráficos correspondentes.

Isso é o que eu tinha antes na versão 2:
https://i.stack.imgur.com/wOL3t.png

Na versão 2, eu tinha valores para os dias 7, 8, 10 e 11. Então estava faltando um dia no meio, mas os rótulos estavam corretamente alinhados com os gráficos.

E aqui está o que tenho na versão 3:
https://i.stack.imgur.com/arIL2.png

Na versão 3, os "rótulos" no eixo x agora foram substituídos por double (para datas, é um timeInterval desde 1970) e formatados por meio de um formatador. Então, indiscutivelmente, o gráfico está mais "correto" agora, já que o gráfico extrapola corretamente o valor para o 9º, mas não consigo descobrir como colocar os rótulos nos gráficos correspondentes.

Este é o meu código para o eixo x:

`` `
let chartView = LineChartView ()
...
deixe xAxis = chartView.xAxis
xAxis.labelPosition = .bottom
xAxis.labelCount = entries.count
xAxis.drawLabelsEnabled = true
xAxis.drawLimitLinesBehindDataEnabled = true
xAxis.avoidFirstLastClippingEnabled = true

// Defina o formatador de data dos valores x
let xValuesNumberFormatter = ChartXAxisFormatter ()
xValuesNumberFormatter.dateFormatter = dayNumberAndShortNameFormatter // por exemplo, "wed 26"
xAxis.valueFormatter = xValuesNumberFormatter

Here is the ChartXAxisFormatter class I created:

import Foundation
importar gráficos

classe ChartXAxisFormatter: NSObject {
var dateFormatter: DateFormatter?
}

extensão ChartXAxisFormatter: IAxisValueFormatter {

func stringForValue(_ value: Double, axis: AxisBase?) -> String {
    if let dateFormatter = dateFormatter {

        let date = Date(timeIntervalSince1970: value)
        return dateFormatter.string(from: date)
    }

    return ""
}

}
`` `
Então, os valores aqui estão corretos, a formatação está correta, o formato do gráfico está correto, mas o alinhamento dos rótulos com os gráficos correspondentes não é bom.

Obrigado pela ajuda

Comentários muito úteis

Corri o mesmo problema usando carimbos de data / hora antes e isso é causado pelo que está sendo renderizado no eixo x é um carimbo de data / hora interpolado. Isso basicamente significa que os carimbos de data / hora resultantes nos rótulos que usam um dateFormatter _não são os carimbos de data / hora para suas pontuações_, fazendo com que os rótulos não correspondam.

Por exemplo, vamos supor que você tenha valores x [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]. O renderizador do eixo x pode, na verdade, imprimir 2,5 como um rótulo tentando distribuir uniformemente os rótulos no eixo x, mesmo que esse valor x específico _não_ esteja em seu conjunto de dados. Se você considerar que está usando carimbos de data / hora, isso significa que dateFormatter pode formatar algum carimbo de data / hora intermediário como uma data. Conforme você formata por dia, isso resulta em seus rótulos não corresponderem às suas pontuações, embora o rótulo esteja correto (já que você basicamente o arredonda por dia). Se você formatar por _dia hora: minuto_ , verá que o rótulo é de fato uma data interpolada.

Resolvi o problema recorrendo ao uso de índices de matriz em vez de usar carimbos de data / hora para o eixo x e usando um formatador de eixo x personalizado:

import Foundation
import Charts

class MyCustomBottomAxisFormatter: NSObject, IAxisValueFormatter {
    private var scores: [MyScoreObject]?

    lazy private var dateFormatter: DateFormatter = {
        // set up date formatter using locale
        let dateFormatter = DateFormatter()
        dateFormatter.locale = Locale.current
        dateFormatter.dateStyle = .short
        dateFormatter.timeStyle = .none
        return dateFormatter
    }()

    convenience init(usingScores scores: [MyScoreObject]) {
        self.init()
        self.scores = scores
    }

    func stringForValue(_ value: Double, axis: AxisBase?) -> String {
        let index = Int(value)

        guard let scores = scores, index < scores.count, let date = scores[index].date else {
            return "?"
        }

        return dateFormatter.string(from: date)
    }
}

Todos 12 comentários

Corri o mesmo problema usando carimbos de data / hora antes e isso é causado pelo que está sendo renderizado no eixo x é um carimbo de data / hora interpolado. Isso basicamente significa que os carimbos de data / hora resultantes nos rótulos que usam um dateFormatter _não são os carimbos de data / hora para suas pontuações_, fazendo com que os rótulos não correspondam.

Por exemplo, vamos supor que você tenha valores x [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]. O renderizador do eixo x pode, na verdade, imprimir 2,5 como um rótulo tentando distribuir uniformemente os rótulos no eixo x, mesmo que esse valor x específico _não_ esteja em seu conjunto de dados. Se você considerar que está usando carimbos de data / hora, isso significa que dateFormatter pode formatar algum carimbo de data / hora intermediário como uma data. Conforme você formata por dia, isso resulta em seus rótulos não corresponderem às suas pontuações, embora o rótulo esteja correto (já que você basicamente o arredonda por dia). Se você formatar por _dia hora: minuto_ , verá que o rótulo é de fato uma data interpolada.

Resolvi o problema recorrendo ao uso de índices de matriz em vez de usar carimbos de data / hora para o eixo x e usando um formatador de eixo x personalizado:

import Foundation
import Charts

class MyCustomBottomAxisFormatter: NSObject, IAxisValueFormatter {
    private var scores: [MyScoreObject]?

    lazy private var dateFormatter: DateFormatter = {
        // set up date formatter using locale
        let dateFormatter = DateFormatter()
        dateFormatter.locale = Locale.current
        dateFormatter.dateStyle = .short
        dateFormatter.timeStyle = .none
        return dateFormatter
    }()

    convenience init(usingScores scores: [MyScoreObject]) {
        self.init()
        self.scores = scores
    }

    func stringForValue(_ value: Double, axis: AxisBase?) -> String {
        let index = Int(value)

        guard let scores = scores, index < scores.count, let date = scores[index].date else {
            return "?"
        }

        return dateFormatter.string(from: date)
    }
}

Muito obrigado pela sua resposta!
E a explicação muito clara sobre os carimbos de data / hora sendo interpolados, realmente faz sentido.
Vou tentar sua solução e informá-lo.

Obrigado!

@ 4np Eu acho que você tem que definir seu array de ChartDataEntry com xValues ​​como índices também, em vez de intervalos de tempo, certo?
Igual a :

// Define chart yValues
        var entries = [ChartDataEntry]()
        for (index, score) in scores.enumerated() {
            let yValue = score.value
            let entry = ChartDataEntry(x: Double(index), y: yValue)
            entries.append(entry)
        }

@ 4np OK, funcionou! Meu gráfico agora é o mesmo da versão 2.

Mas, eu tive que adicionar mais 2 coisas ao meu xAxis:

  // Show the limit lines behind each plot
  xAxis.drawLimitLinesBehindDataEnabled = true

  // Make sure that only 1 x-label per index is shown
  xAxis.granularityEnabled = true
  xAxis.granularity = 1

Obrigado pela ajuda.
Além disso, abri um problema no StackOverflow lá:
http://stackoverflow.com/questions/41720445/ios-charts-3-0-align-x-labels-dates-with-plots

Se você tem uma conta SO e deseja copiar / colar sua resposta lá, ficarei feliz em aceitar sua resposta lá também.

Obrigado
Fred

Além disso, deixei esse problema em aberto até que os desenvolvedores de gráficos tenham a chance de examiná-lo e talvez apresentem uma maneira de escolher como os rótulos devem ser alinhados.

Porque agora tenho que escolher entre 2 soluções ruins:

  • use intervalos de tempo como xValues ​​(que é o espírito no qual o DataEntry agora deve ser construído), mas com rótulos desalinhados,
  • ou use índices como xValues ​​(que é uma forma de reverter para Gráficos 2.0), mas com o risco de que índices consecutivos possam representar dias não consecutivos (como no meu exemplo).

Por favor deixe nos saber o que você pensa.

@ frederic-adda

Consegui resolver o problema
XAxis.granularity = 1.0 deve ser um dia. isso é o mais importante
A menor data deve ser 0

(TimeInSeconds - miniDate) / (3600,0 * 24,0)
capture d ecran 2017-01-30 a 12 35 22

E todo o truque está no formatador
Fazemos a operação oposta para recuperar a data
Let date2 = Date (timeIntervalSince1970: (value * 3600 * 24) + miniTime)
Retornar dateFormatter.string (de: data2)
não esqueça
DateFormatter.timeZone = NSTimeZone (abreviação: "GMT + 0: 00") como TimeZone!
A demonstração é macOS
O link é https://github.com/thierryH91200/Charts-log
Guia Line Date

@ thierryH91200 Obrigado Thierry!
Vou tentar implementar sua solução imediatamente.

Funcionou! Fantástico!
Obrigado pela ajuda!

Legal @ thierryH91200 , parece mesmo o caminho a seguir 👍

Isso não funciona para mim

@frederic-adda encontrou a solução para o seu problema ??
na verdade, tenho problemas ao mostrar datas no X-Axis. Estou mostrando datas no eixo X definindo o valor X de ChartDataEntry como TimeInterval, mas o gráfico não mostra os dados corretamente. Ao definir o valor X de ChartDataEntry como índice de matriz, mostra os dados.

Aqui está meu código para definir o valor X de ChartDataEntry como TimeInterval
para (int i = 0; i <self.temperatureUpdates.count; i ++)
{
SFTemperatureUpdate * temperatureUpdate = [self.temperatureUpdates obje ctAtIndex: i ];
double yVal = [temperatureUpdate.value floatValue];
NSTimeInterval timeInterval = [temperatureUpdate.careatedAt timeIntervalSince1970];
double xVal = timeInterval;
[yVals1 addObject: [[ChartDataEntry alloc] initWithX: xVal y: yVal]];

}

dummydata
Uploading OriginalData.png…

Tentando fazer as soluções acima funcionarem ao usar vários LineChartDataSets em um gráfico, alguém conseguiu fazê-lo funcionar com sucesso?

Estou tendo problemas para encontrar uma solução quando um conjunto de dados pode ser diferente em tamanho em comparação com o outro.
ie

let x = LineChartDataSet(values: values1, label: "test 1") // count 5
let y = LineChartDataSet(values: values2, label: "test 2") // count 8
Esta página foi útil?
0 / 5 - 0 avaliações