我正在尝试做一个像这样的简单条形图。每个条都应该有一个值为 bar 的标签。如果它是一个大条,标签应该在顶部但在里面,如果它很小,它应该在外面。标签的字体应该适应用户的喜好。
到目前为止,我已经为一个条形图提供了此代码,图中的 7 个条形图只是一个具有七个不同数据点的 HStack()。
struct SingleBar: View {
let theCategory: String
let theValue: Double
let theMax: Double
var body: some View {
VStack {
GeometryReader { geometry in
let theHeight = (CGFloat(theValue / theMax) * geometry.size.height)
let theSpace = geometry.size.height - theHeight
let labelPositionY = (theSpace > (geometry.size.height / 2)) ? theSpace - 30 : theSpace + 10
RoundedRectangle(cornerRadius: 5.0)
.fill(LinearGradient(gradient: Gradient(colors: gradientColors), startPoint: .bottom, endPoint: .top))
.frame(height: theHeight)
.padding(.horizontal, 10)
.offset(x: 0, y:theSpace)
Text(theValue.fixedLength1)
.frame(width: geometry.size.width, alignment: .center)
.rotationEffect(Angle.degrees(270), anchor: .center)
.offset(x: 0, y: labelPositionY)
}
Text(theCategory)
.lineLimit(1)
}
}
}
我的问题是:如何计算标签的长度(以像素/点为单位),以便可以使用var labelPositionY将其移动到正确的位置?现在我已经迭代了一种字体大小设置,但我希望它在所有条件下都是完美的......
PS:theValue.fixedLength1 是一个计算属性,它返回一个格式化的字符串
我的解决方案
所以......在意识到问题已经在另一篇文章中得到回答后,我想分享我的“最终”解决方案:
struct ViewSizeKey: PreferenceKey {
static var defaultValue: CGSize = .zero
static func reduce(value: inout CGSize, nextValue: () -> CGSize) {
value = nextValue()
}
}
struct ViewGeometry: View {
var body: some View {
GeometryReader { geometry in
Color.clear
.preference(key: ViewSizeKey.self, value: geometry.size)
}
}
}
struct SingleBar: View {
@State var labelDimensions = CGSize(width: 0, height: 0)
let theCategory: String
let theValue: Double
let theMax: Double
let theTarget: Double
var body: some View {
VStack {
GeometryReader { geometry in
let theHeight = (CGFloat(theValue / theMax) * geometry.size.height)
let theSpace = geometry.size.height - theHeight
let labelPositionY = (theHeight > geometry.size.height / 4) ?
theSpace + labelDimensions.width / 2 :
theSpace - labelDimensions.width / 2 - labelDimensions.height
let labelPositionX = (geometry.size.width - labelDimensions.width) / 2
RoundedRectangle(cornerRadius: 5.0)
.frame(height: theHeight)
.padding(.horizontal, 10)
.offset(x: 0, y:theSpace)
Text(theValue.fixedLength1)
.background(ViewGeometry())
.onPreferenceChange(ViewSizeKey.self) { labelDimensions = $0 }
.rotationEffect(Angle.degrees(270), anchor: .center)
.offset(x: labelPositionX, y: labelPositionY)
}
Text(theCategory)
.lineLimit(1)
}
}
}