首先,我们应该问是否值得写一篇
ShapeBuilder
而不是写
AnyShape
键入如下内容:
public struct AnyShape: Shape {
public let makePath: (CGRect) -> Path
public init(makePath: @escaping (CGRect) -> Path) {
self.makePath = makePath
}
public init<Wrapped: Shape>(_ shape: Wrapped) {
self.makePath = { shape.path(in: $0) }
}
public func path(in rect: CGRect) -> Path {
return makePath(rect)
}
}
我们可以使用
AnyShape
写你的
testForShape
功能如下:
func testForShape(value: Bool) -> AnyShape {
if value {
return AnyShape(Circle())
}
else {
return AnyShape(Rectangle())
}
}
我用过
AnyShape
在各种项目中。
更新
苹果添加了
AnyShape
在2022年发布的iOS 16、macOS 13等中键入SwiftUI。苹果版本没有
makePath
初始化器,就像我上面的实现一样,但是
an equivalent
的
Wrapped
初始化器,这足以解决问题中所述的问题。
现在回到原来
不管怎样,如果我们想写
ShapeBuilder
,我们可以从最简单的实现开始,它只支持包含单个无条件子形状的实体:
@resultBuilder
public struct ShapeBuilder {
public static func buildBlock<C0: Shape>(_ c0: C0) -> C0 { c0 }
}
我们现在可以这样使用它:
@ShapeBuilder
func shape1() -> some Shape {
Circle()
}
好极了
我们可以扩展它来支撑包含
不
通过添加零参数形成子形状
buildBlock
和
EmptyShape
键入以使其返回:
extension ShapeBuilder {
public static func buildBlock() -> EmptyShape { EmptyShape() }
}
public struct EmptyShape: Shape {
public func path(in rect: CGRect) -> Path { Path() }
}
现在我们可以毫无错误地编写以下函数:
@ShapeBuilder
func shape0() -> some Shape {
}
支持
if
不带
else
条款,我们添加
buildOptional
方法和
OptionalShape
键入以使其返回:
extension ShapeBuilder {
public static func buildOptional<C0: Shape>(_ c0: C0?) -> OptionalShape<C0> {
return OptionalShape(c0)
}
}
public struct OptionalShape<Content: Shape>: Shape {
public let content: Content?
public init(_ content: Content?) {
self.content = content
}
public func path(in rect: CGRect) -> Path {
return content?.path(in: rect) ?? Path()
}
}
现在我们可以这样使用它:
@ShapeBuilder
func shape2(flag: Bool) -> some Shape {
if flag {
Circle()
}
}
支持
如果
陈述
具有
一
其他的
子句,并支持
switch
声明,我们添加
buildEither
和
EitherShape
键入以使其返回:
extension ShapeBuilder {
public static func buildEither<First: Shape, Second: Shape>(first: First) -> EitherShape<First, Second> {
return .first(first)
}
public static func buildEither<First: Shape, Second: Shape>(second: Second) -> EitherShape<First, Second> {
return .second(second)
}
}
public enum EitherShape<First: Shape, Second: Shape>: Shape {
case first(First)
case second(Second)
public func path(in rect: CGRect) -> Path {
switch self {
case .first(let first): return first.path(in: rect)
case .second(let second): return second.path(in: rect)
}
}
}
我们现在可以编写此函数:
@ShapeBuilder
func shape3(_ n: Int) -> some Shape {
if n < 0 {
Rectangle()
} else {
switch n {
case 0:
EmptyShape()
case 1:
Rectangle()
default:
Capsule()
}
}
}
我们可以通过编写
构建块
方法,该方法接受两个参数和
Tuple2Shape
以便返回:
extension ShapeBuilder {
public static func buildBlock<C0: Shape, C1: Shape>(_ c0: C0, _ c1: C1) -> Tuple2Shape<C0, C1> {
return Tuple2Shape(c0, c1)
}
}
public struct Tuple2Shape<C0: Shape, C1: Shape>: Shape {
public let tuple: (C0, C1)
public init(_ c0: C0, _ c1: C1) {
tuple = (c0, c1)
}
public func path(in rect: CGRect) -> Path {
var path = tuple.0.path(in: rect)
path.addPath(tuple.1.path(in: rect))
return path
}
}
现在我们可以编写这个函数:
@ShapeBuilder
func shape4() -> some Shape {
Circle()
Rectangle()
}
我们可以通过编写
构建块
方法,该方法接受三个参数和
Tuple3Shape
以便返回:
extension ShapeBuilder {
public static func buildBlock<C0: Shape, C1: Shape, C2: Shape>(_ c0: C0, _ c1: C1, _ c2: C2) -> Tuple3Shape<C0, C1, C2> {
return Tuple3Shape(c0, c1, c2)
}
}
public struct Tuple3Shape<C0: Shape, C1: Shape, C2: Shape>: Shape {
public let tuple: (C0, C1, C2)
public init(_ c0: C0, _ c1: C1, _ c2: C2) {
tuple = (c0, c1, c2)
}
public func path(in rect: CGRect) -> Path {
var path = tuple.0.path(in: rect)
path.addPath(tuple.1.path(in: rect))
path.addPath(tuple.2.path(in: rect))
return path
}
}
这样我们就可以编写这个函数:
@ShapeBuilder
func shape5() -> some Shape {
Circle()
Rectangle()
Capsule()
}
ViewBuilder
有
构建块
方法达到arity 10,但我不会再写了。如果你需要,你可以自己做。
不管怎样,这是
ShapeBuilder
实现在一起,便于复制“粘贴”:
@resultBuilder
public struct ShapeBuilder {
public static func buildBlock<C0: Shape>(_ c0: C0) -> C0 { c0 }
}
extension ShapeBuilder {
public static func buildBlock() -> EmptyShape { EmptyShape() }
}
public struct EmptyShape: Shape {
public func path(in rect: CGRect) -> Path { Path() }
}
extension ShapeBuilder {
public static func buildOptional<C0: Shape>(_ c0: C0?) -> OptionalShape<C0> {
return OptionalShape(c0)
}
}
public struct OptionalShape<Content: Shape>: Shape {
public let content: Content?
public init(_ content: Content?) {
self.content = content
}
public func path(in rect: CGRect) -> Path {
return content?.path(in: rect) ?? Path()
}
}
extension ShapeBuilder {
public static func buildEither<First: Shape, Second: Shape>(first: First) -> EitherShape<First, Second> {
return .first(first)
}
public static func buildEither<First: Shape, Second: Shape>(second: Second) -> EitherShape<First, Second> {
return .second(second)
}
}
public enum EitherShape<First: Shape, Second: Shape>: Shape {
case first(First)
case second(Second)
public func path(in rect: CGRect) -> Path {
switch self {
case .first(let first): return first.path(in: rect)
case .second(let second): return second.path(in: rect)
}
}
}
extension ShapeBuilder {
public static func buildBlock<C0: Shape, C1: Shape>(_ c0: C0, _ c1: C1) -> Tuple2Shape<C0, C1> {
return Tuple2Shape(c0, c1)
}
}
public struct Tuple2Shape<C0: Shape, C1: Shape>: Shape {
public let tuple: (C0, C1)
public init(_ c0: C0, _ c1: C1) {
tuple = (c0, c1)
}
public func path(in rect: CGRect) -> Path {
var path = tuple.0.path(in: rect)
path.addPath(tuple.1.path(in: rect))
return path
}
}
extension ShapeBuilder {
public static func buildBlock<C0: Shape, C1: Shape, C2: Shape>(_ c0: C0, _ c1: C1, _ c2: C2) -> Tuple3Shape<C0, C1, C2> {
return Tuple3Shape(c0, c1, c2)
}
}
public struct Tuple3Shape<C0: Shape, C1: Shape, C2: Shape>: Shape {
public let tuple: (C0, C1, C2)
public init(_ c0: C0, _ c1: C1, _ c2: C2) {
tuple = (c0, c1, c2)
}
public func path(in rect: CGRect) -> Path {
var path = tuple.0.path(in: rect)
path.addPath(tuple.1.path(in: rect))
path.addPath(tuple.2.path(in: rect))
return path
}
}