Don_Mag icon

Example Chart - Swift

Don_Mag | PRO | 06/08/23 06:34:38 PM UTC (Edited) | 0 ⭐ | 8398 👁️ | Never ⏰ | []
Swift |

7.57 KB

|

None

|

0 👍

/

0 👎

// example chart view
 
class BarView: UIView {
    var colors: [UIColor] = []
    var values: [CGFloat] = []
    var hConstraints: [NSLayoutConstraint] = []
 
    public func configureBar(maxVal: CGFloat, values: [CGFloat], colors: [UIColor]) {
        guard values.count == colors.count else {
            fatalError("Values count does not match Colors count!")
        }
        subviews.forEach { v in
            v.removeFromSuperview()
        }
        hConstraints = []
        for _ in 0..<values.count {
            let seg = UIView()
            seg.translatesAutoresizingMaskIntoConstraints = false
            addSubview(seg)
            NSLayoutConstraint.activate([
                seg.leadingAnchor.constraint(equalTo: leadingAnchor),
                seg.trailingAnchor.constraint(equalTo: trailingAnchor),
                seg.bottomAnchor.constraint(equalTo: bottomAnchor),
            ])
            hConstraints.append(seg.heightAnchor.constraint(equalToConstant: 0))
        }
        for (seg, c) in zip(subviews, colors) {
            seg.backgroundColor = c
        }
        var curPct: CGFloat = 0
        for (i, v) in values.enumerated() {
            hConstraints[i].isActive = false
            let seg = subviews[i]
            curPct += v
            hConstraints[i] = seg.heightAnchor.constraint(equalTo: heightAnchor, multiplier: curPct)
            hConstraints[i].isActive = true
        }
        subviews.reversed().forEach { v in
            addSubview(v)
        }
    }
    override init(frame: CGRect) {
        super.init(frame: frame)
        commonInit()
    }
    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        commonInit()
    }
    private func commonInit() {
        layer.cornerRadius = 12
        clipsToBounds = true
    }
    
}
class LegendDotView: UIView {
    override func layoutSubviews() {
        super.layoutSubviews()
        layer.cornerRadius = bounds.width * 0.5
    }
}
class StarterChartView: UIView {
    var colors: [UIColor] = [.systemRed, .systemYellow, .systemGreen]
    var values: [[CGFloat]] = []
    
    let barsStackView = UIStackView()
    var barTopLabels: [UILabel] = []
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        commonInit()
    }
    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        commonInit()
    }
    private func commonInit() {
        backgroundColor = .black
        
        let stackView = UIStackView()
        stackView.axis = .vertical
        stackView.distribution = .equalCentering
        stackView.translatesAutoresizingMaskIntoConstraints = false
        addSubview(stackView)
        NSLayoutConstraint.activate([
            stackView.topAnchor.constraint(equalTo: topAnchor, constant: 40.0),
            stackView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 20.0),
            stackView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -72.0),
            stackView.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -60.0),
        ])
        
        [120, 80, 40, 0].forEach { val in
            let hLine = UIView()
            hLine.backgroundColor = .gray
            hLine.heightAnchor.constraint(equalToConstant: 1.0).isActive = true
            stackView.addArrangedSubview(hLine)
            let label = UILabel()
            label.textColor = .lightGray
            label.textAlignment = .center
            label.text = "\(val) h"
            label.translatesAutoresizingMaskIntoConstraints = false
            addSubview(label)
            NSLayoutConstraint.activate([
                label.leadingAnchor.constraint(equalTo: stackView.trailingAnchor, constant: 8.0),
                label.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -8.0),
                label.centerYAnchor.constraint(equalTo: hLine.centerYAnchor),
            ])
        }
        
        let legendStackView = UIStackView()
        legendStackView.alignment = .center
        legendStackView.spacing = 8
        let legs: [String] = ["Work", "Housework", "Kids"]
        for (s, c) in zip(legs, colors) {
            let dot = LegendDotView()
            dot.backgroundColor = c
            dot.heightAnchor.constraint(equalToConstant: 10.0).isActive = true
            dot.widthAnchor.constraint(equalTo: dot.heightAnchor, multiplier: 1.0).isActive = true
            let label = UILabel()
            label.textColor = .lightGray
            label.text = s
            legendStackView.addArrangedSubview(dot)
            legendStackView.addArrangedSubview(label)
        }
        legendStackView.translatesAutoresizingMaskIntoConstraints = false
        addSubview(legendStackView)
        NSLayoutConstraint.activate([
            legendStackView.leadingAnchor.constraint(equalTo: stackView.leadingAnchor),
            legendStackView.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -8.0)
        ])
        
        barsStackView.spacing = 40.0
        barsStackView.alignment = .bottom
        barsStackView.translatesAutoresizingMaskIntoConstraints = false
        addSubview(barsStackView)
        NSLayoutConstraint.activate([
            barsStackView.topAnchor.constraint(equalTo: stackView.topAnchor, constant: 0.0),
            barsStackView.bottomAnchor.constraint(equalTo: stackView.bottomAnchor, constant: 0.0),
            barsStackView.centerXAnchor.constraint(equalTo: stackView.centerXAnchor),
        ])
    }
 
    
    func configureChart(maxVal: CGFloat, values: [[CGFloat]]) {
        barTopLabels.forEach { v in
            v.removeFromSuperview()
        }
        barTopLabels = []
        barsStackView.arrangedSubviews.forEach { v in
            v.removeFromSuperview()
        }
        
        var pctVals: [[CGFloat]] = []
        values.forEach { valSet in
            let total = valSet.reduce(0, +)
            let pv = valSet.compactMap({ $0 / total})
            pctVals.append(pv)
        }
        
        for (hValSet, pValSet) in zip(values, pctVals) {
            let b = BarView()
            b.configureBar(maxVal: maxVal, values: pValSet, colors: colors)
            barsStackView.addArrangedSubview(b)
            b.widthAnchor.constraint(equalToConstant: 80.0).isActive = true
            let total = hValSet.reduce(0, +)
            let barPct = total / maxVal
            b.heightAnchor.constraint(equalTo: barsStackView.heightAnchor, multiplier: barPct).isActive = true
            let label = UILabel()
            label.textAlignment = .center
            label.textColor = .white
            let mx = hValSet.reduce(0.0, +)
            label.text = "\(Int(mx)) h"
            label.translatesAutoresizingMaskIntoConstraints = false
            addSubview(label)
            label.bottomAnchor.constraint(equalTo: b.topAnchor, constant: -8.0).isActive = true
            label.centerXAnchor.constraint(equalTo: b.centerXAnchor).isActive = true
            barTopLabels.append(label)
        }
    }
 
}
 
class StarterChartVC: UIViewController {
    
    let sampleChartView = StarterChartView()
    
    var sampleValueSets: [[[CGFloat]]] = [
        [[40, 40, 40], [55, 10, 30]],
        [[60, 40, 20], [40, 5, 20]],
        [[50, 30, 20], [50, 15, 10]],
        [[30, 40, 20], [50, 35, 30]],
    ]
    
    let infolabel = UILabel()
 
    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = .systemBackground
        
        sampleChartView.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(sampleChartView)
        
        let g = view.safeAreaLayoutGuide
        NSLayoutConstraint.activate([
            sampleChartView.topAnchor.constraint(equalTo: g.topAnchor, constant: 20.0),
            sampleChartView.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 20.0),
            sampleChartView.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -20.0),
            sampleChartView.heightAnchor.constraint(equalTo: sampleChartView.widthAnchor, multiplier: 1.0),
        ])
 
        infolabel.numberOfLines = 0
        infolabel.textAlignment = .center
        infolabel.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(infolabel)
        NSLayoutConstraint.activate([
            infolabel.topAnchor.constraint(equalTo: sampleChartView.bottomAnchor, constant: 40.0),
            infolabel.centerXAnchor.constraint(equalTo: g.centerXAnchor),
        ])
 
        updateValues()
    }
    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        updateValues()
    }
    func updateValues() {
        let theseVals = sampleValueSets.removeFirst()
        sampleChartView.configureChart(maxVal: 120, values: theseVals)
        sampleValueSets.append(theseVals)
        infolabel.text = "Tap anywhere to cycle through\na few sample value sets...\n\n" + theseVals.description
    }
}
 

Comments

  •  icon
    01/01/70 12:00:00 AM UTC
    Plain Text |

    0 B

    |

    👍

    /

    👎

    
        
  •  icon
    01/01/70 12:00:00 AM UTC
    Plain Text |

    0 B

    |

    👍

    /

    👎

    
        
  •  icon
    01/01/70 12:00:00 AM UTC
    Plain Text |

    0 B

    |

    👍

    /

    👎

    
        
  •  icon
    01/01/70 12:00:00 AM UTC
    Plain Text |

    0 B

    |

    👍

    /

    👎

    
        
  •  icon
    01/01/70 12:00:00 AM UTC
    Plain Text |

    0 B

    |

    👍

    /

    👎

    
        
  •  icon
    01/01/70 12:00:00 AM UTC
    Plain Text |

    0 B

    |

    👍

    /

    👎

    
        
  •  icon
    01/01/70 12:00:00 AM UTC
    Plain Text |

    0 B

    |

    👍

    /

    👎

    
        
  •  icon
    01/01/70 12:00:00 AM UTC
    Plain Text |

    0 B

    |

    👍

    /

    👎

    
        
  •  icon
    01/01/70 12:00:00 AM UTC
    Plain Text |

    0 B

    |

    👍

    /

    👎