Вопрос или проблема
У меня есть представление, в котором я вычисляю размер изображения. Эти размеры должны быть размерами для полноэкранного покрытия. Проблема в том, что переменная @State не изменила значение, когда я присвоил ей новое значение.
Я что-то читал, и, возможно, переменная @State не подходит для этого. Но что еще я могу сделать, чтобы достичь своей цели.
struct ExerciseDetailTopViewiPad: View {
let exercise: ExerciseData
@State private var showExerciseImageViewer = false
@State private var maxWidth: CGFloat = 533
@State private var maxHeight: CGFloat = 850
var body: some View {
Image(uiImage: exercise.exerciseImage.imageExerciseFromBase64)
.onTapGesture {
let dim: CGFloat = exercise.exerciseImage.imageExerciseFromBase64.size.height / exercise.exerciseImage.imageExerciseFromBase64.size.width
let newWidth = maxHeight / dim - 30 // 30 высота верхнего меню
maxWidth = newWidth.rounded(.up) // ==> ТУТ ПРОБЛЕМА - присвоение не изменило значение переменной
showExerciseImageViewer = true
}
.fullScreenCover(isPresented: $showExerciseImageViewer) {
ExerciseDetailImageVieweriPad(exercise: exercise)
.frame(maxWidth: maxWidth, maxHeight: maxHeight)
.clipShape(RoundedRectangle(cornerRadius: 10))
.shadow(color: Color(white: 0.7), radius: 20)
.presentationBackground {Rectangle().fill(.clear)}
}
}
}
Ответ или решение
Вопрос о том, возможно ли использование fullScreenCover
с динамическим размером фрейма, является актуальным в рамках разработки приложений на SwiftUI. Рассмотрим подробно вашу проблему и предложим пути ее решения.
Проблема
Вы сталкиваетесь с тем, что значение переменной @State
(maxWidth
и maxHeight
) не обновляется при попытке задать новое значение в обработчике жеста нажатия (onTapGesture
). Текущий подход, который использует @State
, может не подойти в этой ситуации, поскольку SwiftUI требует определённой структуры для управления состоянием интерфейса.
Решение
-
Использование оберточных свойств (Binding): Вместо изменения значений
maxWidth
иmaxHeight
напрямую в обработчике, вы можете использовать оберткуBinding
. Для этого объявите переменные как@State
в родительской вью и передайте их через инициализатор в дочернюю вью. -
Использование
@State
для динамического размера: Проверьте, корректно ли у вас изменяются значения в вашемonTapGesture
. Иногда значение может обновляться, но не инициировать повторный рендеринг пользовательского интерфейса. В таком случае, стоит использовать@ObservedObject
или@EnvironmentObject
, если у вас имеются данные, которые могут изменяться вне текущего View.
Пример кода
Вот как вы можете изменить ваш текущий код для более надежного обновления размеров:
struct ExerciseDetailTopViewiPad: View {
let exercise: ExerciseData
@State private var showExerciseImageViewer = false
@State private var maxWidth: CGFloat = 533
@State private var maxHeight: CGFloat = 850
var body: some View {
Image(uiImage: exercise.exerciseImage.imageExerciseFromBase64)
.onTapGesture {
let dim: CGFloat = exercise.exerciseImage.imageExerciseFromBase64.size.height /
exercise.exerciseImage.imageExerciseFromBase64.size.width
let newWidth = maxHeight / dim - 30 // 30 высота верхнего меню
maxWidth = newWidth.rounded(.up) // Изменение значения
showExerciseImageViewer = true
}
.fullScreenCover(isPresented: $showExerciseImageViewer) {
ExerciseDetailImageVieweriPad(exercise: exercise, maxWidth: maxWidth, maxHeight: maxHeight)
.frame(maxWidth: maxWidth, maxHeight: maxHeight)
.clipShape(RoundedRectangle(cornerRadius: 10))
.shadow(color: Color(white: 0.7), radius: 20)
.presentationBackground { Rectangle().fill(.clear) }
}
}
}
struct ExerciseDetailImageVieweriPad: View {
let exercise: ExerciseData
var maxWidth: CGFloat
var maxHeight: CGFloat
var body: some View {
// Дизайн вашего вью для отображения изображения
// реализуйте свой интерфейс здесь с использованием maxWidth и maxHeight
}
}
Заключение
Очень важно помнить, что @State
переменные являются внутренними состояниями для конкретного вью. Ваша проблема может заключаться в том, что система SwiftUI не отслеживает изменения в состоянии, если они происходят в пределах контекста, который не вызывает повторный рендер. Используя @Binding
, @ObservedObject
или передавая их напрямую через параметры, вы можете добиться нужного результата.
В данном контексте важно также помнить о тестировании — убедитесь, что ваши изменения работают на реальных устройствах, так как поведение эмулятора может отличаться от поведения на физических устройствах. Удачи в разработке!