我正试图在SwiftUI中创建一个FluidView
形状,它的作用就像容器中的流体,这样当设备处于特定角度时,形状/"流体"也是如此。该形状还有一个特定的容量percentFilled
,它指示应该填充多少父视图。
使用这些约束,类的不变量是
lines.area == rect.area * percentFilled
其中lines
是四边形并且rect
是边界矩形。这个不变量意味着,无论倾斜角度如何,形状的"体积"对于每个percentFilled
都保持不变。
以下是我目前所拥有的:
/// A View made using a specified angle and amount to fill
/// - Invariant: The area of the view is exactly equal to the area of the rectangle of the parent view times `percentFilled`
struct FluidView: Shape {
var angle: CGFloat = 0.0
var percentFilled: CGFloat = 0
/// Creates a new FluidView
/// - Parameters:
/// - angle: A value in the range `0...1`. A value of `0` indicates the view is horizontal, and an angle of `1` indicates the view is vertical (horizontal if viewed as landscape)
/// - percentFilled: the amount of the view bounds to fill represented as a value in the range `0...1`. A value of `x` indicates that `x * 100`% of the parent view is covered by this view
init(angle: CGFloat = 0, percentFilled: CGFloat = 0) {
precondition(0...1 ~= angle)
precondition(0...1 ~= percentFilled)
self.angle = angle
self.percentFilled = percentFilled
}
func path(in rect: CGRect) -> Path {
var path = Path()
path.move(to: CGPoint(x: 0, y: rect.height * (1 - percentFilled))) // top left
let lines = [
(0, rect.height ), // bottom left
(rect.width * 1 / (1 + angle - percentFilled), rect.height ), // bottom right
(rect.width * 1 / (1 + angle - percentFilled), rect.height * (1 + angle - percentFilled)), // top right
(0, rect.height * (1 - angle - percentFilled)) // top left
].map { x, y in
// make sure no points exceed the bounds
CGPoint(x: min(rect.width, x), y: min(rect.height, y))
}
// invariant
assert(lines.area == rect.area * percentFilled)
path.addLines(lines)
return path
}
}
我觉得我目前所拥有的有点接近目标,但不变量失败了。我相信我的y坐标是正确的,但我认为我对x坐标的计算必须改变,但我不确定它们应该改变什么。
任何帮助都将不胜感激,谢谢!
试试这样的东西,
struct FilledShape<S: Shape>: View {
let shape: S
@State var angle: Angle = .zero
@State var percentFull: CGFloat
var gradient: Gradient {
Gradient(stops: [Gradient.Stop(color: .red, location: 0),
Gradient.Stop(color: .red, location: percentFull),
Gradient.Stop(color: .blue, location: percentFull),
Gradient.Stop(color: .blue, location: 1)])
}
var body: some View {
shape.rotation(angle)
.fill(LinearGradient(gradient: gradient, startPoint: .bottom, endPoint: .top))
}
}
struct ContentView: View {
@State var angle: Angle = .degrees(30)
var body: some View {
FilledShape(shape: Rectangle(), angle: angle, percentFull: 0.3).frame(width: 100, height: 100)
}
}
问题是,填充百分比实际上是沿着y轴向上的百分比,而不是填充区域的百分比。你可以使用GeometryReader的某种数字方法来获得面积,并在适当的填充面积和上读取y值(或者如果你只使用四边形,那就更容易了(。暴力:
extension Shape {
func area(in box: CGRect) -> Int {
var area = 0
for x in 0..<Int(box.width) {
for y in 0..<Int(box.height) {
let point = CGPoint(x: x, y: y)
if self.contains(point) {
area += 1
}
}
}
return area
}
}
作为一种不同的方法,请查看SpriteKit和SKPhysicsBody。