Как мне получить доступ к изображению в AsyncImage, чтобы сохранить изображение после его получения? (swiftUI)
В настоящее время я пытаюсь найти способ сохранить изображение из AsyncImage, чтобы сразу же его кэшировать (без использования отдельного процесса извлечения изображений).
Это мой код для AsyncImage и расширения в представлении, которое я использую, чтобы попытаться сохранить его.
AsyncImage(url: asyncUrl) { phase in
switch phase {
case .empty:
ProgressView()
case .success(let image):
image
.resizable()
.scaledToFit()
urlImageModel.image = image.snapshot()
case .failure:
Image(uiImage: UrlImageView.defaultImage!)
.resizable()
.scaledToFit()
@unknown default:
Image(uiImage: UrlImageView.defaultImage!)
.resizable()
.scaledToFit()
}
}
extension View {
func snapshot() -> UIImage {
let controller = UIHostingController(rootView: self)
let view = controller.view
let targetSize = controller.view.intrinsicContentSize
view?.bounds = CGRect(origin: .zero, size: targetSize)
view?.backgroundColor = .clear
let renderer = UIGraphicsImageRenderer(size: targetSize)
return renderer.image { _ in
view?.drawHierarchy(in: controller.view.bounds, afterScreenUpdates: true)
}
}
}
К сожалению, когда я помещаю код в case.success для сохранения изображения в моем urlImageModel, он дает такую ошибку:
Тип '()' не может соответствовать 'View' в отношении закрытия "изображения".
Приветствуются любые идеи о том, как сохранить изображение из AsyncImage!
3 ответа
В
content
закрытие, к которому вы переходите
AsyncImage
это
@ViewBuilder
. Это означает, что каждое утверждение, не являющееся декларацией (например,
let x = ...
) или поток управления (например,
if blah { ... }
) должно быть выражением, которое оценивает некоторые. Проблема в этом заявлении:
urlImageModel.image = image.snapshot()
Это не декларация или поток управления. Это задание. Swift рассматривает его так, как будто он оценивает значение (также написано
()
, пустой кортеж) и
Void
это не .
Вы можете исправить это, заключив в объявление. Но еще одна вещь, на которую следует обратить внимание, - это то, что вы никогда не должны изменять объекты модели во время фазы повторного отображения SwiftUI (во время которой он запрашивает
View
для своего
body
). Так что вы также должны отправить его на всякий случай. Отправленный блок будет запущен позже, вне фазы повторного отображения SwiftUI.
let _ = DispatchQueue.main.async {
urlImageModel.image = image.snapshot()
}
вы можете просто использовать это, у меня хорошо работает:
image
.resizable()
.scaledToFit()
.onAppear {
urlImageModel.image = image.snapshot()
}
Вообще говоря, вы можете немного изменить свое расширение:
extension View {
func snapshot(_ block: (UIImage) -> Void) -> Self {
let controller = UIHostingController(rootView: self)
let view = controller.view
let targetSize = controller.view.intrinsicContentSize
view?.bounds = CGRect(origin: .zero, size: targetSize)
view?.backgroundColor = .clear
let renderer = UIGraphicsImageRenderer(size: targetSize)
block(renderer.image { _ in
view?.drawHierarchy(in: controller.view.bounds, afterScreenUpdates: true)
})
return self
}
}
И используйте вот так:
image
.resizable()
.scaledToFit()
.snapshot { image in
urlImageModel.image = image
}
Тем не менее я не уверен, что это сработает, потому что этот вид еще не нарисован ...
Вместо этого я предлагаю вам ознакомиться с этой статьей
Это эквивалент
AsyncImage
которые можно использовать, начиная с SwiftUI 1, и, что более важно, у вас есть полный доступ к исходному коду, вы можете взаимодействовать с фактическими загружаемыми файлами вместо захвата изображения представления, которое нарисовало изображение.