所以使用reduce(into:_:)
時要注意:「一定要透過 updateAccumulatingResult 來更新 partialResult」,否則回傳值會是原來的 initialResult
// `reduce(_:_:)`
func reduce<Result>(
_ initialResult : Result,
// ⭐️ combine function (with return value)
_ nextPartialResult: (Result, Element) throws -> Result
) rethrows -> Result
// `reduce(into:_:)`
func reduce<Result>(
into initialResult: Result,
_ updateAccumulatingResult: (
_ partialResult: inout Result, // ⭐️ inout parameter
Self.Element
) throws -> () // ⭐️ no return value
) rethrows -> Result
我們用合併 SwiftUI 中的 transitions 來舉例,說明這兩者的用法有哪些不同。在合併 SwiftUI 中的 transitions 時,使用 SwiftUI 原有的語法,顯得太過冗長:
AnyTransition
.move(edge: .leading)
.combined(with: .opacity)
.combined(with: .scale)
// 無法自行推斷 (infer) AnyTransition 型別的時候:
AnyTransition(.move(edge: .leading), .opacity, .scale)
// 可以自行推斷 AnyTransition 型別的時候:
.combined(.move(edge: .leading), .opacity, .scale)
extension AnyTransition {
// ⭐️ combining transitions
// AnyTransition([.scale, .opacity, ...]) ........ (1)
init(_ transitions: [AnyTransition]) {
// use reduce() or reduce(into:)
}
// 讓可以使用的語法更豐富,在可以使用 AnyTransition 的地方,直接用:
// .combined(.move, .scale, ...)
public static func combined(_ transitions: AnyTransition...) -> AnyTransition {
.init(transitions)
}
}
extension AnyTransition: ExpressibleByArrayLiteral {
// convenience init
// AnyTransition(.move, .scale, .opacity, ...)
public init(arrayLiteral elements: AnyTransition...) {
self.init(elements) // 使用 (1)
}
}
init(_ transitions: [AnyTransition]) {
let intialValue = AnyTransition.identity
// ⭐️ 使用 `reduce(_:_)`
self = transitions.reduce(intialValue) { (result, transition) in
// ⭐️ 下面的結果會「自動成為新的 `result`」,不需手動改寫 `result`
// 所以官方稱這個部分的 closure 為:`nextPartialResult`。
result.combined(with: transition) // ⭐️ implicit return
}
}
init(_ transitions: [AnyTransition]) {
var intialValue = AnyTransition.identity
// ⭐️ 使用 `reduce(into:_:)`
self = transitions.reduce(into: intialValue) { (result, transition) in
// ⭐️ 記得手動改寫 `result` 變數,否則效果並不會累加。
// 因為如果我們不寫 `result = ...`,這時 `result` 變數
// 並不會自動改變,所以官方稱這個部分為 `updateAccumulatingResult`
// 而不是 `nextPartialResult`。
result = result.combined(with: transition) // ⭐️ no return value
}
}