Как определить протокол с общей функцией и некоторым представлением в качестве возвращаемого типа

avatar
Dot Freelancer
1 июля 2021 в 18:01
407
2
1

Я хочу создать протокол с одной функцией для следующего

@ViewBuilder
func navigate<T: View>(content: () -> T) -> some View {
    switch self{
    case .list:
        NavigationLink(destination: Text("Test")){ content() }
    default:
        EmptyView()
    }
}

Это моя попытка, и она не работает

protocol Test {
    associatedtype Result: View

    func navigate<T: View>(content: () -> T) -> Result
}
Источник
SeaSpell
1 июля 2021 в 18:15
0

Протокол говорит, что вы будете придерживаться определенных правил. В этом случае вы говорите, что у меня<Any> будет определен метод навигации, чтобы вы могли вызывать его для меня<Any>. Другими словами, каждое представление, которое вы хотите придерживаться этого протокола, должно иметь этот метод в собственном файле определения <The View>. Если это ваше намерение, вы можете просто объявить все <Any>, с которыми вы работаете, с утверждениями о соответствии тесту.

Dot Freelancer
1 июля 2021 в 18:21
0

Понятно, тогда как я могу заставить детей реализовать функцию навигации?

SeaSpell
1 июля 2021 в 18:32
0

На самом деле навигационная ссылка - это структура без наследования, поэтому я не уверен, что это лучший способ справиться с этим. Вы можете сделать расширение и вернуть дополнительную навигационную ссылку.?.

Ответы (2)

avatar
Rob Napier
1 июля 2021 в 20:31
1

Это явно запрещено разделом Associated Type Inference предложения непрозрачных типов результатов. Из предложения:

Вывод связанного типа может вывести непрозрачный тип результата только для неуниверсального требования, поскольку непрозрачный тип параметризуется собственными универсальными аргументами функции. Например, в:

protocol P {
 associatedtype A: P
 func foo<T: P>(x: T) -> A
}

struct Foo: P {
 func foo<T: P>(x: T) -> some P {
   return x
 }
}

не существует единого базового типа для вывода A, поскольку возвращаемый тип foo может изменяться с помощью T.

Чтобы сделать это более конкретным для вашего вопроса, чтобы соответствовать Test, должен быть только один тип, который может быть назначен Result. Однако ваш возвращаемый тип является универсальным, поэтому он зависит от того, что передается. Фактический (непрозрачный) возвращаемый тип navigate:

_ConditionalContent<NavigationLink<T, Text>  EmptyView>

Но T является параметром типа и изменяется в зависимости от того, как вызывается navigate. Таким образом, нет одного типа, который можно было бы присвоить Result.

.

Вам понадобится что-то, что может возвращать один непараметризованный тип. В приведенном вами примере это, вероятно, AnyView, что раздражает.

Тем не менее, то, что вы здесь написали, на самом деле не похоже на протокол. Это очень похоже на просто функцию. Я бы много думал о том, сколько разных способов можно написать navigate. Если бы все реализовывали это одинаково, это была бы просто функция. (Если вы приведете другой пример соответствующего типа, это может помочь разработать лучший подход.)

SeaSpell
1 июля 2021 в 20:42
0

Святое дерьмо. Какой ты ракетный хирург? Не знаю, что ты только что сказал, но звучит правильно🤔. Очень хорошо сформулировано. Вы получаете голос от меня.

Dot Freelancer
1 июля 2021 в 23:25
0

Спасибо за подробный ответ, очень помогло. Я думал об абстрагировании навигации от представлений SwiftUI, поэтому я хотел создать перечисление с функцией навигации для каждого модуля.

Rob Napier
1 июля 2021 в 23:33
0

Обычно я не рекомендую перечисление для таких вещей. Перечисление предполагает наличие фиксированного набора разумных значений. Когда действительно может быть произвольно длинный список значений, который может расти со временем, это действительно структура (и может быть один тип структуры со многими статическими экземплярами). Я должен был бы увидеть, как вы пытались вызвать это, чтобы знать, как реализовать это лучше всего.

avatar
SeaSpell
1 июля 2021 в 19:12
0

Вы можете сделать это, когда вызываете его в пункте назначения.

extension View {
     func navigate< Destination: View, Label: View>(content: Label) -> some View   {
          NavigationLink(destination: Destination) { content.    }
     }
}

Проблема в том, что вы не можете узнать вызывающую сторону внутри метода, потому что вы расширяете протокол.