<p>Diagramme 3.0.1 - x-Beschriftungen (Daten) an Diagrammen ausrichten</p>

Erstellt am 18. Jan. 2017  ·  12Kommentare  ·  Quelle: danielgindi/Charts

Es fällt mir schwer, Charts von Version 2 (Swift 2.3) auf 3 (Swift 3) zu migrieren.

Grundsätzlich kann ich die x-Beschriftungen (Daten) nicht richtig mit den entsprechenden Plots ausrichten.

Das hatte ich vorher in Version 2:
https://i.stack.imgur.com/wOL3t.png

In Version 2 hatte ich Werte für die Tage 7, 8, 10 und 11. Es fehlte also ein Tag in der Mitte, aber die Beschriftungen waren korrekt mit den Plots ausgerichtet.

Und das habe ich in Version 3:
https://i.stack.imgur.com/arIL2.png

In Version 3 wurden die "Labels" in der x-Achse jetzt durch double ersetzt (bei Datumsangaben ist es seit 1970 ein timeInterval) und über einen Formatierer formatiert. Der Graph ist also unbestreitbar "korrekter", da das Diagramm den Wert für den 9. richtig extrapoliert, aber ich kann nicht finden, wie man die Beschriftungen unter den entsprechenden Diagrammen platziert.

Dies ist mein Code für die x-Achse:

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

// Datumsformatierer für x-Werte einstellen
let xValuesNumberFormatter = ChartXAxisFormatter()
xValuesNumberFormatter.dateFormatter = dayNumberAndShortNameFormatter // zB "wed 26"
xAxis.valueFormatter = xValuesNumberFormatter

Here is the ChartXAxisFormatter class I created:

Stiftung importieren
Diagramme importieren

Klasse ChartXAxisFormatter: NSObject {
var dateFormatter: DateFormatter?
}

Erweiterung ChartXAxisFormatter: IAxisValueFormatter {

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

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

    return ""
}

}
```
Die Werte hier sind also korrekt, die Formatierung ist korrekt, die Form des Diagramms ist korrekt, aber die Ausrichtung der Beschriftungen mit den entsprechenden Diagrammen ist nicht gut.

Danke für Ihre Hilfe

Hilfreichster Kommentar

Ich habe das gleiche Problem zuvor mit Zeitstempeln ausgeführt und es wird dadurch verursacht, dass auf der x-Achse ein interpolierter Zeitstempel gerendert wird. Dies bedeutet im Grunde, dass die Zeitstempel, die zu den Labels führen, die einen dateFormatter verwenden, _nicht die Zeitstempel für Ihre Ergebnisse sind_, was dazu führt, dass die Labels nicht übereinstimmen.

Angenommen, Sie haben x-Werte [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]. Der x-Achsen-Renderer druckt möglicherweise 2.5 tatsächlich als Beschriftung, um die Beschriftungen gleichmäßig auf der x-Achse zu verteilen, obwohl dieser bestimmte x-Wert in Ihrem Dataset _nicht_ ist. Wenn Sie dann davon ausgehen, dass Sie Zeitstempel verwenden, bedeutet dies, dass der dateFormatter möglicherweise einen dazwischenliegenden Zeitstempel als Datum formatiert. Wenn Sie nach Tag formatieren, führt dies dazu, dass Ihre Labels nicht mit Ihren Scores übereinstimmen, obwohl das Label korrekt ist (da Sie es grundsätzlich nach Tag abrunden). Wenn Sie nach _Tag Stunde:Minute_ formatieren, sehen Sie, dass das Label tatsächlich ein interpoliertes Datum ist.

Ich habe das Problem auf meiner Seite gelöst, indem ich auf Array-Indizes zurückgegriffen habe, anstatt Zeitstempel für die x-Achse zu verwenden und einen benutzerdefinierten X-Achsen-Formatierer zu verwenden:

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)
    }
}

Alle 12 Kommentare

Ich habe das gleiche Problem zuvor mit Zeitstempeln ausgeführt und es wird dadurch verursacht, dass auf der x-Achse ein interpolierter Zeitstempel gerendert wird. Dies bedeutet im Grunde, dass die Zeitstempel, die zu den Labels führen, die einen dateFormatter verwenden, _nicht die Zeitstempel für Ihre Ergebnisse sind_, was dazu führt, dass die Labels nicht übereinstimmen.

Angenommen, Sie haben x-Werte [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]. Der x-Achsen-Renderer druckt möglicherweise 2.5 tatsächlich als Beschriftung, um die Beschriftungen gleichmäßig auf der x-Achse zu verteilen, obwohl dieser bestimmte x-Wert in Ihrem Dataset _nicht_ ist. Wenn Sie dann davon ausgehen, dass Sie Zeitstempel verwenden, bedeutet dies, dass der dateFormatter möglicherweise einen dazwischenliegenden Zeitstempel als Datum formatiert. Wenn Sie nach Tag formatieren, führt dies dazu, dass Ihre Labels nicht mit Ihren Scores übereinstimmen, obwohl das Label korrekt ist (da Sie es grundsätzlich nach Tag abrunden). Wenn Sie nach _Tag Stunde:Minute_ formatieren, sehen Sie, dass das Label tatsächlich ein interpoliertes Datum ist.

Ich habe das Problem auf meiner Seite gelöst, indem ich auf Array-Indizes zurückgegriffen habe, anstatt Zeitstempel für die x-Achse zu verwenden und einen benutzerdefinierten X-Achsen-Formatierer zu verwenden:

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)
    }
}

Vielen Dank für Ihre Antwort!
Und die sehr klare Erklärung, dass die Zeitstempel interpoliert werden, macht wirklich Sinn.
Ich werde deine Lösung ausprobieren und dir Bescheid geben.

Vielen Dank!

@4np Ich denke, dass Sie dann Ihr Array von ChartDataEntry auch mit xValues ​​als Indizes anstelle von Zeitintervallen definieren müssen, oder?
Wie so:

// 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, das hat funktioniert! Mein Diagramm ist jetzt das gleiche wie in Version 2.

Aber ich musste meiner xAxis noch 2 weitere Dinge hinzufügen:

  // 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

Danke für Ihre Hilfe.
Außerdem habe ich dort ein Problem bei StackOverflow eröffnet:
http://stackoverflow.com/questions/41720445/ios-charts-3-0-align-x-labels-dates-with-plots

Wenn Sie einen SO-Account haben und Ihre Antwort dort kopieren / einfügen möchten, nehme ich Ihre Antwort auch dort gerne entgegen.

Vielen Dank
Fred

Außerdem habe ich dieses Problem offen gelassen, bis die Charts-Entwickler die Möglichkeit haben, es sich anzusehen und vielleicht eine Möglichkeit vorzustellen, wie die Beschriftungen ausgerichtet werden sollen.

Denn ich muss mich jetzt zwischen 2 schlechten Lösungen entscheiden:

  • entweder Zeitintervalle als xValues ​​verwenden (das ist der Geist, in dem der DataEntry jetzt konstruiert werden sollte), aber mit falsch ausgerichteten Labels,
  • oder verwenden Sie Indizes als xValues ​​(was eine Möglichkeit ist, zu Charts 2.0 zurückzukehren), jedoch mit dem Risiko, dass aufeinanderfolgende Indizes nicht aufeinanderfolgende Tage darstellen (wie in meinem Beispiel).

Bitte teilen Sie uns Ihre Meinung mit.

@frederic-adda

Ich habe es geschafft das Problem zu lösen
XAxis.granularity = 1.0 muss ein Tag sein. das ist das wichtigste
Das kleinste Datum muss 0 sein

(TimeInSeconds - miniDate) / (3600.0 * 24.0)
capture d ecran 2017-01-30 a 12 35 22

Und der Trick liegt im Formatierer
Wir machen den umgekehrten Vorgang, um das Datum abzurufen
Sei date2 = Date (timeIntervalSence1970: (Wert * 3600 * 24) + miniTime)
Rückgabe dateFormatter.string (von: date2)
vergessen Sie nicht
DateFormatter.timeZone = NSTimeZone (Abkürzung: "GMT + 0:00") als TimeZone!
Die Demo ist macOS
Der Link ist https://github.com/thierryH91200/Charts-log
Registerkarte Zeilendatum

@thierryH91200 Danke Thierry!
Ich werde versuchen, Ihre Lösung sofort umzusetzen.

Es funktionierte! Fantastisch!
Danke für Ihre Hilfe!

Cool @thierryH91200 , das scheint tatsächlich der richtige Weg zu sein 👍

Das funktioniert bei mir nicht

@frederic-adda hast du die Lösung für dein Problem gefunden??
Tatsächlich habe ich ein Problem beim Anzeigen von Daten auf der X-Achse. Ich zeige Datumsangaben auf der X-Achse an, indem ich den X-Wert von ChartDataEntry als TimeInterval festlege, aber dann zeigt das Diagramm die Daten nicht richtig an. Beim Festlegen des X-Werts von ChartDataEntry als Array-Index werden die Daten angezeigt.

Hier ist mein Code zum Festlegen des X-Werts von ChartDataEntry als TimeInterval
for (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…

Hat jemand versucht, die oben genannten Lösungen bei Verwendung mehrerer LineChartDataSets in einem Diagramm zum Laufen zu bringen, konnte sie erfolgreich zum Laufen gebracht werden?

Ich habe Probleme, eine Lösung zu finden, wenn sich ein Dataset im Vergleich zum anderen in der Größe unterscheiden kann.
dh

let x = LineChartDataSet(values: values1, label: "test 1") // count 5
let y = LineChartDataSet(values: values2, label: "test 2") // count 8
War diese Seite hilfreich?
0 / 5 - 0 Bewertungen

Verwandte Themen

coop44483 picture coop44483  ·  3Kommentare

deepumukundan picture deepumukundan  ·  3Kommentare

newbiebie picture newbiebie  ·  3Kommentare

cilasgimenez picture cilasgimenez  ·  4Kommentare

valeIT picture valeIT  ·  3Kommentare