ShapeBuilder

一个结果构建器实现,允许定义形状构建闭包和变量。

问题

在 SwiftUI 中,你可能会遇到想要根据视图样式更改 Shape 属性的情况。想象一下,你构建了一个视图,它应该呈现圆形外观,或者根据给定的圆角半径具有圆角。你可能会写出类似以下的代码:

struct MyFancyView: View {
  let isRound: Bool

  var body: some View {
    // Fancy content here
      .mask(maskingLayer)
  }

  var maskingLayer: some Shape {
    if isRound {
      return Circle()
    } else {
      return RoundedRectangle(cornerRadius: 10)
    }
  }
}

然而,这段代码无法编译,因为 maskingLayer 声明了一个不透明的返回类型,但在其主体中没有返回语句来推断底层类型。换句话说:Swift 期望 maskingLayer 始终是相同的类型。

可能的解决方案

类型擦除

解决这个问题的一种方法是引入一个类型擦除的 AnyShape 辅助工具,并将返回的 maskingLayer 擦除为 AnyShape。这种方法类似于 SwiftUI 内置的类型擦除 AnyView。

struct AnyShape: Shape {
  let _path: (CGRect) -> Path

  init<S: Shape>(_ shape: S) {
    _path = shape.path(in:)
  }

  func path(in rect: CGRect) -> Path {
    _path(rect)
  }
}

struct MyFancyView: View {
  let isRound: Bool

  var body: some View {
      // Fancy content here
        .mask(maskingLayer)
  }

  var maskingLayer: some Shape {
    if isRound {
      return AnyShape(Circle())
    } else {
      return AnyShape(RoundedRectangle(cornerRadius: 10))
    }
  }
}

正如你所看到的,这要求我们将原始形状包裹在 AnyShape 类型擦除包装器中,这并不是你见过的最漂亮的代码,但它确实有效,不是吗?🤷‍♂️

结果构建器来救援

解决这个问题的第二种方法是定义一个类似于 SwiftUI 自己的 @ViewBuilder 的结果构建器。这个库实现了 @ShapeBuilder@InsettableShapeBuilder 结果构建器,允许你摆脱类型擦除的形状包装器视图,甚至不需要 return 语句。用相应的result builder标记你的计算属性或函数,你就搞定了。

struct MyFancyView: View {
  let isRound: Bool

  var body: some View {
      // Fancy content here
        .mask(maskingLayer)
  }

  @ShapeBuilder var maskingLayer: some Shape {
    if isRound {
      Circle()
    } else {
      RoundedRectangle(cornerRadius: 10)
    }
  }
}

BuiltShape / BuiltInsettableShape

此外,这个库提供了 BuiltShapeBuiltInsettableShape 协议,它们与 SwiftUI 的 View 协议具有相似之处。它们定义了一个只读的 shape 计算属性,该属性用 @ShapeBuilder/@InsettableShapeBuilder 注解标记。

这允许你定义 BuiltShape,它们本身就是形状,并采用形状属性的形式。

struct MyFancyMask: BuiltShape {
  let isCircle: Bool

  var shape: some Shape {
    if isCircle {
      Circle()
    } else {
      RoundedRectangle(cornerRadius: 10)
    }
  }
}

安装

Swift Package

如果你想将 ShapeBuilder 添加到你的 Swift 包中,请将其作为依赖项添加到你的 Package.swift 中。

dependencies: [
  .package(
    url: "https://github.com/ohitsdaniel/ShapeBuilder.git",
    from: "0.1.0"
  )
],
targets: [
  .target(
    name: "MyAwesomePackage",
    dependencies: [
      "ShapeBuilder"
    ]
  )
]

Xcode

你可以通过 Xcode 将 ShapeBuilder 添加到你的项目中。打开你的项目,点击 File → Swift Packages → Add Package Dependency…,输入仓库 URL (https://github.com/ohitsdaniel/ShapeBuilder.git) 并将包产品添加到你的应用程序目标。

许可证

本库在 MIT 许可证下发布。有关详细信息,请参阅 LICENSE