【发布时间】:2020-01-20 19:06:27
【问题描述】:
我一直在创建一个 SwiftUI 组件,我希望这个组件有一个实现协议的属性。具体的用例是为图表绘制一个轴,它基于一个比例。
有几种具体的规模实现将数据从输入域转换为输出范围。我开始使用的两个是线性刻度,它从Double 输入域转换为用Double 表示的输出范围。另一种是基于日期/时间的比例,它从基于Date 的输入域转换为由Double 表示的输出范围。
规模是协议,我尝试将其大致定义为:
public protocol Scale {
associatedtype InputDomain: Comparable // input domain
var isClamped: Bool { get }
// input values
var domain: ClosedRange<InputDomain> { get }
// output values
var range: ClosedRange<Double> { get }
/// converts a value between the input "domain" and output "range"
///
/// - Parameter inputValue: a value within the bounds of the ClosedRange for domain
/// - Returns: a value within the bounds of the ClosedRange for range, or NaN if it maps outside the bounds
func scale(_ inputValue: InputDomain) -> Double
/// converts back from the output "range" to a value within the input "domain". The inverse of scale()
///
/// - Parameter outputValue: a value within the bounds of the ClosedRange for range
/// - Returns: a value within the bounds of the ClosedRange for domain, or NaN if it maps outside the bounds
func invert(_ outputValue: Double) -> InputDomain
/// returns an array of the locations within the ClosedRange of range to locate ticks for the scale
///
/// - Parameter count: a number of ticks to display, defaulting to 10
/// - Returns: an Array of the values within the ClosedRange of the input range
func ticks(count: Int) -> [InputDomain]
}
LinearScale 和 TimeScale 结构符合协议,分别定义了 typealias InputDomain = Double 和 typealias InputDomain = Date。
当我尝试使用此协议来描述与 SwiftUI 组件更通用的结构(比例)时,问题就出现了:
public struct AxisView: View {
let scale: Scale
public var body: some View { ... }
}
编译器提供错误:
Protocol 'Scale' can only be used as a generic constraint because it has Self or associated type requirements
我不确定解决此问题的最佳方法,即解决编译器错误/约束。我应该做些什么来使 SwiftUI 组件成为泛型,还是不应该使用协议的关联类型?
或者是否有其他方法可以考虑使用协议和结构来构建此代码以支持各种比例类型?
更新:我得到了原始问题的答案,但它并不完全适合我。我将泛型定义添加到封闭类型(我的Scale 实现)。
我不清楚为什么需要这样做?在封闭结构上添加通用标记后,编译器错误消失了。我假设这是一个 swift 编译器可以采取多种选择的地方,并告诉它“是的,我希望这是一个通用的”是一条路径 - 其他可能的路径是什么?
我还注意到,即使它被定义为泛型类,我使用的特定类也经常被 swift 编译器推断出来。所以我不需要用通用语法完全指定类型。例如,我可以使用
LinearScale() 而不是LinearScale<Double>(),它会推断出正确的泛型。这是预期的吗?
【问题讨论】:
-
这是一种糟糕的风格,但您可以将 scale 声明为
Any并在必要时打开它。
标签: swift generics swift-protocols