diff --git a/CHANGELOG.md b/CHANGELOG.md index cf8abaa99..e363a2b6f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). # Upcoming -### 🔄 Changed +### 🐞 Fixed +- Fix crash when opening message overlay in iPad with a TabBar [#627](https://github.com/GetStream/stream-chat-swiftui/pull/627) # [4.65.0](https://github.com/GetStream/stream-chat-swiftui/releases/tag/4.65.0) _October 18, 2024_ diff --git a/Sources/StreamChatSwiftUI/ChatChannel/Reactions/ReactionsOverlayView.swift b/Sources/StreamChatSwiftUI/ChatChannel/Reactions/ReactionsOverlayView.swift index 28f7f4246..1741728ee 100644 --- a/Sources/StreamChatSwiftUI/ChatChannel/Reactions/ReactionsOverlayView.swift +++ b/Sources/StreamChatSwiftUI/ChatChannel/Reactions/ReactionsOverlayView.swift @@ -71,7 +71,7 @@ public struct ReactionsOverlayView: View { currentSnapshot: currentSnapshot, popInAnimationInProgress: !popIn ) - .offset(y: spacing > 0 ? screenHeight - currentSnapshot.size.height : 0) + .offset(y: overlayOffsetY) } else { Color.gray.opacity(0.4) } @@ -290,7 +290,16 @@ public struct ReactionsOverlayView: View { return originY - spacing } - + + private var overlayOffsetY: CGFloat { + if isIPad && UITabBar.appearance().isHidden == false { + // When using iPad with TabBar, this hard coded value makes + // sure that the overlay is in the correct position. + return 20 + } + return spacing > 0 ? screenHeight - currentSnapshot.size.height : 0 + } + private var spacing: CGFloat { let divider: CGFloat = isIPad ? 2 : 1 let spacing = (UIScreen.main.bounds.height - screenHeight) / divider diff --git a/Sources/StreamChatSwiftUI/ChatChannel/Utils/ChatChannelHelpers.swift b/Sources/StreamChatSwiftUI/ChatChannel/Utils/ChatChannelHelpers.swift index ee9fcec65..e03704ced 100644 --- a/Sources/StreamChatSwiftUI/ChatChannel/Utils/ChatChannelHelpers.swift +++ b/Sources/StreamChatSwiftUI/ChatChannel/Utils/ChatChannelHelpers.swift @@ -82,6 +82,9 @@ public struct BottomLeftView: View { /// Returns the top most view controller. func topVC() -> UIViewController? { + // TODO: Refactor ReactionsOverlayView to use a background blur, instead of a snapshot. + /// Since the current approach is too error-prone and dependent of the app's hierarchy, + let keyWindow = UIApplication.shared.windows.filter { $0.isKeyWindow }.first if var topController = keyWindow?.rootViewController { @@ -92,10 +95,16 @@ func topVC() -> UIViewController? { if UIDevice.current.userInterfaceIdiom == .pad { let children = topController.children if !children.isEmpty { - let splitVC = children[0] - let sideVCs = splitVC.children - if sideVCs.count > 1 { - topController = sideVCs[1] + if let splitVC = children[0] as? UISplitViewController, + let contentVC = splitVC.viewControllers.last { + topController = contentVC + return topController + } else if let tabVC = children[0] as? UITabBarController, + let selectedVC = tabVC.selectedViewController { + // If the selectedVC is split view, we need to grab the content view of it + // other wise, the selectedVC is already the content view. + let selectedContentVC = selectedVC.children.first?.children.last?.children.first + topController = selectedContentVC ?? selectedVC return topController } } diff --git a/Sources/StreamChatSwiftUI/Utils/SnapshotCreator.swift b/Sources/StreamChatSwiftUI/Utils/SnapshotCreator.swift index af4aed693..adcfedcee 100644 --- a/Sources/StreamChatSwiftUI/Utils/SnapshotCreator.swift +++ b/Sources/StreamChatSwiftUI/Utils/SnapshotCreator.swift @@ -28,15 +28,9 @@ public class DefaultSnapshotCreator: SnapshotCreator { } func makeSnapshot(from view: UIView) -> UIImage { - let currentSnapshot: UIImage? - UIGraphicsBeginImageContext(view.frame.size) - if let currentGraphicsContext = UIGraphicsGetCurrentContext() { - view.layer.render(in: currentGraphicsContext) - currentSnapshot = UIGraphicsGetImageFromCurrentImageContext() - } else { - currentSnapshot = images.snapshot + let renderer = UIGraphicsImageRenderer(size: view.bounds.size) + return renderer.image { _ in + view.drawHierarchy(in: view.bounds, afterScreenUpdates: true) } - UIGraphicsEndImageContext() - return currentSnapshot ?? images.snapshot } }