Эффект согласованной геометрии с AsyncImage iOS 15
Рассмотрим следующий пример:
struct ContentView: View {
@State var showSplash: Bool = true
@Namespace var animationNamespace
var body: some View {
ZStack {
if showSplash {
GeometryReader { geometry in
AsyncImage(url: URL(string: "https://picsum.photos/seed/864a5875-6d8b-43d6-8d65-04c5cfb13f3b/1920/1440")) { image in
image.resizable()
.scaledToFill()
.matchedGeometryEffect(id: "SplashImage", in: animationNamespace)
.transition(.move(edge: .bottom))
.frame(width: geometry.size.width)
.transition(.move(edge: .bottom))
.edgesIgnoringSafeArea(.all)
.clipped()
} placeholder: {
Color.gray
}
}
.onTapGesture {
toggleSplashScreen(false)
}
} else {
ScrollView {
GeometryReader { geometry in
AsyncImage(url: URL(string: "https://picsum.photos/seed/864a5875-6d8b-43d6-8d65-04c5cfb13f3b/1920/1440")) { image in
image
image
.resizable()
.scaledToFill()
.matchedGeometryEffect(id: "SplashImage", in: animationNamespace)
.transition(.move(edge: .bottom))
} placeholder: {
Color.gray
}
.frame(width: geometry.size.width, height: 400)
.clipped()
}
.edgesIgnoringSafeArea(.all)
.onTapGesture {
toggleSplashScreen(true)
}
}
}
}
}
}
С помощью вспомогательного метода здесь:
private extension ContentView {
func toggleSplashScreen(_ toggle: Bool) {
withAnimation(.spring(response: 0.85, dampingFraction: 0.95)) {
showSplash = toggle
}
}
}
Это производит:
Я заметил здесь две вещи, которые хотел бы исправить
- Мигающий белый эффект при переходе между двумя состояниями.
- Я заметил, так как мы используем
AsyncImage
, когдаshowSplash
изменяет AsyncImages, только иногда попадает в блок. В результате переход становится действительно прерывистым. Я проверил это со статическим изображением из файла ресурсов, и переход стал плавным. Я также попытался создать механизм кэширования для AsyncImage, но все еще имел проблемы с его нажатиемplaceholder
блокировать иногда.
Хотелось бы услышать любые идеи :) Спасибо!
1 ответ
Есть несколько вещей, которые, я думаю, вы могли бы сделать, чтобы улучшить это.
Во-первых, вы немного боретесь с тем, как SwiftUI поддерживает идентичность представления. Один из способов, которыми SwiftUI определяет, когда он может повторно использовать существующую структуру, а не воссоздавать структуру, - это ее расположение в иерархии представлений. Итак, когда вы переключаете свою структуру, вы переходите от:
GeometryReader
AsyncImage
к
ScrollView
GeometryReader
AsyncImage
В результате система считает, что существует два представления, и поэтому каждый раз перестраивает изображение (и перезагружает изображение). Я думаю, что именно отсюда появляются ваши белые вспышки, поскольку вы видите серый заполнитель в середине анимации. Если бы вы могли оставить представление прокрутки на месте, возможно, отключив прокрутку, когда она не нужна (если это возможно), тогда ОС могла бы сохранить идентичность. (см. https://developer.apple.com/videos/play/wwdc2021/10022/)
Это подводит вас ко второй области исследования. замечательно по удобству, которое дает вам при загрузке контента из сети. К сожалению, это не ускоряет общение. Ваша цель должна заключаться в том, чтобы выходить в сеть как можно реже.
Прямо сейчас ваша стратегия изменения размера фокусируется на изменении размера изображения. Это означает, что при каждом переходе вы «попадаете в сеть» (прочтите, что ваш код идет по медленной, пыльной, грунтовой дороге). Вместо изменения размера изображения вы должны просто загрузить изображение один раз (медленная часть) и изменить размер представления, в котором оно отображается. Общая идея заключается в том, чтобы позволить загрузить изображение, а затем управлять тем, как изображение анимируется, путем анимации кадра представления.
Вот где я получаю меньше пользы. Я не знаю достаточно, чтобы понять, сможет ли он реализовать эту стратегию. Вроде так и должно быть ... но я не знаю, что это так. Возможно, вам придется прибегнуть к загрузке и сохранению изображения как состояния отдельно от представления, которое его представляет.
Поэтому я советую ограничить количество перезагрузок сетевых данных. Это включает в себя помощь SwiftUI в сохранении идентичности