#swift #swiftui #downsampling
#swift #swiftui #понижающая дискретизация
Вопрос:
Я отображаю изображения в своем приложении, загруженные из сети, но я хотел бы уменьшить их дискретизацию, чтобы они не занимали несколько МБ памяти. Раньше я мог бы сделать это довольно легко с помощью UIKit:
func resizedImage(image: UIImage, for size: CGSize) -> UIImage? {
let renderer = UIGraphicsImageRenderer(size: size)
return renderer.image { (context) in
image.draw(in: CGRect(origin: .zero, size: size))
}
}
Существуют и другие методы, но все они зависят от знания желаемого размера изображения, что в SwiftUI непросто.
Существует ли хороший API / метод специально для понижающей дискретизации изображений SwiftUI?
Комментарии:
1. И какой размер представления желателен для вас?
2. Я не уверен, я думаю, я мог бы использовать GeometryReader? Я хочу уменьшить дискретизацию до любого размера, при котором изображение фактически отображается, если вы это имеете в виду
3. Затем вы можете просто использовать ту же функцию, что и вы, и создать изображение с помощью
Image(uiImage: ...)
.4. Вы можете уменьшить выборку по масштабу, а не по размеру cgsize, используя CIFilter
Ответ №1:
В итоге я решил это с помощью geometry Reader, что не идеально, поскольку немного портит макет.
@State var image: UIImage
var body: some View {
GeometryReader { geo in
Image(uiImage: self.image)
.resizable()
.aspectRatio(contentMode: .fit)
.onAppear {
let imageFrame = CGRect(x: 0, y: 0, width: geo.size.width, height: geo.size.height)
self.downsize(frame: imageFrame) // call whatever downsizing function you want
}
}
}
Используйте прокси-сервер геометрии для определения кадра изображения, затем уменьшите дискретизацию до этого кадра. Я бы хотел, чтобы у SwiftUI был свой собственный API для этого.
Ответ №2:
Для изменения размера используйте эту функцию. Он также быстро работает в списках или LazyVStack и уменьшает потребление памяти изображениями.
public var body: some View {
GeometryReader { proxy in
let image = UIImage(named: imageName)?
.resize(height: proxy.size.height)
Image(uiImage: image ?? UIImage())
.resizable()
.scaledToFill()
}
}
public extension UIImage {
/// Resizes the image by keeping the aspect ratio
func resize(height: CGFloat) -> UIImage {
let scale = height / self.size.height
let width = self.size.width * scale
let newSize = CGSize(width: width, height: height)
let renderer = UIGraphicsImageRenderer(size: newSize)
return renderer.image { _ in
self.draw(in: CGRect(origin: .zero, size: newSize))
}
}
}
Ответ №3:
Этот метод использует CIFilter для уменьшения размера пользовательского изображения
public extension UIImage {
func downsampled(by reductionAmount: Float) -> UIImage? {
let image = UIKit.CIImage(image: self)
guard let lanczosFilter = CIFilter(name: "CILanczosScaleTransform") else { return nil }
lanczosFilter.setValue(image, forKey: kCIInputImageKey)
lanczosFilter.setValue(NSNumber.init(value: reductionAmount), forKey: kCIInputScaleKey)
guard let outputImage = lanczosFilter.outputImage else { return nil }
let context = CIContext(options: [CIContextOption.useSoftwareRenderer: false])
guard let cgImage = context.createCGImage(outputImage, from: outputImage.extent) else { return nil}
let scaledImage = UIImage(cgImage: cgImage)
return scaledImage
}
}
И затем вы можете использовать в представлении SwiftUI
struct ContentView: View {
var body: some View {
if let uiImage = UIImage(named: "sample")?.downsampled(by: 0.3) {
Image(uiImage: uiImage)
}
}
}
Комментарии:
1. Как я узнаю, что установить в качестве значения уменьшения? Обычно методы понижающей дискретизации UIKit зависят от знания фрейма ImageView для изображения, которое вы хотите отобразить, и понижающей дискретизации для соответствия этому фрейму. Моя проблема в том, что у SwiftUI нет ImageViews со свойствами фрейма.
2. @bze12 Этот метод можно использовать, когда вы знаете, на сколько уменьшить масштаб. 1.0 — это значение по умолчанию, и вы можете установить значение ниже 1.0 для уменьшения масштаба.
3. да, я знаю это, но я хочу сказать, что я не знаю, насколько уменьшить масштаб.
4. Этот фильтр не работает. Когда я использую его с коэффициентом 0,5, использование моей памяти почти удвоилось со 120 МБ до 197 МБ, и это очень медленно (хорошо, это может быть решено любым кэшем)