// 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
0 B
|👍
/👎
0 B
|👍
/👎
0 B
|👍
/👎
0 B
|👍
/👎
0 B
|👍
/👎
0 B
|👍
/👎
0 B
|👍
/👎
0 B
|👍
/👎
0 B
|👍
/👎