形状

这是关于什么?

这个 Swift 包可以用来轻松构建符合 Shape 协议的 SwiftUI 形状。大多数广为人知的形状都在这里,还有一些不太常见的形状也在这里!

要求

Shapes 需要最低 macOS 12、iOS 15 和 tvOS 15。

正在建设中

此页面的目标是列出所有可用的形状,并包含代码示例和可视化效果。
待办事项:浅色/深色模式图像

InsetShape

此项目中定义的每个形状都遵循 SwiftUI 的 Shape 协议。为这些自定义形状提供内边距功能也是可取的,类似于内置的 SwiftUI 形状。

InsetShape 类型被引入作为一种包装器类型,类似于 RotatedShapeOffsetShape 等。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 协议,因为它们可能包含曲面。

多边形示例:SimplePolygon

库中最简单的 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

凸多边形,也称为正多边形,是一个边长都相等,且顶点在单位圆周围均匀分布的多边形。

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) 允许用户根据需要指定额外的旋转偏移。

这些方法对于获取多边形的其他形状的顶点也很有用,例如具有弯曲边的 ReuleauxPolygonTorx

现在我们了解了 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)
    }
  }
}