SwiftUI: Как сделать так, чтобы мое закрытие возвращало «Content» вместо «some View» при вызове из другого инициализатора? - PullRequest
1 голос
/ 22 марта 2020

В настоящее время я изучаю SwiftUI и столкнулся с проблемой.

В приведенном ниже коде у меня есть два инициализатора. Первый работает (если я закомментирую второй достаточно долго, чтобы протестировать код). Второй нет. Выдает ошибку компилятора, говорящую « Невозможно присвоить значение типа« некоторое представление »типу« Содержимое ». »

Что я здесь не так делаю? Я могу успешно вызвать первый инициализатор из-за пределов структуры, используя Infobox() { Text("Some Text") }, который мне кажется точно таким же синтаксисом, который я использую при попытке вызвать его из второго инициализатора. Независимо от того, сколько я гуглю, я не могу понять это.

Я бы очень признателен за любую помощь, которую вы можете оказать.

struct Infobox<Content>: View where Content: View {

    let content: Content

    var body: some View {
        content
    }

    init(@ViewBuilder _ content: @escaping () -> Content) {
        self.content = content()
    }

    init(withHeader headerText: String, @ViewBuilder _ content: @escaping () -> Content) {
        self.init() {
            VStack(alignment: .leading) {
                Text(headerText)
                    .font(.headline)
                }
                content()
            }
        }
    }
}

1 Ответ

1 голос
/ 22 марта 2020

Что вы имеете в виду, вероятно, это:

init<T: View>(withHeader headerText: String, @ViewBuilder _ content: @escaping () -> T) 
    where Content == VStack<TupleView<(Text, T)>> {
    self.init {
        VStack(alignment: .leading) {
            Text(headerText)
                .font(.headline)
            content()
        }
    }
}

Представление, созданное этим инициализатором, всегда будет иметь форму (И да)

InfoBox<VStack<TupleView<(Text, SomeOtherView)>>>

Другими словами, этот init решает, что Content должно быть. Так что это применимо только к InfoBox es с Content == ThatLongTypeName, отсюда и ограничение.

Обратите внимание, что вызывающая сторона может указать тип SomeOtherView, но это не Content! Content - это VStack<TupleView<(Text, SomeOtherView)>>. Поэтому добавляется дополнительный универсальный параметр c и тип возвращаемого значения замыкания изменяется.


IMO, было бы намного проще, если бы вы просто сделали это:

struct Infobox<Content>: View where Content: View {

    let content: () -> Content
    let headerText: String

    var body: some View {
        VStack(alignment: .leading) {
            if !headerText.isEmpty {
                Text(headerText)
                    .font(.headline)
            }
            content()
        }
    }

    // note the optional parameter here
    init(withHeader headerText: String = "", @ViewBuilder _ content: @escaping () -> Content) {
        self.content = content
        self.headerText = headerText
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...