I am trying to create a view like this in SwiftUI (sorry it's so huge):
Specifically, I'm trying to build the scrolling row of labels/bar graph bars in the top quarter of the screen.
To me, it looks like a ScrollView with horizontal scrolling, containing some number of "barGraphItems" which are each a 270 degree-rotated VStack
of a label and a colored rectangle.
Here's the code I have so far:
import SwiftUI
struct ContentView: View {
var body: some View {
HStack(alignment: .bottom, spacing: 0) {
Spacer()
VStack(alignment: .leading, spacing: 0) {
Text("Demo")
.font(.caption)
.fontWeight(.bold)
Rectangle().foregroundColor(.orange)
.frame(width: 84, height: 10)
}
.rotationEffect(Angle(degrees: 270.0))
VStack(alignment: .leading, spacing: 0) {
Text("Demo")
.font(.caption)
.fontWeight(.bold)
Rectangle().foregroundColor(.orange)
.frame(width: 84, height: 10)
}
.rotationEffect(Angle(degrees: 270.0))
VStack(alignment: .leading, spacing: 0) {
Text("Demo")
.font(.caption)
.fontWeight(.bold)
Rectangle().foregroundColor(.orange)
.frame(width: 84, height: 10)
}
.rotationEffect(Angle(degrees: 270.0))
VStack(alignment: .leading, spacing: 0) {
Text("Demo")
.font(.caption)
.fontWeight(.bold)
Rectangle().foregroundColor(.orange)
.frame(width: 84, height: 10)
}
.rotationEffect(Angle(degrees: 270.0))
VStack(alignment: .leading, spacing: 0) {
Text("Demo")
.font(.caption)
.fontWeight(.bold)
Rectangle().foregroundColor(.orange)
.frame(width: 84, height: 10)
}
.rotationEffect(Angle(degrees: 270.0))
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
And here's what it produces (sorry it's so huge):
How can I get the barGraphItems closer together? How can I add the "Budget" line? How can I make the whole business scrollable WITHOUT scrolling the Budget line?
I think the other elements onscreen I can suss out, but I've been fiddling with this bar graph widget all afternoon and I'm stumped.
The problem with applying rotation effect to complex container having many views is that layout of container does not change, so all other views affected by rotated container
It looks more appropriate different approach. The idea is to have plot bars as rectangles where height or rectangle show values, and label is a transformed overlay for such rectangle, thus rectangles form layout, and overlays do not affect anything (almost).
Tested with Xcode 11.4 / iOS 13.4
Note: the budget line can be added above plot scrollview by wrap both into ZStack
.
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
VStack(alignment: .leading, spacing: 0) {
Text("Title above").font(.largeTitle)
ContentView()
Text("comment below")
}
}
}
struct ContentView: View {
var plotHeight: CGFloat = 120
// test with different lenth labels
let demos = ["Demo", "Demoos", "Demoooos", "Demooooooos"]
var body: some View {
ScrollView(.horizontal, showsIndicators: false) {
HStack(alignment: .bottom, spacing: 24) {
ForEach(0..<20) {_ in
Rectangle().foregroundColor(.orange)
.frame(width: 14, height: CGFloat.random(in: 0..<self.plotHeight)) // values for demo
.overlay(
Text(self.demos.randomElement()!) // values for demo
.font(.caption)
.fontWeight(.bold)
.fixedSize()
.rotationEffect(Angle(degrees: 270.0), anchor: .leading)
.offset(x: -8, y: 6) // align as/if needed here
, alignment: .bottomLeading)
}
}
.frame(height: plotHeight, alignment: .bottom)
.padding(.leading) // required offset for first label in scrollview
}
}
}