#swift #swiftui
#swift #swiftui
Вопрос:
Я работаю над пользовательским контейнером, но получаю сообщение об ошибке:
Не удалось выполнить диагностику для выражения; пожалуйста, отправьте отчет об ошибке
с помощью следующего кода (местоположение ошибки находится внизу кода ниже):
struct ZoomCardView<Content: View>: View {
let contentDefault: ((Binding<Bool>)) -> Content
let contentZoomed: (Binding<Bool>) -> Content
init(@ViewBuilder defaultContent: @escaping (Binding<Bool>) -> Content, @ViewBuilder zoomed: @escaping (Binding<Bool>) -> Content) {
self.contentDefault = defaultContent
self.contentZoomed = zoomed
}
@State private var isShowing: Bool = false
var body: some View {
HStack {
if isShowing {
self.contentZoomed($isShowing)
.transition(.scale(scale: 0, anchor: .center))
} else {
self.contentDefault($isShowing)
.transition(.scale(scale: 0, anchor: .center))
}
}
}
}
struct DefaultView: View {
var body: some View {
HStack {
Text("Default")
}
}
}
struct ZoomView: View {
var body: some View {
HStack {
Text("Zoom")
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View { <----------- *** Error
ZoomCardView { isShowing in
DefaultView()
} zoomed: { isShowing in
ZoomView()
}
.preferredColorScheme(.dark)
}
}
Я заметил, что ContentView_Previews
, если я передаю одно и то же представление, ошибка исчезает. Например:
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ZoomCardView { isShowing in
ZoomView()
} zoomed: { isShowing in
ZoomView()
}
.preferredColorScheme(.dark)
}
}
Почему я не могу передавать разные представления, когда ZoomCardView
ожидает параметр типа <Content: View>
? Что я хотел бы сделать, так это в конечном итоге указать a View
или любой из типов стека (т. Е. ZStack
VStack
Или HStack
), Предоставляя callsite окончательный контроль над тем, что должно быть показано. Заранее спасибо!
Ответ №1:
Вам нужно использовать два общих параметра, если вы хотите передать два разных представления.
Вместо:
struct ZoomCardView<Content: View>: View
используйте:
struct ZoomCardView<ContentDefault, ContentZoomed>: View where ContentDefault: View, ContentZoomed: View
Полный пример:
struct ZoomCardView<ContentDefault, ContentZoomed>: View where ContentDefault: View, ContentZoomed: View {
let contentDefault: ((Binding<Bool>)) -> ContentDefault
let contentZoomed: (Binding<Bool>) -> ContentZoomed
init(
@ViewBuilder defaultContent: @escaping (Binding<Bool>) -> ContentDefault,
@ViewBuilder zoomed: @escaping (Binding<Bool>) -> ContentZoomed
) {
self.contentDefault = defaultContent
self.contentZoomed = zoomed
}
@State private var isShowing: Bool = false
var body: some View {
HStack {
if isShowing {
self.contentZoomed($isShowing)
.transition(.scale(scale: 0, anchor: .center))
} else {
self.contentDefault($isShowing)
.transition(.scale(scale: 0, anchor: .center))
}
}
}
}
Комментарии:
1. Спасибо @pawello2222! Отлично работает. TBH, я все еще не понимаю, зачем это нужно. Если представления имеют тип
View
, а ZoomCardView был определен как<Content: View>: View
, почему компилятор проводит различие? Мне это очень любопытно. Еще раз спасибо!2. @titusmagnus Если вы посмотрите на init , он ожидает два закрытия, оба с одинаковым типом возвращаемого значения (Content ). Таким образом, вы можете передавать только один и тот же тип для обоих замыканий. А DefaultView и ZoomView имеют разные типы. Создание второго универсального типа позволяет вам использовать два разных типа для этих замыканий.
3. Это имеет смысл, но я думаю, что меня смущает то, что, поскольку оба они являются a
View
иinit
требуютContent
типаView
, то, даже если оба представления имеют другой тип, они все равно имеют типView
. Вы видите, в чем заключается мое замешательство? Еще раз спасибо!4. @titusmagnus, я тоже заметил это в вашем предыдущем вопросе.
Content
определяется как общий тип, который представляет какой-то вид представления — конкретный вид определяется тем, как вызывается ваш init (например, может бытьText
, илиVStack<Text>
, что бы это ни было). Но вам нужны два разных представления — каждое из них является инициализацией другого типа, поэтому для представления этого вам нужны два общих типа5. @titusmagnus Но вы не можете использовать
View
как ограничение, поскольку это протокол с associatedtype . Я предлагаю вам прочитать общие и непрозрачные типы