arraysswiftcollectionskey-valueswiftcharts

Adding Elements to a KeyValuePair<X, Y>


I looked at this answer, and it appears the response may be "you can't get there, from here," but I have to try...

I am building a dynamic SwiftUI Charts generator, and need to figure out how to create legends, on the fly.

The chartForegroundStyleScale decorator takes a KeyValuePair, as an initializer argument, but it appears as if KeyValuePair can only be built, using literals. I need to be able to build the legend programmatically (hopefully, without having to manually create the views by hand, which will work, but is a pain).

Here's the code I have now, which DOES NOT work:

/* ##################################################### */
/**
 Returns a chart legend KeyValuePairs instance.
 */
var legend: KeyValuePairs<String, Color> {
    var dictionaryLiterals = [(String, Color)]()
    rows.forEach { $0.plottableData.forEach { dictionaryLiterals.append(($0.description, $0.color)) } }
    var ret = KeyValuePairs<String, Color>()
    dictionaryLiterals.forEach { ret.append($0) }
    return ret
}

Can I get there, from here?

EDITED TO ADD: Yeah, I know that there's issues with the code, but I'll burn that bridge, when I get to it. The sample is good enough to show what I'm trying to do. If I can't get this working, I'll be building a view, instead, which will be a different operation, entirely.


Solution

  • This has been an issue for me for a while now but reading this thread, I followed @david suggestion of chartForegroundStyleScale(domain:mapping:).

    This worked for me! (finally)

    The first part uses computed properties to combine the categories and the colors into their own matching arrays.

    let showRate: Bool
    let showRetries: Bool
    
    let rateLegend = ["Tx Rate": Color.blue.opacity(0.5), "Rx Rate": Color.gray.opacity(0.5)]
    let retriesLegend = ["Tx Retry Ratio": Color.mint.opacity(0.7), "Rx Retry Ratio": Color.purple]
    
    var categories: [String] {
        var combinedCategories: [String] = []
        if showRate { combinedCategories.append(contentsOf:  rateLegend.keys) }
        if showRetries { combinedCategories.append(contentsOf:  retriesLegend.keys) }
        return combinedCategories
    }
    
    var colors: [Color] {
        var combinedColors: [Color] = []
        if showRate { combinedColors.append(contentsOf: rateLegend.values) }
        if showRetries { combinedColors.append(contentsOf: retriesLegend.values) }
        return combinedColors
    }
    

    Then on the chart, the following modifier does the trick.

    .chartForegroundStyleScale(domain: categories, mapping: { category in
            if let index = categories.firstIndex(of: category) {
                return colors[index]
            } else {
                return .clear
            }
        }
    )