From f11847582ac27fe3e310b8ad2a3e1d0ae2ce8c4c Mon Sep 17 00:00:00 2001 From: Eric Andrews Date: Wed, 11 Dec 2024 21:33:58 -0500 Subject: [PATCH 01/11] bare minimum overlay --- Mlem.xcodeproj/project.pbxproj | 4 +++ Mlem/App/Views/Pages/ImageViewer.swift | 10 +++++-- Mlem/App/Views/Root/ContentView.swift | 10 +++++-- .../Shared/Images/MediaOverlayView.swift | 29 +++++++++++++++++++ .../Images/Wrappers/LargeImageView.swift | 9 ++---- .../Shared/Navigation/NavigationModel.swift | 2 ++ .../Navigation/NavigationPage+View.swift | 2 ++ .../Shared/Navigation/NavigationPage.swift | 2 ++ 8 files changed, 58 insertions(+), 10 deletions(-) create mode 100644 Mlem/App/Views/Shared/Images/MediaOverlayView.swift diff --git a/Mlem.xcodeproj/project.pbxproj b/Mlem.xcodeproj/project.pbxproj index cce2b8530..2a582ebab 100644 --- a/Mlem.xcodeproj/project.pbxproj +++ b/Mlem.xcodeproj/project.pbxproj @@ -285,6 +285,7 @@ CD09BA7F2CB4698E00C93926 /* OledPalette.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD09BA7E2CB4698600C93926 /* OledPalette.swift */; }; CD0E06F72C0E739F00445849 /* PostType+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD0E06F62C0E739F00445849 /* PostType+Extensions.swift */; }; CD0F280A2C6CEFBE00C1F65B /* View+IsAtTopSubscriber.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD0F28092C6CEFBE00C1F65B /* View+IsAtTopSubscriber.swift */; }; + CD10CC182D0A0A650006C20F /* MediaOverlayView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD10CC172D0A0A5C0006C20F /* MediaOverlayView.swift */; }; CD10FA772C7A8622008985AD /* ImageSaver.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD10FA762C7A8622008985AD /* ImageSaver.swift */; }; CD13CC572C582DD8001AF428 /* DynamicMediaView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD13CC562C582DD8001AF428 /* DynamicMediaView.swift */; }; CD13CC592C583C7A001AF428 /* WebsitePreviewView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD13CC582C583C7A001AF428 /* WebsitePreviewView.swift */; }; @@ -705,6 +706,7 @@ CD0E06F62C0E739F00445849 /* PostType+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PostType+Extensions.swift"; sourceTree = ""; }; CD0E07002C12707700445849 /* ToolbarEllipsisMenu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ToolbarEllipsisMenu.swift; sourceTree = ""; }; CD0F28092C6CEFBE00C1F65B /* View+IsAtTopSubscriber.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "View+IsAtTopSubscriber.swift"; sourceTree = ""; }; + CD10CC172D0A0A5C0006C20F /* MediaOverlayView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaOverlayView.swift; sourceTree = ""; }; CD10FA762C7A8622008985AD /* ImageSaver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageSaver.swift; sourceTree = ""; }; CD13CC562C582DD8001AF428 /* DynamicMediaView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DynamicMediaView.swift; sourceTree = ""; }; CD13CC582C583C7A001AF428 /* WebsitePreviewView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WebsitePreviewView.swift; sourceTree = ""; }; @@ -1150,6 +1152,7 @@ 039D75652C4FC56A004F24C2 /* Images */ = { isa = PBXGroup; children = ( + CD10CC172D0A0A5C0006C20F /* MediaOverlayView.swift */, CD13CC682C5D3CCA001AF428 /* Wrappers */, CD13CC672C5D3CBE001AF428 /* Core */, CD13CC662C5D3CA7001AF428 /* Helpers */, @@ -2335,6 +2338,7 @@ CD7DB9762C4D6C0A00DCC542 /* FeedCommentView.swift in Sources */, 03D3A1EF2BB9CA1D009DE55E /* MenuButton.swift in Sources */, CD0507732C6AA0C8008B1505 /* FeedSelection.swift in Sources */, + CD10CC182D0A0A650006C20F /* MediaOverlayView.swift in Sources */, 039F58862C7A810100C61658 /* ExpandedPostView+Logic.swift in Sources */, 031E2D512BEF961D0003BC45 /* SubscriptionListView.swift in Sources */, CDD4A09E2C8B69FC0001AD1A /* Button.swift in Sources */, diff --git a/Mlem/App/Views/Pages/ImageViewer.swift b/Mlem/App/Views/Pages/ImageViewer.swift index f8d0c60ce..feaabdb09 100644 --- a/Mlem/App/Views/Pages/ImageViewer.swift +++ b/Mlem/App/Views/Pages/ImageViewer.swift @@ -9,6 +9,7 @@ import SwiftUI struct ImageViewer: View { @Environment(Palette.self) var palette + @Environment(MediaState.self) var mediaState let url: URL @@ -25,9 +26,14 @@ struct ImageViewer: View { } .toolbar { ToolbarItem(placement: .topBarTrailing) { - CloseButtonView() + CloseButtonView { + withAnimation { + mediaState.url = nil + } + } } } - .background(palette.background) + .background(Color.black) + // .background(palette.background) } } diff --git a/Mlem/App/Views/Root/ContentView.swift b/Mlem/App/Views/Root/ContentView.swift index 5a1cc108f..66bd7f6c3 100644 --- a/Mlem/App/Views/Root/ContentView.swift +++ b/Mlem/App/Views/Root/ContentView.swift @@ -32,6 +32,7 @@ struct ContentView: View { var palette: Palette { .main } var tabReselectTracker: TabReselectTracker { .main } var navigationModel: NavigationModel { .main } + var mediaState: MediaState = .init() @State var avatarImage: UIImage? @State var selectedAvatarImage: UIImage? @@ -59,9 +60,7 @@ struct ContentView: View { try await (appState.firstSession as? UserSession)?.unreadCount?.refresh() } } - .navigationSheetModifiers(nextLayer: navigationModel.layers.first, model: navigationModel) .tint(palette.accent) - .environment(palette) .environment(tabReselectTracker) .environment(appState) .task { @@ -102,6 +101,8 @@ struct ContentView: View { } } } + .environment(mediaState) + .environment(palette) .environment(AppState.main) } } @@ -170,5 +171,10 @@ struct ContentView: View { location: .top ) } + .overlay { + if let url = mediaState.url { + NavigationLayerView(layer: .init(root: .mediaOverlay(url), model: navigationModel), hasSheetModifiers: true) + } + } } } diff --git a/Mlem/App/Views/Shared/Images/MediaOverlayView.swift b/Mlem/App/Views/Shared/Images/MediaOverlayView.swift new file mode 100644 index 000000000..cf51b6458 --- /dev/null +++ b/Mlem/App/Views/Shared/Images/MediaOverlayView.swift @@ -0,0 +1,29 @@ +// +// MediaOverlayView.swift +// Mlem +// +// Created by Eric Andrews on 2024-12-11. +// + +import SwiftUI + +@Observable +class MediaState { + var url: URL? + + func setUrl(_ url: URL) { + withAnimation { + self.url = url + } + } +} + +struct MediaOverlayView: View { + @Environment(MediaState.self) var mediaState + + let url: URL + + var body: some View { + ImageViewer(url: url) + } +} diff --git a/Mlem/App/Views/Shared/Images/Wrappers/LargeImageView.swift b/Mlem/App/Views/Shared/Images/Wrappers/LargeImageView.swift index b267b9f51..e65d6238f 100644 --- a/Mlem/App/Views/Shared/Images/Wrappers/LargeImageView.swift +++ b/Mlem/App/Views/Shared/Images/Wrappers/LargeImageView.swift @@ -12,6 +12,8 @@ import SwiftUI // and anything else that can't go in thumbnail views etc. struct LargeImageView: View { + @Environment(MediaState.self) var mediaState + @Environment(NavigationLayer.self) private var navigation @Setting(\.blurNsfw) var blurNsfw @@ -55,12 +57,7 @@ struct LargeImageView: View { onTapActions() } if let loading, loading == .done, let url { - // Sheets don't cover the whole screen on iPad, so use a fullScreenCover instead - if UIDevice.isPad { - navigation.showFullScreenCover(.imageViewer(url)) - } else { - navigation.openSheet(.imageViewer(url)) - } + mediaState.setUrl(url) } } .onPreferenceChange(MediaLoadingPreferenceKey.self, perform: { loading = $0 }) diff --git a/Mlem/App/Views/Shared/Navigation/NavigationModel.swift b/Mlem/App/Views/Shared/Navigation/NavigationModel.swift index 1b11f28fc..7ed9b3e4e 100644 --- a/Mlem/App/Views/Shared/Navigation/NavigationModel.swift +++ b/Mlem/App/Views/Shared/Navigation/NavigationModel.swift @@ -23,6 +23,8 @@ class NavigationModel { var showingFilePicker: Bool = false var filePickerCallback: ((URL) -> Void)? + var mediaUrl: URL? + private func openSheet(_ page: NavigationPage, hasNavigationStack: Bool? = nil, isFullScreenCover: Bool) { layers.append( .init( diff --git a/Mlem/App/Views/Shared/Navigation/NavigationPage+View.swift b/Mlem/App/Views/Shared/Navigation/NavigationPage+View.swift index ff43fc2af..cef5b568f 100644 --- a/Mlem/App/Views/Shared/Navigation/NavigationPage+View.swift +++ b/Mlem/App/Views/Shared/Navigation/NavigationPage+View.swift @@ -12,6 +12,8 @@ extension NavigationPage { // swiftlint:disable:next cyclomatic_complexity function_body_length @ViewBuilder func view() -> some View { switch self { + case let .mediaOverlay(url): + MediaOverlayView(url: url) case .subscriptionList: SubscriptionListView() case let .selectText(string): diff --git a/Mlem/App/Views/Shared/Navigation/NavigationPage.swift b/Mlem/App/Views/Shared/Navigation/NavigationPage.swift index 9216f3a61..7a2f34bfe 100644 --- a/Mlem/App/Views/Shared/Navigation/NavigationPage.swift +++ b/Mlem/App/Views/Shared/Navigation/NavigationPage.swift @@ -9,6 +9,8 @@ import MlemMiddleware import SwiftUI enum NavigationPage: Hashable { + case mediaOverlay(_ url: URL) + case settings(_ page: SettingsPage = .root) case logIn(_ page: LoginPage = .pickInstance) case signUp(_ instance: HashWrapper) From 821f0d27eb07fbf9e264002a8117aef34823b143 Mon Sep 17 00:00:00 2001 From: Eric Andrews Date: Wed, 11 Dec 2024 21:36:21 -0500 Subject: [PATCH 02/11] change hasSheetModifiers --- Mlem/App/Views/Root/ContentView.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mlem/App/Views/Root/ContentView.swift b/Mlem/App/Views/Root/ContentView.swift index 66bd7f6c3..347971846 100644 --- a/Mlem/App/Views/Root/ContentView.swift +++ b/Mlem/App/Views/Root/ContentView.swift @@ -173,7 +173,7 @@ struct ContentView: View { } .overlay { if let url = mediaState.url { - NavigationLayerView(layer: .init(root: .mediaOverlay(url), model: navigationModel), hasSheetModifiers: true) + NavigationLayerView(layer: .init(root: .mediaOverlay(url), model: navigationModel), hasSheetModifiers: false) } } } From 461667950eed3e15d3966ce51d76699c670a0521 Mon Sep 17 00:00:00 2001 From: Eric Andrews Date: Thu, 12 Dec 2024 21:47:09 -0500 Subject: [PATCH 03/11] some garbage --- Mlem/App/Views/Pages/ImageViewer.swift | 29 +++++++++++++++---- Mlem/App/Views/Root/ContentView.swift | 1 + .../Shared/Images/MediaOverlayView.swift | 4 +-- .../Navigation/NavigationPage+View.swift | 2 +- Mlem/App/Views/Shared/ZoomableContainer.swift | 12 +++++++- Mlem/Localizable.xcstrings | 6 ++-- 6 files changed, 41 insertions(+), 13 deletions(-) diff --git a/Mlem/App/Views/Pages/ImageViewer.swift b/Mlem/App/Views/Pages/ImageViewer.swift index feaabdb09..89aa63c17 100644 --- a/Mlem/App/Views/Pages/ImageViewer.swift +++ b/Mlem/App/Views/Pages/ImageViewer.swift @@ -13,6 +13,9 @@ struct ImageViewer: View { let url: URL + @State var isZoomed: Bool = false + @State var dragDistance: CGFloat = 0 + init(url: URL) { var components = URLComponents(url: url, resolvingAgainstBaseURL: false)! components.queryItems = components.queryItems?.filter { $0.name != "thumbnail" } @@ -20,20 +23,34 @@ struct ImageViewer: View { } var body: some View { - ZoomableContainer { + ZoomableContainer(isZoomed: $isZoomed) { DynamicMediaView(url: url, playImmediately: true) .padding(Constants.main.standardSpacing) + .offset(y: dragDistance) } + // .frame(maxWidth: .infinity, maxHeight: .infinity) .toolbar { ToolbarItem(placement: .topBarTrailing) { CloseButtonView { - withAnimation { - mediaState.url = nil - } + mediaState.setUrl(nil) } } } - .background(Color.black) - // .background(palette.background) + .background(Color.black.opacity(1.0 - (abs(dragDistance) / UIScreen.main.bounds.height))) +// .gesture(DragGesture(minimumDistance: 0.0) +// .onChanged { value in +// if !isZoomed { +// dragDistance = value.translation.height +// } +// } +// .onEnded { value in +// if abs(value.translation.height) > 200 { +// dragDistance = (value.translation.height > 0 ? 1000 : -1000) +// mediaState.setUrl(nil) +// } else { +// dragDistance = 0 +// } +// } +// ) } } diff --git a/Mlem/App/Views/Root/ContentView.swift b/Mlem/App/Views/Root/ContentView.swift index 347971846..4bec0cf47 100644 --- a/Mlem/App/Views/Root/ContentView.swift +++ b/Mlem/App/Views/Root/ContentView.swift @@ -174,6 +174,7 @@ struct ContentView: View { .overlay { if let url = mediaState.url { NavigationLayerView(layer: .init(root: .mediaOverlay(url), model: navigationModel), hasSheetModifiers: false) + .background(.clear) } } } diff --git a/Mlem/App/Views/Shared/Images/MediaOverlayView.swift b/Mlem/App/Views/Shared/Images/MediaOverlayView.swift index cf51b6458..63f7f29fa 100644 --- a/Mlem/App/Views/Shared/Images/MediaOverlayView.swift +++ b/Mlem/App/Views/Shared/Images/MediaOverlayView.swift @@ -9,9 +9,9 @@ import SwiftUI @Observable class MediaState { - var url: URL? + private(set) var url: URL? - func setUrl(_ url: URL) { + func setUrl(_ url: URL?) { withAnimation { self.url = url } diff --git a/Mlem/App/Views/Shared/Navigation/NavigationPage+View.swift b/Mlem/App/Views/Shared/Navigation/NavigationPage+View.swift index cef5b568f..b83d0e45c 100644 --- a/Mlem/App/Views/Shared/Navigation/NavigationPage+View.swift +++ b/Mlem/App/Views/Shared/Navigation/NavigationPage+View.swift @@ -13,7 +13,7 @@ extension NavigationPage { @ViewBuilder func view() -> some View { switch self { case let .mediaOverlay(url): - MediaOverlayView(url: url) + ImageViewer(url: url) case .subscriptionList: SubscriptionListView() case let .selectText(string): diff --git a/Mlem/App/Views/Shared/ZoomableContainer.swift b/Mlem/App/Views/Shared/ZoomableContainer.swift index 7fbc75250..4a13302d9 100644 --- a/Mlem/App/Views/Shared/ZoomableContainer.swift +++ b/Mlem/App/Views/Shared/ZoomableContainer.swift @@ -17,9 +17,11 @@ struct ZoomableContainer: View { let content: Content @State private var currentScale: CGFloat = 1.0 @State private var tapLocation: CGPoint = .zero + @Binding var isZoomed: Bool - init(@ViewBuilder content: () -> Content) { + init(isZoomed: Binding = .constant(false), @ViewBuilder content: () -> Content) { self.content = content() + self._isZoomed = isZoomed } func doubleTapAction(location: CGPoint) { @@ -31,7 +33,11 @@ struct ZoomableContainer: View { ZoomableScrollView(scale: $currentScale, tapLocation: $tapLocation) { content } + .border(.red) .onTapGesture(count: 2, perform: doubleTapAction) + .onChange(of: currentScale) { + isZoomed = currentScale != 1.0 + } } fileprivate struct ZoomableScrollView: UIViewRepresentable { @@ -102,6 +108,10 @@ struct ZoomableContainer: View { self.hostingController = hostingController _currentScale = scale } + + func scrollViewDidScroll(_ scrollView: UIScrollView) { + print("DEBUG \(scrollView.contentOffset)") + } func viewForZooming(in _: UIScrollView) -> UIView? { hostingController.view diff --git a/Mlem/Localizable.xcstrings b/Mlem/Localizable.xcstrings index 4586fab6a..a3875f920 100644 --- a/Mlem/Localizable.xcstrings +++ b/Mlem/Localizable.xcstrings @@ -1493,10 +1493,10 @@ } } }, - "The name that is displayed on your profile. This is not the same as your username, which cannot be changed." : { + "The name shown in the account switcher." : { }, - "The name shown in the account switcher." : { + "The name that is displayed on your profile. This is not the same as your username, which cannot be changed." : { }, "The number of child comments that are shown in a chain before the \"More Replies\" button is shown." : { @@ -1874,4 +1874,4 @@ } }, "version" : "1.0" -} +} \ No newline at end of file From d1ba5bbe70ef984b50d7e9cbdd8b78be9034b03c Mon Sep 17 00:00:00 2001 From: Eric Andrews Date: Fri, 13 Dec 2024 13:00:50 -0500 Subject: [PATCH 04/11] working implementation --- Mlem.xcodeproj/project.pbxproj | 10 +-- Mlem/App/Globals/Definitions/MediaState.swift | 13 +++ Mlem/App/Views/Pages/ImageViewer.swift | 81 +++++++++++++------ Mlem/App/Views/Root/ContentView.swift | 7 +- .../Helpers/AnimationControlLayer.swift | 2 +- .../Shared/Images/MediaOverlayView.swift | 29 ------- .../Images/Wrappers/LargeImageView.swift | 2 +- .../Images/Wrappers/ThumbnailImageView.swift | 8 +- .../Navigation/NavigationLayerView.swift | 1 + .../Navigation/NavigationPage+View.swift | 2 - .../Shared/Navigation/NavigationPage.swift | 2 - Mlem/App/Views/Shared/ZoomableContainer.swift | 7 +- 12 files changed, 88 insertions(+), 76 deletions(-) create mode 100644 Mlem/App/Globals/Definitions/MediaState.swift delete mode 100644 Mlem/App/Views/Shared/Images/MediaOverlayView.swift diff --git a/Mlem.xcodeproj/project.pbxproj b/Mlem.xcodeproj/project.pbxproj index 2a582ebab..944ca404b 100644 --- a/Mlem.xcodeproj/project.pbxproj +++ b/Mlem.xcodeproj/project.pbxproj @@ -285,7 +285,7 @@ CD09BA7F2CB4698E00C93926 /* OledPalette.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD09BA7E2CB4698600C93926 /* OledPalette.swift */; }; CD0E06F72C0E739F00445849 /* PostType+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD0E06F62C0E739F00445849 /* PostType+Extensions.swift */; }; CD0F280A2C6CEFBE00C1F65B /* View+IsAtTopSubscriber.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD0F28092C6CEFBE00C1F65B /* View+IsAtTopSubscriber.swift */; }; - CD10CC182D0A0A650006C20F /* MediaOverlayView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD10CC172D0A0A5C0006C20F /* MediaOverlayView.swift */; }; + CD10CC182D0A0A650006C20F /* MediaState.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD10CC172D0A0A5C0006C20F /* MediaState.swift */; }; CD10FA772C7A8622008985AD /* ImageSaver.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD10FA762C7A8622008985AD /* ImageSaver.swift */; }; CD13CC572C582DD8001AF428 /* DynamicMediaView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD13CC562C582DD8001AF428 /* DynamicMediaView.swift */; }; CD13CC592C583C7A001AF428 /* WebsitePreviewView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD13CC582C583C7A001AF428 /* WebsitePreviewView.swift */; }; @@ -706,7 +706,7 @@ CD0E06F62C0E739F00445849 /* PostType+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PostType+Extensions.swift"; sourceTree = ""; }; CD0E07002C12707700445849 /* ToolbarEllipsisMenu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ToolbarEllipsisMenu.swift; sourceTree = ""; }; CD0F28092C6CEFBE00C1F65B /* View+IsAtTopSubscriber.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "View+IsAtTopSubscriber.swift"; sourceTree = ""; }; - CD10CC172D0A0A5C0006C20F /* MediaOverlayView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaOverlayView.swift; sourceTree = ""; }; + CD10CC172D0A0A5C0006C20F /* MediaState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaState.swift; sourceTree = ""; }; CD10FA762C7A8622008985AD /* ImageSaver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageSaver.swift; sourceTree = ""; }; CD13CC562C582DD8001AF428 /* DynamicMediaView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DynamicMediaView.swift; sourceTree = ""; }; CD13CC582C583C7A001AF428 /* WebsitePreviewView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WebsitePreviewView.swift; sourceTree = ""; }; @@ -1152,7 +1152,6 @@ 039D75652C4FC56A004F24C2 /* Images */ = { isa = PBXGroup; children = ( - CD10CC172D0A0A5C0006C20F /* MediaOverlayView.swift */, CD13CC682C5D3CCA001AF428 /* Wrappers */, CD13CC672C5D3CBE001AF428 /* Core */, CD13CC662C5D3CA7001AF428 /* Helpers */, @@ -1667,13 +1666,14 @@ CD4D59212B87BD0800B82964 /* Definitions */ = { isa = PBXGroup; children = ( - CDCA44AE2C17672900C092B3 /* HapticManager */, CD4D58B22B86BFD400B82964 /* AccountsTracker.swift */, 036CC3AE2B8145C30098B6A1 /* AppState.swift */, 0380965E2C10AA80003ED1D8 /* AppState+Transition.swift */, + CD10CC172D0A0A5C0006C20F /* MediaState.swift */, CD317D4B2BE97FED008F63E2 /* Palette.swift */, CD4D58A42B86BD1B00B82964 /* PersistenceRepository.swift */, CD4ED8462BF110FA00EFA0A2 /* TabReselectTracker.swift */, + CDCA44AE2C17672900C092B3 /* HapticManager */, ); path = Definitions; sourceTree = ""; @@ -2338,7 +2338,7 @@ CD7DB9762C4D6C0A00DCC542 /* FeedCommentView.swift in Sources */, 03D3A1EF2BB9CA1D009DE55E /* MenuButton.swift in Sources */, CD0507732C6AA0C8008B1505 /* FeedSelection.swift in Sources */, - CD10CC182D0A0A650006C20F /* MediaOverlayView.swift in Sources */, + CD10CC182D0A0A650006C20F /* MediaState.swift in Sources */, 039F58862C7A810100C61658 /* ExpandedPostView+Logic.swift in Sources */, 031E2D512BEF961D0003BC45 /* SubscriptionListView.swift in Sources */, CDD4A09E2C8B69FC0001AD1A /* Button.swift in Sources */, diff --git a/Mlem/App/Globals/Definitions/MediaState.swift b/Mlem/App/Globals/Definitions/MediaState.swift new file mode 100644 index 000000000..95c4eb4f9 --- /dev/null +++ b/Mlem/App/Globals/Definitions/MediaState.swift @@ -0,0 +1,13 @@ +// +// MediaState.swift +// Mlem +// +// Created by Eric Andrews on 2024-12-11. +// + +import SwiftUI + +@Observable +class MediaState { + var url: URL? +} diff --git a/Mlem/App/Views/Pages/ImageViewer.swift b/Mlem/App/Views/Pages/ImageViewer.swift index 89aa63c17..962ccd447 100644 --- a/Mlem/App/Views/Pages/ImageViewer.swift +++ b/Mlem/App/Views/Pages/ImageViewer.swift @@ -14,7 +14,10 @@ struct ImageViewer: View { let url: URL @State var isZoomed: Bool = false - @State var dragDistance: CGFloat = 0 + @State var offset: CGFloat = UIScreen.main.bounds.height + @State var isDismissing: Bool = false + + var screenHeight: CGFloat { UIScreen.main.bounds.height } init(url: URL) { var components = URLComponents(url: url, resolvingAgainstBaseURL: false)! @@ -24,33 +27,63 @@ struct ImageViewer: View { var body: some View { ZoomableContainer(isZoomed: $isZoomed) { - DynamicMediaView(url: url, playImmediately: true) + DynamicMediaView(url: url, cornerRadius: 0, playImmediately: true) + } + .offset(y: offset) + .onAppear { + updateDragDistance(0) + } + .background(Color.black.opacity(1.0 - (abs(offset) / screenHeight))) + .overlay(alignment: .topTrailing) { + if offset == 0 { + Button { + dismiss() + } label: { + Image(systemName: Icons.close) + .resizable() + .frame(width: 15, height: 15) + .foregroundStyle(.white) + .padding([.top, .trailing], Constants.main.standardSpacing) + .padding([.bottom, .leading], Constants.main.doubleSpacing) + .contentShape(.rect) + } .padding(Constants.main.standardSpacing) - .offset(y: dragDistance) + } } - // .frame(maxWidth: .infinity, maxHeight: .infinity) - .toolbar { - ToolbarItem(placement: .topBarTrailing) { - CloseButtonView { - mediaState.setUrl(nil) + .simultaneousGesture(DragGesture(minimumDistance: 0.0) + .onChanged { value in + if !isZoomed, !isDismissing { + offset = value.translation.height + } + } + .onEnded { value in + guard !isDismissing, !isZoomed else { return } + + if abs(value.translation.height) > 100 { + dismiss(finalOffset: value.translation.height > 0 ? screenHeight : -screenHeight) + } else { + updateDragDistance(0) } } + ) + } + + private func dismiss(finalOffset: CGFloat = UIScreen.main.bounds.height) { + isDismissing = true + updateDragDistance(finalOffset) { + mediaState.url = nil + } + } + + private func updateDragDistance(_ newDistance: CGFloat, callback: (() -> Void)? = nil) { + let duration: CGFloat = 0.35 + withAnimation(.easeOut(duration: duration)) { + offset = newDistance + } + if let callback { + DispatchQueue.main.asyncAfter(deadline: .now() + duration) { + callback() + } } - .background(Color.black.opacity(1.0 - (abs(dragDistance) / UIScreen.main.bounds.height))) -// .gesture(DragGesture(minimumDistance: 0.0) -// .onChanged { value in -// if !isZoomed { -// dragDistance = value.translation.height -// } -// } -// .onEnded { value in -// if abs(value.translation.height) > 200 { -// dragDistance = (value.translation.height > 0 ? 1000 : -1000) -// mediaState.setUrl(nil) -// } else { -// dragDistance = 0 -// } -// } -// ) } } diff --git a/Mlem/App/Views/Root/ContentView.swift b/Mlem/App/Views/Root/ContentView.swift index 4bec0cf47..277751442 100644 --- a/Mlem/App/Views/Root/ContentView.swift +++ b/Mlem/App/Views/Root/ContentView.swift @@ -173,8 +173,11 @@ struct ContentView: View { } .overlay { if let url = mediaState.url { - NavigationLayerView(layer: .init(root: .mediaOverlay(url), model: navigationModel), hasSheetModifiers: false) - .background(.clear) + NavigationLayerView( + layer: .init(root: .imageViewer(url), + model: navigationModel, + hasNavigationStack: false), + hasSheetModifiers: false) } } } diff --git a/Mlem/App/Views/Shared/Images/Helpers/AnimationControlLayer.swift b/Mlem/App/Views/Shared/Images/Helpers/AnimationControlLayer.swift index d948d1776..62c51ccf0 100644 --- a/Mlem/App/Views/Shared/Images/Helpers/AnimationControlLayer.swift +++ b/Mlem/App/Views/Shared/Images/Helpers/AnimationControlLayer.swift @@ -26,7 +26,7 @@ private struct AnimationControlLayer: ViewModifier { } } } - .overlay(alignment: .topTrailing) { + .overlay(alignment: .bottomTrailing) { if let muted { Image(systemName: muted.wrappedValue ? Icons.muted : Icons.unmuted) .resizable() diff --git a/Mlem/App/Views/Shared/Images/MediaOverlayView.swift b/Mlem/App/Views/Shared/Images/MediaOverlayView.swift deleted file mode 100644 index 63f7f29fa..000000000 --- a/Mlem/App/Views/Shared/Images/MediaOverlayView.swift +++ /dev/null @@ -1,29 +0,0 @@ -// -// MediaOverlayView.swift -// Mlem -// -// Created by Eric Andrews on 2024-12-11. -// - -import SwiftUI - -@Observable -class MediaState { - private(set) var url: URL? - - func setUrl(_ url: URL?) { - withAnimation { - self.url = url - } - } -} - -struct MediaOverlayView: View { - @Environment(MediaState.self) var mediaState - - let url: URL - - var body: some View { - ImageViewer(url: url) - } -} diff --git a/Mlem/App/Views/Shared/Images/Wrappers/LargeImageView.swift b/Mlem/App/Views/Shared/Images/Wrappers/LargeImageView.swift index e65d6238f..f2bd14b0b 100644 --- a/Mlem/App/Views/Shared/Images/Wrappers/LargeImageView.swift +++ b/Mlem/App/Views/Shared/Images/Wrappers/LargeImageView.swift @@ -57,7 +57,7 @@ struct LargeImageView: View { onTapActions() } if let loading, loading == .done, let url { - mediaState.setUrl(url) + mediaState.url = url } } .onPreferenceChange(MediaLoadingPreferenceKey.self, perform: { loading = $0 }) diff --git a/Mlem/App/Views/Shared/Images/Wrappers/ThumbnailImageView.swift b/Mlem/App/Views/Shared/Images/Wrappers/ThumbnailImageView.swift index e081533e7..93bbacc75 100644 --- a/Mlem/App/Views/Shared/Images/Wrappers/ThumbnailImageView.swift +++ b/Mlem/App/Views/Shared/Images/Wrappers/ThumbnailImageView.swift @@ -12,6 +12,7 @@ import SwiftUI struct ThumbnailImageView: View { @Environment(Palette.self) var palette @Environment(NavigationLayer.self) var navigation + @Environment(MediaState.self) var mediaState @Environment(\.openURL) var openURL @State var loading: MediaLoadingState? @@ -55,12 +56,7 @@ struct ThumbnailImageView: View { if let loading, loading == .done || loading == .proxyFailed { post.markRead() - // Sheets don't cover the whole screen on iPad, so use a fullScreenCover instead - if UIDevice.isPad { - navigation.showFullScreenCover(.imageViewer(url)) - } else { - navigation.openSheet(.imageViewer(url)) - } + mediaState.url = url } } .contextMenu { diff --git a/Mlem/App/Views/Shared/Navigation/NavigationLayerView.swift b/Mlem/App/Views/Shared/Navigation/NavigationLayerView.swift index b2da5c3c6..3426846a6 100644 --- a/Mlem/App/Views/Shared/Navigation/NavigationLayerView.swift +++ b/Mlem/App/Views/Shared/Navigation/NavigationLayerView.swift @@ -83,6 +83,7 @@ struct NavigationLayerView: View { }) .modifier(HandleLemmyLinksModifier()) .environment(layer) + .presentationBackground(.clear) } @ViewBuilder diff --git a/Mlem/App/Views/Shared/Navigation/NavigationPage+View.swift b/Mlem/App/Views/Shared/Navigation/NavigationPage+View.swift index b83d0e45c..ff43fc2af 100644 --- a/Mlem/App/Views/Shared/Navigation/NavigationPage+View.swift +++ b/Mlem/App/Views/Shared/Navigation/NavigationPage+View.swift @@ -12,8 +12,6 @@ extension NavigationPage { // swiftlint:disable:next cyclomatic_complexity function_body_length @ViewBuilder func view() -> some View { switch self { - case let .mediaOverlay(url): - ImageViewer(url: url) case .subscriptionList: SubscriptionListView() case let .selectText(string): diff --git a/Mlem/App/Views/Shared/Navigation/NavigationPage.swift b/Mlem/App/Views/Shared/Navigation/NavigationPage.swift index 7a2f34bfe..9216f3a61 100644 --- a/Mlem/App/Views/Shared/Navigation/NavigationPage.swift +++ b/Mlem/App/Views/Shared/Navigation/NavigationPage.swift @@ -9,8 +9,6 @@ import MlemMiddleware import SwiftUI enum NavigationPage: Hashable { - case mediaOverlay(_ url: URL) - case settings(_ page: SettingsPage = .root) case logIn(_ page: LoginPage = .pickInstance) case signUp(_ instance: HashWrapper) diff --git a/Mlem/App/Views/Shared/ZoomableContainer.swift b/Mlem/App/Views/Shared/ZoomableContainer.swift index 4a13302d9..c1f368238 100644 --- a/Mlem/App/Views/Shared/ZoomableContainer.swift +++ b/Mlem/App/Views/Shared/ZoomableContainer.swift @@ -33,7 +33,6 @@ struct ZoomableContainer: View { ZoomableScrollView(scale: $currentScale, tapLocation: $tapLocation) { content } - .border(.red) .onTapGesture(count: 2, perform: doubleTapAction) .onChange(of: currentScale) { isZoomed = currentScale != 1.0 @@ -109,9 +108,9 @@ struct ZoomableContainer: View { _currentScale = scale } - func scrollViewDidScroll(_ scrollView: UIScrollView) { - print("DEBUG \(scrollView.contentOffset)") - } +// func scrollViewDidScroll(_ scrollView: UIScrollView) { +// print("DEBUG \(scrollView.contentOffset)") +// } func viewForZooming(in _: UIScrollView) -> UIView? { hostingController.view From 93eb00516a2aead628475e9593e2af0d6c78909a Mon Sep 17 00:00:00 2001 From: Eric Andrews Date: Fri, 13 Dec 2024 13:05:20 -0500 Subject: [PATCH 05/11] cleanup --- Mlem/App/Views/Root/ContentView.swift | 3 ++- Mlem/App/Views/Shared/Navigation/NavigationLayerView.swift | 1 - Mlem/App/Views/Shared/ZoomableContainer.swift | 4 ---- 3 files changed, 2 insertions(+), 6 deletions(-) diff --git a/Mlem/App/Views/Root/ContentView.swift b/Mlem/App/Views/Root/ContentView.swift index 277751442..6b501b8b2 100644 --- a/Mlem/App/Views/Root/ContentView.swift +++ b/Mlem/App/Views/Root/ContentView.swift @@ -60,7 +60,9 @@ struct ContentView: View { try await (appState.firstSession as? UserSession)?.unreadCount?.refresh() } } + .navigationSheetModifiers(nextLayer: navigationModel.layers.first, model: navigationModel) .tint(palette.accent) + .environment(palette) .environment(tabReselectTracker) .environment(appState) .task { @@ -102,7 +104,6 @@ struct ContentView: View { } } .environment(mediaState) - .environment(palette) .environment(AppState.main) } } diff --git a/Mlem/App/Views/Shared/Navigation/NavigationLayerView.swift b/Mlem/App/Views/Shared/Navigation/NavigationLayerView.swift index 3426846a6..b2da5c3c6 100644 --- a/Mlem/App/Views/Shared/Navigation/NavigationLayerView.swift +++ b/Mlem/App/Views/Shared/Navigation/NavigationLayerView.swift @@ -83,7 +83,6 @@ struct NavigationLayerView: View { }) .modifier(HandleLemmyLinksModifier()) .environment(layer) - .presentationBackground(.clear) } @ViewBuilder diff --git a/Mlem/App/Views/Shared/ZoomableContainer.swift b/Mlem/App/Views/Shared/ZoomableContainer.swift index c1f368238..7f098fbad 100644 --- a/Mlem/App/Views/Shared/ZoomableContainer.swift +++ b/Mlem/App/Views/Shared/ZoomableContainer.swift @@ -107,10 +107,6 @@ struct ZoomableContainer: View { self.hostingController = hostingController _currentScale = scale } - -// func scrollViewDidScroll(_ scrollView: UIScrollView) { -// print("DEBUG \(scrollView.contentOffset)") -// } func viewForZooming(in _: UIScrollView) -> UIView? { hostingController.view From 173877b1ecc4b178fc473b1f18248ff72cc478bf Mon Sep 17 00:00:00 2001 From: Eric Andrews Date: Fri, 13 Dec 2024 15:24:18 -0500 Subject: [PATCH 06/11] tune animation time --- Mlem/App/Views/Pages/ImageViewer.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mlem/App/Views/Pages/ImageViewer.swift b/Mlem/App/Views/Pages/ImageViewer.swift index 962ccd447..fe8db035a 100644 --- a/Mlem/App/Views/Pages/ImageViewer.swift +++ b/Mlem/App/Views/Pages/ImageViewer.swift @@ -76,7 +76,7 @@ struct ImageViewer: View { } private func updateDragDistance(_ newDistance: CGFloat, callback: (() -> Void)? = nil) { - let duration: CGFloat = 0.35 + let duration: CGFloat = 0.25 withAnimation(.easeOut(duration: duration)) { offset = newDistance } From 195ade6f1270105b0fbdce880fae6fc4ad1f470d Mon Sep 17 00:00:00 2001 From: Eric Andrews Date: Sat, 14 Dec 2024 12:53:46 -0500 Subject: [PATCH 07/11] fix edge case --- Mlem/App/Views/Pages/ImageViewer.swift | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/Mlem/App/Views/Pages/ImageViewer.swift b/Mlem/App/Views/Pages/ImageViewer.swift index fe8db035a..6e2cf2697 100644 --- a/Mlem/App/Views/Pages/ImageViewer.swift +++ b/Mlem/App/Views/Pages/ImageViewer.swift @@ -13,6 +13,8 @@ struct ImageViewer: View { let url: URL + @GestureState var dragState: Bool = false + @State var isZoomed: Bool = false @State var offset: CGFloat = UIScreen.main.bounds.height @State var isDismissing: Bool = false @@ -56,16 +58,19 @@ struct ImageViewer: View { offset = value.translation.height } } - .onEnded { value in - guard !isDismissing, !isZoomed else { return } - - if abs(value.translation.height) > 100 { - dismiss(finalOffset: value.translation.height > 0 ? screenHeight : -screenHeight) + .updating($dragState) { _, state, _ in + state = true + } + ) + .onChange(of: dragState) { + if !dragState { + if abs(offset) > 100 { + dismiss(finalOffset: offset > 0 ? screenHeight : -screenHeight) } else { updateDragDistance(0) } } - ) + } } private func dismiss(finalOffset: CGFloat = UIScreen.main.bounds.height) { From 59831b1fc93f0a02b8bb6e6de5e33543d1a9c574 Mon Sep 17 00:00:00 2001 From: Eric Andrews Date: Sun, 15 Dec 2024 20:00:25 -0500 Subject: [PATCH 08/11] opacity transition --- Mlem/App/Views/Pages/ImageViewer.swift | 30 ++++++++++++++++++++------ 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/Mlem/App/Views/Pages/ImageViewer.swift b/Mlem/App/Views/Pages/ImageViewer.swift index 6e2cf2697..0f34f6fe9 100644 --- a/Mlem/App/Views/Pages/ImageViewer.swift +++ b/Mlem/App/Views/Pages/ImageViewer.swift @@ -13,11 +13,14 @@ struct ImageViewer: View { let url: URL + let duration: CGFloat = 0.25 + @GestureState var dragState: Bool = false @State var isZoomed: Bool = false - @State var offset: CGFloat = UIScreen.main.bounds.height + @State var offset: CGFloat = 0 // UIScreen.main.bounds.height @State var isDismissing: Bool = false + @State var opacity: CGFloat = 0 var screenHeight: CGFloat { UIScreen.main.bounds.height } @@ -32,10 +35,11 @@ struct ImageViewer: View { DynamicMediaView(url: url, cornerRadius: 0, playImmediately: true) } .offset(y: offset) - .onAppear { - updateDragDistance(0) - } - .background(Color.black.opacity(1.0 - (abs(offset) / screenHeight))) +// .onAppear { +// updateDragDistance(0) +// } + .opacity(opacity) + .background(Color.black.opacity((1.0 - (abs(offset) / screenHeight)) * opacity)) .overlay(alignment: .topTrailing) { if offset == 0 { Button { @@ -52,6 +56,11 @@ struct ImageViewer: View { .padding(Constants.main.standardSpacing) } } + .onAppear { + withAnimation(.easeOut(duration: duration)) { + opacity = 1.0 + } + } .simultaneousGesture(DragGesture(minimumDistance: 0.0) .onChanged { value in if !isZoomed, !isDismissing { @@ -73,6 +82,16 @@ struct ImageViewer: View { } } + private func dismiss() { + isDismissing = true + withAnimation(.easeOut(duration: duration)) { + opacity = 0 + } + DispatchQueue.main.asyncAfter(deadline: .now() + duration) { + mediaState.url = nil + } + } + private func dismiss(finalOffset: CGFloat = UIScreen.main.bounds.height) { isDismissing = true updateDragDistance(finalOffset) { @@ -81,7 +100,6 @@ struct ImageViewer: View { } private func updateDragDistance(_ newDistance: CGFloat, callback: (() -> Void)? = nil) { - let duration: CGFloat = 0.25 withAnimation(.easeOut(duration: duration)) { offset = newDistance } From c2a85a3d3cef577955f8e02ef63912f357366c27 Mon Sep 17 00:00:00 2001 From: Eric Andrews Date: Mon, 16 Dec 2024 11:50:01 -0500 Subject: [PATCH 09/11] testing cleanup --- Mlem/App/Views/Pages/ImageViewer.swift | 38 ++++++++++++++------------ 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/Mlem/App/Views/Pages/ImageViewer.swift b/Mlem/App/Views/Pages/ImageViewer.swift index 0f34f6fe9..618db8b03 100644 --- a/Mlem/App/Views/Pages/ImageViewer.swift +++ b/Mlem/App/Views/Pages/ImageViewer.swift @@ -14,16 +14,15 @@ struct ImageViewer: View { let url: URL let duration: CGFloat = 0.25 + let screenHeight: CGFloat = UIScreen.main.bounds.height @GestureState var dragState: Bool = false @State var isZoomed: Bool = false - @State var offset: CGFloat = 0 // UIScreen.main.bounds.height + @State var offset: CGFloat = 0 @State var isDismissing: Bool = false @State var opacity: CGFloat = 0 - var screenHeight: CGFloat { UIScreen.main.bounds.height } - init(url: URL) { var components = URLComponents(url: url, resolvingAgainstBaseURL: false)! components.queryItems = components.queryItems?.filter { $0.name != "thumbnail" } @@ -35,15 +34,12 @@ struct ImageViewer: View { DynamicMediaView(url: url, cornerRadius: 0, playImmediately: true) } .offset(y: offset) -// .onAppear { -// updateDragDistance(0) -// } + .background(Color.black.opacity(1.0 - (abs(offset) / screenHeight))) .opacity(opacity) - .background(Color.black.opacity((1.0 - (abs(offset) / screenHeight)) * opacity)) .overlay(alignment: .topTrailing) { if offset == 0 { Button { - dismiss() + fadeDismiss() } label: { Image(systemName: Icons.close) .resizable() @@ -57,9 +53,7 @@ struct ImageViewer: View { } } .onAppear { - withAnimation(.easeOut(duration: duration)) { - opacity = 1.0 - } + updateOpacity(1.0) } .simultaneousGesture(DragGesture(minimumDistance: 0.0) .onChanged { value in @@ -74,7 +68,7 @@ struct ImageViewer: View { .onChange(of: dragState) { if !dragState { if abs(offset) > 100 { - dismiss(finalOffset: offset > 0 ? screenHeight : -screenHeight) + swipeDismiss(finalOffset: offset > 0 ? screenHeight : -screenHeight) } else { updateDragDistance(0) } @@ -82,23 +76,31 @@ struct ImageViewer: View { } } - private func dismiss() { + private func fadeDismiss() { isDismissing = true - withAnimation(.easeOut(duration: duration)) { - opacity = 0 - } - DispatchQueue.main.asyncAfter(deadline: .now() + duration) { + updateOpacity(0) { mediaState.url = nil } } - private func dismiss(finalOffset: CGFloat = UIScreen.main.bounds.height) { + private func swipeDismiss(finalOffset: CGFloat = UIScreen.main.bounds.height) { isDismissing = true updateDragDistance(finalOffset) { mediaState.url = nil } } + private func updateOpacity(_ newOpacity: CGFloat, callback: (() -> Void)? = nil) { + withAnimation(.easeOut(duration: duration)) { + opacity = 0 + } + if let callback { + DispatchQueue.main.asyncAfter(deadline: .now() + duration) { + callback() + } + } + } + private func updateDragDistance(_ newDistance: CGFloat, callback: (() -> Void)? = nil) { withAnimation(.easeOut(duration: duration)) { offset = newDistance From 67384e5c3c29a91c94b93f9aa0e74ae34b6553ed Mon Sep 17 00:00:00 2001 From: Eric Andrews Date: Mon, 16 Dec 2024 18:03:18 -0500 Subject: [PATCH 10/11] tidy --- Mlem/App/Views/Pages/ImageViewer.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mlem/App/Views/Pages/ImageViewer.swift b/Mlem/App/Views/Pages/ImageViewer.swift index 618db8b03..aadcfba55 100644 --- a/Mlem/App/Views/Pages/ImageViewer.swift +++ b/Mlem/App/Views/Pages/ImageViewer.swift @@ -92,7 +92,7 @@ struct ImageViewer: View { private func updateOpacity(_ newOpacity: CGFloat, callback: (() -> Void)? = nil) { withAnimation(.easeOut(duration: duration)) { - opacity = 0 + opacity = newOpacity } if let callback { DispatchQueue.main.asyncAfter(deadline: .now() + duration) { From c04a5800f80108b0e2a507fa6be3031376f46f9b Mon Sep 17 00:00:00 2001 From: Eric Andrews Date: Mon, 16 Dec 2024 18:46:24 -0500 Subject: [PATCH 11/11] streamline opacity --- Mlem/App/Views/Pages/ImageViewer.swift | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/Mlem/App/Views/Pages/ImageViewer.swift b/Mlem/App/Views/Pages/ImageViewer.swift index aadcfba55..24b81d7c4 100644 --- a/Mlem/App/Views/Pages/ImageViewer.swift +++ b/Mlem/App/Views/Pages/ImageViewer.swift @@ -34,7 +34,7 @@ struct ImageViewer: View { DynamicMediaView(url: url, cornerRadius: 0, playImmediately: true) } .offset(y: offset) - .background(Color.black.opacity(1.0 - (abs(offset) / screenHeight))) + .background(.black) .opacity(opacity) .overlay(alignment: .topTrailing) { if offset == 0 { @@ -52,19 +52,18 @@ struct ImageViewer: View { .padding(Constants.main.standardSpacing) } } - .onAppear { - updateOpacity(1.0) - } .simultaneousGesture(DragGesture(minimumDistance: 0.0) - .onChanged { value in + .updating($dragState) { value, state, _ in + state = true if !isZoomed, !isDismissing { offset = value.translation.height + opacity = 1.0 - (abs(value.translation.height) / screenHeight) } } - .updating($dragState) { _, state, _ in - state = true - } ) + .onAppear { + updateOpacity(1.0) + } .onChange(of: dragState) { if !dragState { if abs(offset) > 100 { @@ -104,6 +103,7 @@ struct ImageViewer: View { private func updateDragDistance(_ newDistance: CGFloat, callback: (() -> Void)? = nil) { withAnimation(.easeOut(duration: duration)) { offset = newDistance + opacity = 1.0 - (abs(newDistance) / screenHeight) } if let callback { DispatchQueue.main.asyncAfter(deadline: .now() + duration) {