damus

nostr ios client
git clone git://jb55.com/damus
Log | Files | Refs | README | LICENSE

commit b14858b4b1f7ee3bab800cb710963c0027f4a362
parent f236ea52e126b2d73347cafc22c0f428f00a2dca
Author: mainvolume <sina@mainvolume.com>
Date:   Wed,  5 Apr 2023 11:47:08 +0200

Improve display logic for images

Changelog-Fixed: Reduce chopping of images
Closes: #869

Diffstat:
Mdamus/Components/ImageCarousel.swift | 117+++++++++++++++++++++++++++++++++++++++++++++-----------------------------------
1 file changed, 66 insertions(+), 51 deletions(-)

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