Improve display logic for images

Changelog-Fixed: Reduce chopping of images
Closes: #869
This commit is contained in:
mainvolume
2023-04-05 11:47:08 +02:00
committed by William Casarin
parent f236ea52e1
commit b14858b4b1

View File

@@ -45,12 +45,12 @@ struct ImageCarousel: View {
let evid: String let evid: String
let previews: PreviewCache let previews: PreviewCache
let minHeight: CGFloat = 150
let maxHeight: CGFloat = 500
@State private var open_sheet: Bool = false @State private var open_sheet: Bool = false
@State private var current_url: URL? = nil @State private var current_url: URL? = nil
@State private var image_fill: ImageFill? = nil @State private var image_fill: ImageFill? = nil
@State private var fillHeight: CGFloat = 350
@State private var maxHeight: CGFloat = UIScreen.main.bounds.height * 0.85
init(previews: PreviewCache, evid: String, urls: [URL]) { init(previews: PreviewCache, evid: String, urls: [URL]) {
_open_sheet = State(initialValue: false) _open_sheet = State(initialValue: false)
@@ -65,6 +65,10 @@ struct ImageCarousel: View {
image_fill?.filling == true image_fill?.filling == true
} }
var height: CGFloat {
image_fill?.height ?? 0
}
var body: some View { var body: some View {
TabView { TabView {
ForEach(urls, id: \.absoluteString) { url in ForEach(urls, id: \.absoluteString) { url in
@@ -73,23 +77,17 @@ struct ImageCarousel: View {
.overlay { .overlay {
GeometryReader { geo in GeometryReader { geo in
KFAnimatedImage(url) KFAnimatedImage(url)
.callbackQueue(.dispatch(.global(qos:.background)))
.backgroundDecode(true)
.imageContext(.note) .imageContext(.note)
.cancelOnDisappear(true) .cancelOnDisappear(true)
.configure { view in .configure { view in
view.framePreloadCount = 3 view.framePreloadCount = 3
} }
.imageModifier({ img in .imageFill(for: geo.size, max: maxHeight, fill: fillHeight) { fill in
let img_size = img.size previews.cache_image_meta(evid: evid, image_fill: fill)
let is_animated = img.kf.imageFrameCount != nil image_fill = fill
}
DispatchQueue.main.async {
let geo_size = geo.size
let fill = calculate_image_fill(geo_size: geo_size, img_size: img_size, is_animated: is_animated, maxHeight: maxHeight, minHeight: minHeight)
self.previews.cache_image_meta(evid: evid, image_fill: fill)
self.image_fill = fill
}
})
.aspectRatio(contentMode: filling ? .fill : .fit) .aspectRatio(contentMode: filling ? .fill : .fit)
.tabItem { .tabItem {
Text(url.absoluteString) Text(url.absoluteString)
@@ -102,7 +100,7 @@ struct ImageCarousel: View {
.fullScreenCover(isPresented: $open_sheet) { .fullScreenCover(isPresented: $open_sheet) {
ImageView(urls: urls) ImageView(urls: urls)
} }
.frame(height: image_fill?.height ?? 0) .frame(height: height)
.onTapGesture { .onTapGesture {
open_sheet = true open_sheet = true
} }
@@ -110,48 +108,65 @@ struct ImageCarousel: View {
} }
} }
func determine_image_shape(_ size: CGSize) -> ImageShape { // MARK: - Image Modifier
guard size.height > 0 else { extension KFOptionSetter {
return .unknown /// Sets a block to get image size
} ///
let imageRatio = size.width / size.height /// - Parameter block: The block which is used to read the image object.
switch imageRatio { /// - Returns: `Self` value after read size
case 1.0: return .square public func imageFill(for size: CGSize, max: CGFloat, fill: CGFloat, block: @escaping (ImageFill) throws -> Void) -> Self {
case ..<1.0: return .portrait let modifier = AnyImageModifier { image -> KFCrossPlatformImage in
case 1.0...: return .landscape let img_size = image.size
default: return .unknown let geo_size = size
let fill = ImageFill.calculate_image_fill(geo_size: geo_size,
img_size: img_size,
maxHeight: max,
fillHeight: fill)
DispatchQueue.main.async { [block, fill] in
try? block(fill)
}
return image
}
options.imageModifier = modifier
return self
} }
} }
struct ImageFill {
public struct ImageFill {
let filling: Bool? let filling: Bool?
let height: CGFloat let height: CGFloat
}
func calculate_image_fill(geo_size: CGSize, img_size: CGSize, is_animated: Bool, maxHeight: CGFloat, minHeight: CGFloat) -> ImageFill {
let shape = determine_image_shape(img_size)
let xfactor = geo_size.width / img_size.width static func determine_image_shape(_ size: CGSize) -> ImageShape {
let scaled = img_size.height * xfactor guard size.height > 0 else {
let yfactor = maxHeight / scaled return .unknown
// calculate scaled image height }
// set scale factor and constrain images to minimum 150 let imageRatio = size.width / size.height
// and animations to scaled factor for dynamic size adjustment switch imageRatio {
switch shape { case 1.0: return .square
case .portrait: case ..<1.0: return .portrait
let filling = yfactor <= 1.0 case 1.0...: return .landscape
let height = filling ? maxHeight : max(scaled, minHeight) default: return .unknown
return ImageFill(filling: filling, height: height) }
case .square: }
let height = max(min(maxHeight, scaled), minHeight)
return ImageFill(filling: nil, height: height) static func calculate_image_fill(geo_size: CGSize, img_size: CGSize, maxHeight: CGFloat, fillHeight: CGFloat) -> ImageFill {
case .landscape: let shape = determine_image_shape(img_size)
let filling = scaled > maxHeight || xfactor < 1.0
let height = is_animated ? scaled : filling ? min(maxHeight, scaled) : max(scaled, minHeight) let xfactor = geo_size.width / img_size.width
return ImageFill(filling: filling, height: height) let scaled = img_size.height * xfactor
case .unknown: // calculate scaled image height
let height = max(img_size.height, minHeight) // set scale factor and constrain images to minimum 150
return ImageFill(filling: nil, height: height) // and animations to scaled factor for dynamic size adjustment
switch shape {
case .portrait, .landscape:
let filling = scaled > maxHeight
let height = filling ? fillHeight : scaled
return ImageFill(filling: filling, height: height)
case .square, .unknown:
return ImageFill(filling: nil, height: scaled)
}
} }
} }