Привязка принимает перечисления, соответствующие одному протоколу в Swift - PullRequest
2 голосов
/ 05 марта 2020

У меня есть два перечисления, которые соответствуют одному протоколу, наблюдаемый объект, который содержит массивы этих перечислений, и представление, которое я хочу принять оба перечисления.

Наблюдаемый объект:

class OptionsStore: ObservableObject {
    @Published var topicOptions: [TopicOption]
    @Published var levelOptions: [LevelOption]
}

Одно из перечислений и протокола:

enum TopicOption: String, CaseIterable, OptionCompatible {
    case music, food, traffic

    ...
}

protocol OptionCompatible {
    ...
}

Просмотры:

struct MainView: View {
    @EnvironmentObject var optionsStore: OptionsStore

    var body: some View {
        OptionsView(
            options: TopicOption.allCases,                   // no error
            activeOptions: self.$optionsStore.topicOptions   // error
        )
    }
}

struct OptionsView: View {
    let options: [OptionCompatible]
    @Binding var activeOptions: [OptionCompatible]
}

Ошибка

Невозможно преобразовать значение типа 'Binding <[TopicOption]> 'к ожидаемому типу аргумента' Binding <[OptionCompatible]> '

Мне нужно, чтобы OptionsView принял оба перечисления. Почему это go хорошо, вызывая stati c allCases, а не когда я передаю массив этих перечислений? Как мне это исправить?

1 Ответ

3 голосов
/ 05 марта 2020

Обратите внимание, что разница между этими двумя строками:

options: TopicOption.allCases,                   // no error
activeOptions: self.$optionsStore.topicOptions   // error

заключается в том, что первая строка не содержит Binding. Обычно массив подтипов может быть назначен переменной типа массива супертипов. Это не верно для Binding<[Subtype]> и Binding<[Supertype]>.

Это потому, что Binding нужно не только получить значение, которое вы передаете. Это также устанавливает это. Вспомните общий сценарий передачи Binding<String> в TextField. Значение Binding изменится, когда пользователь введет текст, верно?

OptionsView объявляет Binding<[OptionCompatible]>. Это говорит о том, что он может добавить какой-то тип OptionCompatible в массив. Но вы передаете это [TopicOptions]! Например, если OptionsView хочет добавить LevelOption в массив, он не может!

Вы можете сказать: «Но я уверен, что мой OptionsView будет добавлять только объекты правильного типа! " Ну, способ сказать это компилятору - сделать OptionsView generi c:

struct OptionsView<OptionType: OptionCompatible>: View {
    let options: [OptionType]
    @Binding var activeOptions: [OptionType]

}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...