Я создаю компонент SwiftUI и хочу, чтобы этот компонент имел свойство, реализующее протокол. В указанном сценарии использования c используется ось для диаграммы, основанная на масштабе.
Существует несколько конкретных реализаций шкалы, которые преобразуют данные из входной области в выходной диапазон. Два, с которых я начинаю, - это линейная шкала, которая преобразует из входной области 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 обобщенным c, или я не должен использовать связанный тип с протоколом?
Или есть другой способ подумать о структурировании этого кода для поддержки множества масштабных типов, с использованием протоколов и структур?
ОБНОВЛЕНИЕ : Я получил ответ на первоначальный вопрос, но он не совсем для меня. Я добавил определение generi c к типу включения (мои реализации Scale
).
Что мне непонятно, зачем это нужно? После того, как я добавил маркер c в структуру, ошибка компилятора исчезла. Я предполагаю, что это место, где было несколько вариантов, которые мог бы использовать быстрый компилятор, и сказать «да, я хочу, чтобы это было обобщенно c» - это один путь - какие возможны другие?
Я также заметил, что, хотя он был определен как обобщенный c класс, указанный мной c класс, который я использовал, часто выводился компилятором swift. Поэтому мне не нужно было полностью указывать тип с синтаксисом c. Например, я мог бы использовать
LinearScale()
вместо LinearScale<Double>
(), и это вывело бы правильный шаблон c. Это ожидается?