如何在 GeometryReader 中放置旋转标签
How to place rotated labels within a GeometryReader
我有下面的,它试图用几组标签做一个条形图,一组旋转。在下面,我使用 GeometryReader 将 space 的一半分配给实际的柱。
然后我在下方放置了 2 组标签,第一组没问题,但第二组(已旋转)最终位于第一组之上。让第二组标签显示在第一组标签下方的简洁方法是什么?
struct ContentView: View {
var body: some View {
GeometryReader { geometry in
let width = geometry.size.width / 3.0
VStack {
Text("Chart").font(.title)
HStack(alignment: .bottom, spacing: 0.0) {
Rectangle().fill(.blue).frame(width: width, height: geometry.size.height/2.0)
Rectangle().fill(.orange).frame(width: width, height: geometry.size.height/2.0 * 0.7)
Rectangle().fill(.red).frame(width: width, height: geometry.size.height/2.0 * 0.88)
}
Divider()
HStack {
Text("100%").frame(width: width)
Text("70%").frame(width: width)
Text("88%").frame(width: width)
}
Divider()
HStack {
Text("1somelonglabel").rotationEffect(Angle(degrees: 90.0))
Text("2somelonglabel").rotationEffect(Angle(degrees: 90.0))
Text("3somelonglabel").rotationEffect(Angle(degrees: 90.0))
}
}
}
}
}
编辑:
这是一些更新后的代码,包含更多条形图。虽然旋转标签现在位于第一组下方,但它们被截断了,因为看起来即使在旋转后原始宽度也被考虑在内。整个旋转点是为了能够适应标签,所以不知道如何让它们在旋转后占据可用的space。
struct Entry: Identifiable {
var id: Int
var label: String
var value: Double
var color: Color
func labelPercentage() -> String {
let percentage = value * 100.0
return String(format: "%.0f", percentage) + "%"
}
}
struct Entries {
var entries: [Entry] = []
init() {
entries.append( Entry(id: 1, label: "short", value: 0.3, color: .blue) )
entries.append( Entry(id: 2, label: "medium", value: 0.5, color: .orange) )
entries.append( Entry(id: 3, label: "label2", value: 0.0, color: .gray) )
entries.append( Entry(id: 4, label: "longerlabel12", value: 0.3, color: .black) )
entries.append( Entry(id: 5, label: "label12", value: 0.7, color: .red) )
entries.append( Entry(id: 6, label: "another333", value: 0.6, color: .green) )
entries.append( Entry(id: 7, label: "another", value: 0.0, color: .purple) )
entries.append( Entry(id: 8, label: "medium123", value: 0.1, color: .yellow) )
}
}
struct ContentView: View {
var entries: Entries
var body: some View {
GeometryReader { geometry in
let width = geometry.size.width / CGFloat(entries.entries.count) - 8.0
let height = geometry.size.height / 2.0
let offset = width / 2.0
VStack {
Text("Chart").font(.title)
HStack(alignment: .bottom, spacing: 4.0) {
ForEach(entries.entries) { entry in
Bar(color: entry.color, percentage: entry.value, width: width, height: height)
}
}
Divider()
HStack {
ForEach(entries.entries) { entry in
Text(entry.labelPercentage()).font(.caption).frame(width: width)
}
}
Divider()
HStack {
ForEach(entries.entries) { entry in
Text(entry.label).lineLimit(1)
.frame(width: width, alignment: .topLeading)
.offset(x: offset, y: 0)
.rotationEffect(Angle(degrees: 90.0)).border(.red, width: 3)
}
}
}
}
}
}
struct Bar: View {
var color: Color
var percentage: Double
var width: Double
var height: Double
var body: some View {
ZStack(alignment: .bottom) {
Rectangle().fill(.white).frame(width: width, height: height).opacity(0.0) // Want the full height
Rectangle().fill(color).frame(width: width, height: height * percentage)
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
// ContentView(words: Words(numWords: 200))
ContentView(entries: Entries())
}
}
这是它的样子:
您遇到的问题是 .rotationEffect()
正在围绕其中心旋转 Text()
。由于 Text()
在旋转时位于 Divider()
的正下方,它的一半遮住了上面的视图。因此,您只需将标签向右移动,当顺时针旋转时,标签会向下移动。我只是使用了你宽度的一半,这对于这个例子来说已经足够了。我也把它放在一个框架中,以防止标签变得太长。
struct ContentView: View {
var body: some View {
GeometryReader { geometry in
let width = geometry.size.width / 3.0
let offset = width / 2.0
VStack {
Text("Chart").font(.title)
HStack(alignment: .bottom, spacing: 0.0) {
Rectangle().fill(.blue).frame(width: width, height: geometry.size.height/2.0)
Rectangle().fill(.orange).frame(width: width, height: geometry.size.height/2.0 * 0.7)
Rectangle().fill(.red).frame(width: width, height: geometry.size.height/2.0 * 0.88)
}
Divider()
HStack {
Text("100%").frame(width: width)
Text("70%").frame(width: width)
Text("88%").frame(width: width)
}
Divider()
HStack {
Text("1somelonglabel").frame(width: width).offset(x: offset, y: 0).rotationEffect(Angle(degrees: 90.0))
Text("2somelonglabel").frame(width: width).offset(x: offset, y: 0).rotationEffect(Angle(degrees: 90.0))
Text("3somelonglabel").frame(width: width).offset(x: offset, y: 0).rotationEffect(Angle(degrees: 90.0))
}
}
}
}
}
好的,根据其中一条评论中的信息(在矩形上使用叠加层),我终于得到了我想要的:
import SwiftUI
struct Entry: Identifiable {
var id: Int
var label: String
var value: Double
var color: Color
func labelPercentage() -> String {
let percentage = value * 100.0
return String(format: "%.0f", percentage) + "%"
}
}
struct Entries {
var entries: [Entry] = []
init() {
entries.append( Entry(id: 1, label: "short", value: 0.3, color: .blue) )
entries.append( Entry(id: 2, label: "medium", value: 0.5, color: .orange) )
entries.append( Entry(id: 3, label: "label2", value: 0.0, color: .gray) )
entries.append( Entry(id: 4, label: "longerlabel12", value: 0.3, color: .black) )
entries.append( Entry(id: 5, label: "label12", value: 0.7, color: .red) )
entries.append( Entry(id: 6, label: "another333", value: 0.6, color: .green) )
entries.append( Entry(id: 7, label: "another", value: 0.0, color: .purple) )
entries.append( Entry(id: 8, label: "medium123", value: 0.1, color: .yellow) )
}
}
struct ContentView: View {
var entries: Entries
var body: some View {
GeometryReader { geometry in
let width = geometry.size.width / CGFloat(entries.entries.count) - 8.0
let height = geometry.size.height / 2.0
VStack {
Text("Chart").font(.title)
HStack(alignment: .bottom, spacing: 4.0) {
ForEach(entries.entries) { entry in
Bar(color: entry.color, percentage: entry.value, width: width, height: height)
}
}
Divider()
HStack(spacing: 4) {
ForEach(entries.entries) { entry in
Text(entry.labelPercentage()).font(.caption).frame(width: width)
}
}
Divider()
HStack(spacing: 4) {
ForEach(entries.entries) { entry in
Rectangle().foregroundColor(.white)
.frame(width: width, height: height/2.0)
.overlay(
Text(entry.label)
.font(.caption)
.fontWeight(.bold)
.fixedSize()
.rotationEffect(Angle(degrees: 90), anchor: .leading)
.offset(x: width / 2.0, y: 0)
, alignment: .topLeading)
}
}
}
}
}
}
struct Bar: View {
var color: Color
var percentage: Double
var width: Double
var height: Double
var body: some View {
ZStack(alignment: .bottom) {
Rectangle().fill(.white).frame(width: width, height: height).opacity(0.0) // Want the full height
Rectangle().fill(color).frame(width: width, height: height * percentage)
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView(entries: Entries())
}
}
我有下面的,它试图用几组标签做一个条形图,一组旋转。在下面,我使用 GeometryReader 将 space 的一半分配给实际的柱。
然后我在下方放置了 2 组标签,第一组没问题,但第二组(已旋转)最终位于第一组之上。让第二组标签显示在第一组标签下方的简洁方法是什么?
struct ContentView: View {
var body: some View {
GeometryReader { geometry in
let width = geometry.size.width / 3.0
VStack {
Text("Chart").font(.title)
HStack(alignment: .bottom, spacing: 0.0) {
Rectangle().fill(.blue).frame(width: width, height: geometry.size.height/2.0)
Rectangle().fill(.orange).frame(width: width, height: geometry.size.height/2.0 * 0.7)
Rectangle().fill(.red).frame(width: width, height: geometry.size.height/2.0 * 0.88)
}
Divider()
HStack {
Text("100%").frame(width: width)
Text("70%").frame(width: width)
Text("88%").frame(width: width)
}
Divider()
HStack {
Text("1somelonglabel").rotationEffect(Angle(degrees: 90.0))
Text("2somelonglabel").rotationEffect(Angle(degrees: 90.0))
Text("3somelonglabel").rotationEffect(Angle(degrees: 90.0))
}
}
}
}
}
编辑:
这是一些更新后的代码,包含更多条形图。虽然旋转标签现在位于第一组下方,但它们被截断了,因为看起来即使在旋转后原始宽度也被考虑在内。整个旋转点是为了能够适应标签,所以不知道如何让它们在旋转后占据可用的space。
struct Entry: Identifiable {
var id: Int
var label: String
var value: Double
var color: Color
func labelPercentage() -> String {
let percentage = value * 100.0
return String(format: "%.0f", percentage) + "%"
}
}
struct Entries {
var entries: [Entry] = []
init() {
entries.append( Entry(id: 1, label: "short", value: 0.3, color: .blue) )
entries.append( Entry(id: 2, label: "medium", value: 0.5, color: .orange) )
entries.append( Entry(id: 3, label: "label2", value: 0.0, color: .gray) )
entries.append( Entry(id: 4, label: "longerlabel12", value: 0.3, color: .black) )
entries.append( Entry(id: 5, label: "label12", value: 0.7, color: .red) )
entries.append( Entry(id: 6, label: "another333", value: 0.6, color: .green) )
entries.append( Entry(id: 7, label: "another", value: 0.0, color: .purple) )
entries.append( Entry(id: 8, label: "medium123", value: 0.1, color: .yellow) )
}
}
struct ContentView: View {
var entries: Entries
var body: some View {
GeometryReader { geometry in
let width = geometry.size.width / CGFloat(entries.entries.count) - 8.0
let height = geometry.size.height / 2.0
let offset = width / 2.0
VStack {
Text("Chart").font(.title)
HStack(alignment: .bottom, spacing: 4.0) {
ForEach(entries.entries) { entry in
Bar(color: entry.color, percentage: entry.value, width: width, height: height)
}
}
Divider()
HStack {
ForEach(entries.entries) { entry in
Text(entry.labelPercentage()).font(.caption).frame(width: width)
}
}
Divider()
HStack {
ForEach(entries.entries) { entry in
Text(entry.label).lineLimit(1)
.frame(width: width, alignment: .topLeading)
.offset(x: offset, y: 0)
.rotationEffect(Angle(degrees: 90.0)).border(.red, width: 3)
}
}
}
}
}
}
struct Bar: View {
var color: Color
var percentage: Double
var width: Double
var height: Double
var body: some View {
ZStack(alignment: .bottom) {
Rectangle().fill(.white).frame(width: width, height: height).opacity(0.0) // Want the full height
Rectangle().fill(color).frame(width: width, height: height * percentage)
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
// ContentView(words: Words(numWords: 200))
ContentView(entries: Entries())
}
}
这是它的样子:
您遇到的问题是 .rotationEffect()
正在围绕其中心旋转 Text()
。由于 Text()
在旋转时位于 Divider()
的正下方,它的一半遮住了上面的视图。因此,您只需将标签向右移动,当顺时针旋转时,标签会向下移动。我只是使用了你宽度的一半,这对于这个例子来说已经足够了。我也把它放在一个框架中,以防止标签变得太长。
struct ContentView: View {
var body: some View {
GeometryReader { geometry in
let width = geometry.size.width / 3.0
let offset = width / 2.0
VStack {
Text("Chart").font(.title)
HStack(alignment: .bottom, spacing: 0.0) {
Rectangle().fill(.blue).frame(width: width, height: geometry.size.height/2.0)
Rectangle().fill(.orange).frame(width: width, height: geometry.size.height/2.0 * 0.7)
Rectangle().fill(.red).frame(width: width, height: geometry.size.height/2.0 * 0.88)
}
Divider()
HStack {
Text("100%").frame(width: width)
Text("70%").frame(width: width)
Text("88%").frame(width: width)
}
Divider()
HStack {
Text("1somelonglabel").frame(width: width).offset(x: offset, y: 0).rotationEffect(Angle(degrees: 90.0))
Text("2somelonglabel").frame(width: width).offset(x: offset, y: 0).rotationEffect(Angle(degrees: 90.0))
Text("3somelonglabel").frame(width: width).offset(x: offset, y: 0).rotationEffect(Angle(degrees: 90.0))
}
}
}
}
}
好的,根据其中一条评论中的信息(在矩形上使用叠加层),我终于得到了我想要的:
import SwiftUI
struct Entry: Identifiable {
var id: Int
var label: String
var value: Double
var color: Color
func labelPercentage() -> String {
let percentage = value * 100.0
return String(format: "%.0f", percentage) + "%"
}
}
struct Entries {
var entries: [Entry] = []
init() {
entries.append( Entry(id: 1, label: "short", value: 0.3, color: .blue) )
entries.append( Entry(id: 2, label: "medium", value: 0.5, color: .orange) )
entries.append( Entry(id: 3, label: "label2", value: 0.0, color: .gray) )
entries.append( Entry(id: 4, label: "longerlabel12", value: 0.3, color: .black) )
entries.append( Entry(id: 5, label: "label12", value: 0.7, color: .red) )
entries.append( Entry(id: 6, label: "another333", value: 0.6, color: .green) )
entries.append( Entry(id: 7, label: "another", value: 0.0, color: .purple) )
entries.append( Entry(id: 8, label: "medium123", value: 0.1, color: .yellow) )
}
}
struct ContentView: View {
var entries: Entries
var body: some View {
GeometryReader { geometry in
let width = geometry.size.width / CGFloat(entries.entries.count) - 8.0
let height = geometry.size.height / 2.0
VStack {
Text("Chart").font(.title)
HStack(alignment: .bottom, spacing: 4.0) {
ForEach(entries.entries) { entry in
Bar(color: entry.color, percentage: entry.value, width: width, height: height)
}
}
Divider()
HStack(spacing: 4) {
ForEach(entries.entries) { entry in
Text(entry.labelPercentage()).font(.caption).frame(width: width)
}
}
Divider()
HStack(spacing: 4) {
ForEach(entries.entries) { entry in
Rectangle().foregroundColor(.white)
.frame(width: width, height: height/2.0)
.overlay(
Text(entry.label)
.font(.caption)
.fontWeight(.bold)
.fixedSize()
.rotationEffect(Angle(degrees: 90), anchor: .leading)
.offset(x: width / 2.0, y: 0)
, alignment: .topLeading)
}
}
}
}
}
}
struct Bar: View {
var color: Color
var percentage: Double
var width: Double
var height: Double
var body: some View {
ZStack(alignment: .bottom) {
Rectangle().fill(.white).frame(width: width, height: height).opacity(0.0) // Want the full height
Rectangle().fill(color).frame(width: width, height: height * percentage)
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView(entries: Entries())
}
}