2016-08-09 47 views
2

Bibliothek, die ich verwende: charts
Ich habe Liniendiagramm mit Werten an bestimmten Tagen im Jahr. Ich möchte nicht jeden Tag als Label auf der x-Achse zeichnen (es kann mehr als 300 Tage reichen), also möchte ich nur Monate zeichnen. Im Moment sieht meine xVals wie folgt aus: [nil, nil, nil, "07/2016", nil nil nil (..)]. Leider wird kein Etikett angezeigt.
Gibt es eine andere Möglichkeit, das gewünschte Verhalten zu erreichen?

-Code (x-Achse-Werte):
So rendern Sie nur ausgewählte Achsenbeschriftungen in ios-Diagrammen

while date.compare(to) != NSComparisonResult.OrderedDescending { // iterate over all days in range 
     let components = currentCalendar.components([.Day, .Month , .Year], fromDate: date) 
     if components.day != 1 { 
      vals.append(nil) 
     } else { // first day of the month 
      vals.append(String(components.month) + "/" + String(components.year)) 
     } 

     let incrementComponents = NSDateComponents() 
     incrementComponents.day = 1 
     date = currentCalendar.dateByAddingComponents(incrementComponents, toDate: date, options: [])! 
} 

Und Einträge:

for point in base!.points { 
     if let index = chartValuesFormatter?.indexFromDate(point.date) { 
      entries.append(ChartDataEntry(value: point.value, xIndex: index)) 
     } 
} 

Im Allgemeinen funktioniert es, wenn ich alle Werte sind einige von ihnen angeben angezeigt und Einträge werden korrekt wiedergegeben. Mein einziges Problem ist, nur Monatsetiketten am Anfang von Monatszeiträumen anzuzeigen.

+0

nur den Monat setzen für die ganzen Wert, wird die lib approtiate Anzahl des Wertes auswählen – Tj3n

+0

zu zeigen, aber manchmal wird es im selben Monat mehr als einmal zeigen. Ich muss Monatsetiketten am 1. jedes Monats zeigen (oder weniger oft, wenn es zu viele Monate gibt - das sollte die Bibliothek selbst optimieren). –

+0

Können Sie bitte etwas Code einfügen, wie Sie die Daten erstellen. – Jassi

Antwort

0

Ok, also habe ich eine Lösung gefunden. Es ist nicht so einfach wie es sollte (für ein solches Grundproblem), ist aber sehr anpassbar. LineChartView (via BarLineChartViewBase) hat Eigenschaft xAxisRenderer. Sie können die Klasse ableiten, die die Standardeinstellung ist, und die Funktion drawLabels überschreiben. In meinem Fall habe ich ursprünglichen Code und geänderte Logik hinter Berechnung kopiert, welches Label und wo gezeichnet werden soll.

Bearbeiten: Meine benutzerdefinierten Renderer mit Überlappungslogik. Wahrscheinlich zu kompliziert, aber es funktioniert so weit.

class ChartXAxisDateRenderer: ChartXAxisRenderer { 
internal static let FDEG2RAD = CGFloat(M_PI/180.0) 

/// draws the x-labels on the specified y-position 
override func drawLabels(context context: CGContext, pos: CGFloat, anchor: CGPoint) { 

    guard let xAxis = xAxis else { return } 

    let paraStyle = NSParagraphStyle.defaultParagraphStyle().mutableCopy() as! NSMutableParagraphStyle 
    paraStyle.alignment = .Center 

    let labelAttrs = [NSFontAttributeName: xAxis.labelFont, 
         NSForegroundColorAttributeName: xAxis.labelTextColor, 
         NSParagraphStyleAttributeName: paraStyle] 
    let labelRotationAngleRadians = xAxis.labelRotationAngle * ChartXAxisDateRenderer.FDEG2RAD 

    let valueToPixelMatrix = transformer.valueToPixelMatrix 

    let minLabelsMargin: CGFloat = 4.0 

    var position = CGPoint(x: 0.0, y: 0.0) 

    var labelMaxSize = CGSize() 

    if (xAxis.isWordWrapEnabled) { 
     labelMaxSize.width = xAxis.wordWrapWidthPercent * valueToPixelMatrix.a 
    } 

    var positions = [CGPoint]() 
    var widths = [CGFloat]() 
    var labels = [String]() 
    var originalIndices = [Int]() 

    for i in 0...xAxis.values.count-1 { 
     let label = xAxis.values[i] 
     if (label == nil || label == "") 
     { 
      continue 
     } 

     originalIndices.append(i) 
     labels.append(label!) 

     position.x = CGFloat(i) 
     position.y = 0.0 
     position = CGPointApplyAffineTransform(position, valueToPixelMatrix) 
     positions.append(position) 

     let labelns = label! as NSString 
     let width = labelns.boundingRectWithSize(labelMaxSize, options: .UsesLineFragmentOrigin, attributes: labelAttrs, context: nil).size.width 
     widths.append(width) 
    } 

    let newIndices = findBestPositions(positions, widths: widths, margin: minLabelsMargin) 

    for index in newIndices { 
     let label = labels[index] 
     let position = positions[index] 
     let i = originalIndices[index] 

     if (viewPortHandler.isInBoundsX(position.x)) { 
      drawLabel(context: context, label: label, xIndex: i, x: position.x, y: pos, attributes: labelAttrs, constrainedToSize: labelMaxSize, anchor: anchor, angleRadians: labelRotationAngleRadians) 
     } 
    } 
} 

// Best position indices - minimum "n" without overlapping 
private func findBestPositions(positions: [CGPoint], widths: [CGFloat], margin: CGFloat) -> [Int] { 
    var n = 1 
    var overlap = true 

    // finding "n" 
    while n < widths.count && overlap { 
     overlap = doesOverlap(n, positions: positions, widths: widths, margin: margin) 
     if overlap { 
      n += 1 
     } 
    } 

    var newPositions = [Int]() 
    var i = 0 
    // create result indices 
    while i < positions.count { 
     newPositions.append(i) 
     i += n 
    } 

    return newPositions 
} 

// returns whether drawing only n-th labels will casue overlapping 
private func doesOverlap(n: Int, positions: [CGPoint], widths: [CGFloat], margin: CGFloat) -> Bool { 
    var i = 0 
    var newPositions = [CGPoint]() 
    var newWidths = [CGFloat]() 

    // getting only n-th records 
    while i < positions.count { 
     newPositions.append(positions[i]) 
     newWidths.append(widths[i]) 
     i += n 
    } 

    // overlap with next label checking 
    for j in 0...newPositions.count - 2 { 
     if newPositions[j].x + newWidths[j] + margin > newPositions[j+1].x { 
      return true 
     } 
    } 

    return false 
} 

}

+0

Entschuldigung für die Unannehmlichkeiten, aber ich habe das gleiche Problem, könnten Sie Ihren benutzerdefinierten Code posten? Vielen Dank! – user3745888

+0

@ user3745888 geschrieben. Beim meisten Code geht es darum, sich zu überlappen, aber Ruhe sollte ziemlich klar sein. Ich denke, es ist fehlerhaft und überkompliziert (es ist noch nicht gut getestet). Fühlen Sie sich frei, einen Kommentar zu hinterlassen. –