官方文档:
struct ContentView: View { var body: some View { // This is inside a result builder VStack { Text("Hello World!") // VStack and Text are 'build blocks' } } }
@ViewBuilder var body: Self.Body { get }
@functionBuilder
或 @_functionBuilder
,本质是同一个东西。@resultBuilder struct AttributedStringBuilder { static func buildBlock(_ segments: NSAttributedString...) -> NSAttributedString { let string = NSMutableAttributedString() segments.forEach { string.append($0) } return string } }
@resultBuilder
属性标注一个结构体,然后我们必须要实现上面的这个方法,这个方法的含义是你想要如何通过 block 传递进来的数据构造 NSAttributedString
。传递进来的是一个或者多个 NSAttributedString
,我们通过遍历拼接之后返回。
OK,我们已经完成了最重要的一步。extension NSAttributedString { convenience init(@AttributedStringBuilder _ content: () -> NSAttributedString) { self.init(attributedString: content()) } }
AttributedStringBuilder
结构体已经变成了一个简单的函数,使用的时候用 @AttributedStringBuilder
来修饰,在闭包中返回一个或者多个 NSAttributedString
,生成一个拼接好的 NSAttributedString
。
为了让我们使用起来更加的方便,我们给 NSAttributedString
和 string
加上几个扩展方法,在使用字符串创建的时候更简洁。// MARK: - extension extension NSAttributedString { func apply(_ attributes: [NSAttributedString.Key:Any]) -> NSAttributedString { let mutable = NSMutableAttributedString(string: self.string, attributes: self.attributes(at: 0, effectiveRange: nil)) mutable.addAttributes(attributes, range: NSMakeRange(0, (self.string as NSString).length)) return mutable } func foregroundColor(_ color: UIColor) -> NSAttributedString { self.apply([.foregroundColor : color]) } func font(_ font: UIFont) -> NSAttributedString { self.apply([.font: font]) } } extension String { func foregroundColor(_ color: UIColor) -> NSAttributedString { NSAttributedString(string: self, attributes: [.foregroundColor : color]) } func font(_ font: UIFont) -> NSAttributedString { NSAttributedString(string: self, attributes: [.font: font]) } }
let str = NSAttributedString { "hello " .foregroundColor(.red) .font(.systemFont(ofSize: 14)) "world!" .foregroundColor(.label) .font(.systemFont(ofSize: 20, weight: .bold)) }
NSAttributedString
了,在 github 中的仓库就是使用的 @resultBuilder
,其核心就是步骤一中的代码。@resultBuilder
还有一些可选的实现方法,来完善构造器,例如我们如果返回一个可选值,该如何处理?如果想要像 swiftUI 一样支持 if-else,switch,该如何处理?// 对应 block 中没有值返回的情况 static func buildBlock(_ components: Component...) -> Component // 通过一个或多个值来构建 必须实现 static func buildBlock(_ components: Component...) -> Component Combines an array of partial results into a single partial result. A result builder must implement this method. // 通过可选值来构建,可以支持不包含 else 的 if 语句 static func buildOptional(_ component: Component?) -> Component Builds a partial result from a partial result that can be nil. Implement this method to support if statements that don’t include an else clause. // 支持通过条件来变化的值,通过下面这两个方法支持包含 switch 和 if-else 的语句 static func buildEither(first: Component) -> Component Builds a partial result whose value varies depending on some condition. Implement both this method and buildEither(second:) to support switch statements and if statements that include an else clause. static func buildEither(second: Component) -> Component Builds a partial result whose value varies depending on some condition. Implement both this method and buildEither(first:) to support switch statements and if statements that include an else clause. // 通过数组来构建 可以支持 forin 语句 static func buildArray(_ components: [Component]) -> Component Builds a partial result from an array of partial results. Implement this method to support for loops. // 通过 Expression 来生成,Expression 为自定义的表达式 具体可以看官方文档中的例子 static func buildExpression(_ expression: Expression) -> Component Builds a partial result from an expression. You can implement this method to perform preprocessing—for example, converting expressions to an internal type—or to provide additional information for type inference at use sites. // 用于对最外层的 `buildBlock` 结果的再包装。例如,让结果构建器隐藏一些它并不想对外的类型(转换成可对外的类型)。 static func buildFinalResult(_ component: Component) -> FinalResult Builds a final result from a partial result. You can implement this method as part of a result builder that uses a different type for partial and final results, or to perform other postprocessing on a result before returning it. // 如果提供了 `buildLimitedAvailability` 的实现,构建器提供了对 API 可用性检查(如 `if #available(..)`)的支持。 static func buildLimitedAvailability(_ component: Component) -> Component Builds a partial result that propagates or erases type information outside a compiler-control statement that performs an availability check. You can use this to erase type information that varies between the conditional branches.