как правильно использовать протокол, имеющий связанный тип, для описания свойства в другом объекте - PullRequest
0 голосов
/ 20 января 2020

Я создаю компонент 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. Это ожидается?

1 Ответ

2 голосов
/ 20 января 2020

Вы также должны сделать свое представление обобщенным c:

public struct AxisView<ScaleType: Scale>: View {    
    let scale:  ScaleType    
    public var body: some View { ... }
}
...