diff --git a/apps/ios/Shared/Views/Chat/ChatItem/CIImageView.swift b/apps/ios/Shared/Views/Chat/ChatItem/CIImageView.swift index d1f49f635a..4369a5ca04 100644 --- a/apps/ios/Shared/Views/Chat/ChatItem/CIImageView.swift +++ b/apps/ios/Shared/Views/Chat/ChatItem/CIImageView.swift @@ -96,12 +96,13 @@ struct CIImageView: View { if img.imageData == nil { Image(uiImage: img) .resizable() - .scaledToFit() - .frame(width: w) + .scaledToFill() + .frame(width: w, height: w * heightRatio(img.size)) + .clipped() } else { - SwiftyGif(image: img) - .frame(width: w, height: w * img.size.height / img.size.width) - .scaledToFit() + SwiftyGif(image: img, contentMode: .scaleAspectFill) + .frame(width: w, height: w * heightRatio(img.size)) + .clipped() } if !blurred || !showDownloadButton(chatItem.file?.fileStatus) { loadingIndicator() diff --git a/apps/ios/Shared/Views/Chat/ChatItem/CILinkView.swift b/apps/ios/Shared/Views/Chat/ChatItem/CILinkView.swift index f07e90b953..980a40bc9f 100644 --- a/apps/ios/Shared/Views/Chat/ChatItem/CILinkView.swift +++ b/apps/ios/Shared/Views/Chat/ChatItem/CILinkView.swift @@ -19,7 +19,8 @@ struct CILinkView: View { if let uiImage = imageFromBase64(linkPreview.image) { Image(uiImage: uiImage) .resizable() - .scaledToFit() + .aspectRatio(1 / heightRatio(uiImage.size), contentMode: .fill) + .clipped() .modifier(PrivacyBlur(blurred: $blurred)) .if(!blurred) { v in v.simultaneousGesture(TapGesture().onEnded { diff --git a/apps/ios/Shared/Views/Chat/ChatItem/CIVideoView.swift b/apps/ios/Shared/Views/Chat/ChatItem/CIVideoView.swift index eacbe9360a..9946bed00b 100644 --- a/apps/ios/Shared/Views/Chat/ChatItem/CIVideoView.swift +++ b/apps/ios/Shared/Views/Chat/ChatItem/CIVideoView.swift @@ -185,7 +185,8 @@ struct CIVideoView: View { ZStack(alignment: .center) { let canBePlayed = !chatItem.chatDir.sent || file.fileStatus == CIFileStatus.sndComplete || (file.fileStatus == .sndStored && file.fileProtocol == .local) VideoPlayerView(player: player, url: url, showControls: false) - .frame(width: w, height: w * preview.size.height / preview.size.width) + .frame(width: w, height: w * heightRatio(preview.size)) + .clipped() .onChange(of: m.stopPreviousRecPlay) { playingUrl in if playingUrl != url { player.pause() @@ -313,8 +314,9 @@ struct CIVideoView: View { return ZStack(alignment: .topTrailing) { Image(uiImage: img) .resizable() - .scaledToFit() - .frame(width: w) + .scaledToFill() + .frame(width: w, height: w * heightRatio(img.size)) + .clipped() .modifier(PrivacyBlur(blurred: $blurred)) if !blurred || !showDownloadButton(chatItem.file?.fileStatus) { fileStatusIcon() diff --git a/apps/ios/Shared/Views/Helpers/VideoPlayerView.swift b/apps/ios/Shared/Views/Helpers/VideoPlayerView.swift index 33acf22ebe..71316cc5aa 100644 --- a/apps/ios/Shared/Views/Helpers/VideoPlayerView.swift +++ b/apps/ios/Shared/Views/Helpers/VideoPlayerView.swift @@ -29,6 +29,7 @@ struct VideoPlayerView: UIViewRepresentable { func makeUIView(context: UIViewRepresentableContext) -> UIView { let controller = AVPlayerViewController() controller.showsPlaybackControls = showControls + controller.videoGravity = .resizeAspectFill if #available(iOS 16.0, *) { controller.speeds = [] } diff --git a/apps/ios/SimpleXChat/ImageUtils.swift b/apps/ios/SimpleXChat/ImageUtils.swift index c70ca5edd8..f93b090517 100644 --- a/apps/ios/SimpleXChat/ImageUtils.swift +++ b/apps/ios/SimpleXChat/ImageUtils.swift @@ -402,6 +402,11 @@ extension UIImage { } } +// Max image height/width ratio for chat item display, taller images are cropped +public func heightRatio(_ size: CGSize) -> CGFloat { + size.width > 0 ? min(size.height / size.width, 2.33) : 1 +} + public func imageFromBase64(_ base64Encoded: String?) -> UIImage? { if let base64Encoded { if let img = imageCache.object(forKey: base64Encoded as NSString) { diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/item/FramedItemView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/item/FramedItemView.kt index 9a9626b1b8..572a326e23 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/item/FramedItemView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/item/FramedItemView.kt @@ -437,7 +437,7 @@ fun PriorityLayout( ) { measureable, constraints -> // Find important element which should tell what max width other elements can use // Expecting only one such element. Can be less than one but not more - // Constrain max image height to prevent crashes and scroll issues from images with extreme aspect ratios + // Max image height for chat item display, taller images are cropped val maxImageHeight = (constraints.maxWidth * 2.33f).toInt().coerceAtMost(constraints.maxHeight) val imageConstraints = constraints.copy(maxHeight = maxImageHeight) val imagePlaceable = measureable.firstOrNull { it.layoutId == priorityLayoutId }?.measure(imageConstraints)