我创建了一个环形段形式的自定义形状。我最终想在我的应用程序中使用这个形状作为按钮,因此我需要能够将文本放在它的中间。下图显示了环段(蓝色)和我希望在蓝色段内垂直居中的文本(红色)。
下面的代码创建自定义蓝色形状段:
struct CurvedButtonData {
enum ButtonLocation {
case top
case right
case bottom
case left
}
/// The button's width as an angle in degrees.
let width: Double = 90.0
/// The button's start location, as an angle in degrees.
fileprivate(set) var start = 0.0
/// The button's end location, as an angle in degrees.
fileprivate(set) var end = 0.0
init(position: ButtonLocation) {
switch position {
case .top:
start = -135
case .right:
start = -45
case .bottom:
start = 45
case .left:
start = 135
}
end = start + width
}
}
struct CurvedButton: Shape, InsettableShape {
let data: CurvedButtonData
var insetAmount = 0.0
func path(in rect: CGRect) -> Path {
let points = CurvedButtonGeometry(curvedButtonData: data, rect: rect)
var path = Path()
path.addArc(center: points.center, radius: points.innerRadius, startAngle: .degrees(data.start), endAngle: .degrees(data.end), clockwise: false)
path.addArc(center: points.center, radius: points.outerRadius - insetAmount, startAngle: .degrees(data.end), endAngle: .degrees(data.start), clockwise: true)
path.closeSubpath()
return path
}
func inset(by amount: CGFloat) -> CurvedButton {
var button = self
button.insetAmount += amount
return button
}
}
struct CurvedButtonGeometry {
let data: CurvedButtonData
let center: CGPoint
let innerRadius: CGFloat
let outerRadius: CGFloat
init(curvedButtonData: CurvedButtonData, rect: CGRect) {
center = CGPoint(x: rect.midX, y: rect.midY)
let radius = min(rect.width, rect.height) / 4
innerRadius = radius
outerRadius = radius * 2
data = curvedButtonData
}
/// Returns the view location of the point in the wedge at unit-
/// space location `unitPoint`, where the X axis of `p` moves around the
/// wedge arc and the Y axis moves out from the inner to outer
/// radius.
subscript(unitPoint: UnitPoint) -> CGPoint {
let radius = lerp(innerRadius, outerRadius, by: unitPoint.y)
let angle = lerp(data.start, data.end, by: Double(unitPoint.x))
return CGPoint(x: center.x + CGFloat(cos(angle)) * radius,
y: center.y + CGFloat(sin(angle)) * radius)
}
/// Linearly interpolate from `from` to `to` by the fraction `amount`.
private func lerp<T: BinaryFloatingPoint>(_ fromValue: T, _ toValue: T, by amount: T) -> T {
return fromValue + (toValue - fromValue) * amount
}
}
这是我用我的自定义形状创建一个按钮并将“单击我”文本居中在蓝色区域内的可惜尝试:
import SwiftUI
struct CircularCVBControl: View {
var body: some View {
Button {
} label: {
ZStack(alignment: .top) {
CurvedButton(data: .init(position: .top))
.fill(.blue)
.overlay(alignment: .top) {
Text("Click me")
.foregroundColor(.red)
}
}
}
.buttonStyle(.plain)
}
}
struct CircularCVBControl_Previews: PreviewProvider {
static var previews: some View {
CircularCVBControl()
}
}
我现在如何整齐(并且不在文本上使用hacky padding)将文本垂直居中在我的自定义形状中?我正在寻找某种可以完成这项工作的自定义对齐指南/逻辑。此外,形状也可以向其他方向旋转,文本也应随之旋转。