这个 Swift 包可以用来轻松构建符合 Shape
协议的 SwiftUI 形状。大多数广为人知的形状都在这里,还有一些不太常见的形状也在这里!
Shapes 需要最低 macOS 12、iOS 15 和 tvOS 15。
此项目中定义的每个形状都遵循 SwiftUI 的 Shape
协议。为这些自定义形状提供内边距功能也是可取的,类似于内置的 SwiftUI 形状。
InsetShape
类型被引入作为一种包装器类型,类似于 RotatedShape
、OffsetShape
等。InsetShape
遵循 InsettableShape
协议,并提供了一种内边距任何给定形状的方法。
用法:InsetShape(shape: MyCustomShape(), inset: amount)
此外,还提供了一个 Shape
扩展方法,使其易于使用
用法:MyShape().inset(amount: amount)
// 返回 InsetShape
Polygon
是一个由多条线段组成的闭合形状。在此项目中,它被定义为一个协议
/// A plane figure that is described by a finite number of straight line segments connected to form a closed polygonal chain
public protocol Polygon {
var sides: Int { get }
func vertices(in rect: CGRect) -> [CGPoint]
}
每个 Polygon
都由边数定义。为了绘制形状,还需要每个多边形的顶点
。这些顶点通过 CGPoint
数组进行管理。顶点通过一个方法获得,以便确定在给定 CGRect
中的位置
并非此库中的所有形状都符合 Polygon
协议,因为它们可能包含曲面。
库中最简单的 Polygon 示例被恰当地命名为 SimplePolygon
。在初始化器中提供一个比率数组,以构造一个多边形,其点位于单位圆周围,并包含在形状的框架中。
例如,如果要定义一个多边形,其点位于 0.33、0.67 和 1
SimplePolygon(ratios: [0.33, 0.67, 1])
这告诉库生成一个多边形,其顶点位于单位圆周围行进距离的 33%、67% 和 100% 处。(0% 从下图朝东的点开始)
用于生成此图的代码是
struct SimplePolygon_Previews: PreviewProvider {
static var previews: some View {
ZStack {
Circle().stroke() // the unit circle
SimplePolygon(ratios: [0.33, 0.67, 1])
.foregroundStyle(Color.blue)
}
.border(Color.purple)
}
}
您可以根据需要以任何顺序提供任意数量的比率,形状将适当地渲染它们
struct SimplePolygon2_Previews: PreviewProvider {
static var previews: some View {
ZStack {
Circle().stroke() // the unit circle
SimplePolygon(ratios: [0.1, 0.5, 0.756, 0.3, 0.4])
.foregroundStyle(Color.blue)
}
.border(Color.purple)
}
}
注意:对于非正方形框架,多边形将渲染在框架的中心。单位圆是基于 CGRect
的最小尺寸来测量的
struct SimplePolygon3_Previews: PreviewProvider {
static var previews: some View {
ZStack {
Circle().stroke()
SimplePolygon(ratios: [0.1, 0.5, 0.756, 0.3, 0.4])
.foregroundStyle(Color.blue)
}
.border(Color.purple)
.frame(width: 256, height: 128)
}
}
凸多边形,也称为正多边形,是一个边长都相等,且顶点在单位圆周围均匀分布的多边形。
ConvexPolygon
被定义为一个遵循 RegularPolygon
协议的结构体。作为协议,RegularPolygon
需要以下属性和方法
/// A polygon with equal-length sides and equally-spaced vertices along a circumscribed circle
public protocol RegularPolygon: Polygon {}
没错,它是空的! RegularPolygon
遵循 Polygon
协议,没有任何额外的要求。RegularPolygon
存在的原因是通过协议扩展来实现 vertices(in: CGRect)
方法
public extension RegularPolygon {
/// obtains the equally spaced points of a polygon around a circle inscribed in rect, arranged clockwise starting at the top of the unit circle
func vertices(in rect: CGRect) -> [CGPoint] {
vertices(in: rect, offset: .zero)
}
/// obtains the equally spaced points of a polygon around a circle inscribed in rect, arranged clockwise starting at the top of the unit circle, with any additional rotational offset
func vertices(in rect: CGRect, offset: Angle = .zero) -> [CGPoint] {
let r = min(rect.size.width, rect.size.height) / 2
let origin = CGPoint(x: rect.midX, y: rect.midY)
let array: [CGPoint] = Array(0 ..< sides).map {
let theta = 2 * .pi * CGFloat($0) / CGFloat(sides) + CGFloat(offset.radians) - CGFloat.pi / 2 // pointing north!
return CGPoint(x: origin.x + r * cos(theta), y: origin.y + r * sin(theta))
}
return array
}
}
在上面的代码中,我做出了有意识的选择,将单位圆的原点旋转为朝上。我在教科书和互联网上看到的大多数多边形图纸(抱歉,没有提供引用)都显示一个角指向图像的顶部,所以这里使用了这个约定。
第二个方法 vertices(in: CGRect, offset: Angle)
允许用户根据需要指定额外的旋转偏移。
这些方法对于获取非多边形的其他形状的顶点也很有用,例如具有弯曲边的 ReuleauxPolygon
和 Torx
。
现在我们了解了 RegularPolygon
的用法,是时候了解 ConvexPolygon
了。初始化器接受边数,像这样
ConvexPolygon(sides: 7)
可以使用以下示例来说明此形状
struct ConvexPolygon_Previews: PreviewProvider {
static var previews: some View {
ZStack {
Circle() // unit circle
.strokeBorder(Color.red, lineWidth: 10)
ConvexPolygon(sides: 7)
.strokeBorder(Color.green.opacity(0.8), lineWidth: 10)
}
}
}