Как мне работать в Swift 5 с параметрами функции протокола, которые используют протоколы со связанными типами (например, .pickerStyle())

Я использую SwiftUI и хотел бы установить pickerStyle представления в зависимости от количества элементов в средстве выбора. С небольшим количеством элементов SegmentedPickerStyle() идеален, с большим количеством WheelPickerStyle() лучше.

}.pickerStyle(productsObserver.product.productFamilies?.count ?? 0 < 5 ? SegmentedPickerStyle() : WheelPickerStyle())

Сигнатура функции гласит: func pickerStyle<S>(_ style: S) -> some View where S : PickerStyle, которая, как я узнал, использует универсальную сигнатуру функции, потому что PickerStyle использует связанный тип.

Это не должно быть такой сложной проблемой, и, вероятно, это не так - протоколы должны работать так просто = , но я этого не вижу. Любая помощь высоко ценится!


person Emwee    schedule 04.04.2021    source источник
comment
pickerStyle — это общий метод, который принимает конкретный тип (во время компиляции), который соответствует PickerStyle. Так что не может быть ни SegmentedPickerStyle, ни WheelPickerStyle — должно быть и то, и другое. Что бы это ни было, компилятор создаст конкретный метод, например. pickerStyle<SegmentedPickerStyle>.   -  person New Dev    schedule 04.04.2021
comment
Спасибо за ELI5, теперь я понял. Можете ли вы также помочь мне с решением? Думал о внедрении моего собственного класса (например, DynamicPickerStyle), но я думаю, это просто смещает проблему?   -  person Emwee    schedule 06.04.2021


Ответы (1)


pickerStyle — это общий метод, который принимает конкретный тип (во время компиляции), который соответствует PickerStyle. Таким образом, это не может быть ни SegmentedPickerStyle, ни WheelPickerStyle (определяется во время выполнения) — должно быть либо одно, либо другое.

Таким образом, одним из предложений было бы создать модификатор представления и условно применить стиль средства выбора. Критическое отличие здесь в том, что он возвращает условное представление типа _ConditionalContent<TrueContent, FalseContent>.

struct PickerStyleOption<P1: PickerStyle, P2: PickerStyle>: ViewModifier {
    let predicate: () -> Bool
    let style1: P1
    let style2: P2
    
    @ViewBuilder
    func body(content: Content) -> some View {
        if predicate() {
            content
                .pickerStyle(style1)
        } else {
            content
                .pickerStyle(style2)
        }
    }
}

Для удобства можно создать расширение:

extension View {
    func pickerStyleOption<P1: PickerStyle, P2: PickerStyle>(
            _ condition: @autoclosure @escaping () -> Bool,
            then style1: P1, 
            else style2: P2) -> some View {

        self.modifier(
            PickerStyleOption(predicate: condition, style1: style1, style2: style2)
        )
    }
}

И используйте его так:

Picker(...) {
   ...
}
.pickerStyleOption((productsObserver.product.productFamilies?.count ?? 0) < 5, 
   then: SegmentedPickerStyle(), else: WheelPickerStyle())

person New Dev    schedule 06.04.2021
comment
Спасибо! Имеет смысл. Это звучит как план. Я попробую это в выходные и дам вам знать на следующей неделе, если у меня возникнут дополнительные вопросы. - person Emwee; 09.04.2021
comment
Чтобы вернуться к этому проекту, потребовалось больше недели. Большое спасибо за этот элегантный обходной путь, а также за проницательное объяснение! - person Emwee; 02.05.2021