我一直在创建SwiftUI组件,并且我希望该组件具有实现协议的属性。特定的用例是绘制图表的Axis(基于比例尺)。
有几种具体的规模实现,可以将数据从输入域转换到输出范围。我首先使用的两个是线性标度,它将从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组件成为通用的,还是不应该在协议中使用关联的类型?
或者还有其他方法可以考虑使用协议和结构来构造此代码以支持各种比例类型吗?
UPDATE:我得到了原始问题的答案,但对我来说并不完全是胶凝。我将通用定义添加到了封闭类型(我的Scale
实现)。
我不清楚为什么需要这样做?在将通用标记添加到封闭结构上之后,编译器错误消失了。我假设这是swift编译器可以采用的几种选择的地方,并告诉它“是的,我希望这是通用的”是一条路径-可能还有其他选择吗?
我还注意到,即使将其定义为通用类,但我经常使用的特定类是由swift编译器推断出来的。因此,我不需要使用通用语法完全指定类型。例如,我可以使用
LinearScale()
而不是LinearScale<Double>
(),它将推断出正确的泛型。这是预期的吗?
您还应该使视图通用:
public struct AxisView<ScaleType: Scale>: View {
let scale: ScaleType
public var body: some View { ... }
}