damus

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

commit 8ffa8446b61cbc0382cc56e7db6cad0fc85fcaa1
parent fb1f99e7283b2e1cc302fb94cde8cb045e6ffbfe
Author: Swift <scoder1747@gmail.com>
Date:   Wed, 18 Jan 2023 10:07:03 -0800

Image Pinch Zooming

Changelog-Added: Added pinch to zoom on images

Diffstat:
Mdamus.xcodeproj/project.pbxproj | 6+++++-
Mdamus/Components/ImageCarousel.swift | 128+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------
Adamus/Views/ImageView.swift | 20++++++++++++++++++++
Adamus/Views/MagnificationGestureView.swift | 33+++++++++++++++++++++++++++++++++
Mdamus/Views/NoteContentView.swift | 2+-
Mdamus/Views/ProfileView.swift | 5++---
Adamus/Views/ProfileZoomView.swift | 95+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
7 files changed, 256 insertions(+), 33 deletions(-)

diff --git a/damus.xcodeproj/project.pbxproj b/damus.xcodeproj/project.pbxproj @@ -148,6 +148,7 @@ 4CEE2B02280B39E800AB5EEF /* EventActionBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CEE2B01280B39E800AB5EEF /* EventActionBar.swift */; }; 4FE60CDD295E1C5E00105A1F /* Wallet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4FE60CDC295E1C5E00105A1F /* Wallet.swift */; }; 647D9A8D2968520300A295DE /* SideMenuView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 647D9A8C2968520300A295DE /* SideMenuView.swift */; }; + 6439E014296790CF0020672B /* ProfileZoomView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6439E013296790CF0020672B /* ProfileZoomView.swift */; }; 64FBD06F296255C400D9D3B2 /* Theme.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64FBD06E296255C400D9D3B2 /* Theme.swift */; }; 6C7DE41F2955169800E66263 /* Vault in Frameworks */ = {isa = PBXBuildFile; productRef = 6C7DE41E2955169800E66263 /* Vault */; }; 7C45AE6D297352F90031D7BC /* SVGKit in Frameworks */ = {isa = PBXBuildFile; productRef = 7C45AE6C297352F90031D7BC /* SVGKit */; }; @@ -356,6 +357,7 @@ 4CEE2B01280B39E800AB5EEF /* EventActionBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventActionBar.swift; sourceTree = "<group>"; }; 4FE60CDC295E1C5E00105A1F /* Wallet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Wallet.swift; sourceTree = "<group>"; }; 647D9A8C2968520300A295DE /* SideMenuView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SideMenuView.swift; sourceTree = "<group>"; }; + 6439E013296790CF0020672B /* ProfileZoomView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileZoomView.swift; sourceTree = "<group>"; }; 64FBD06E296255C400D9D3B2 /* Theme.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Theme.swift; sourceTree = "<group>"; }; 7C45AE70297353390031D7BC /* KFImageModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KFImageModel.swift; sourceTree = "<group>"; }; 9609F057296E220800069BF3 /* BannerImageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BannerImageView.swift; sourceTree = "<group>"; }; @@ -514,8 +516,8 @@ 4C216F33286F5ACD00040376 /* DMView.swift */, E990020E2955F837003BBC5A /* EditMetadataView.swift */, 3169CAE4294E699400EE4006 /* Empty Views */, - 4CEE2AF0280B216B00AB5EEF /* EventDetailView.swift */, 4C75EFB82804A2740006080F /* EventView.swift */, + 4CEE2AF0280B216B00AB5EEF /* EventDetailView.swift */, 4C3AC79E2833115300E1F516 /* FollowButtonView.swift */, 4C3AC79C2833036D00E1F516 /* FollowingView.swift */, 4C90BD17283A9EE5008EE7EF /* LoginView.swift */, @@ -547,6 +549,7 @@ 647D9A8C2968520300A295DE /* SideMenuView.swift */, 9609F057296E220800069BF3 /* BannerImageView.swift */, 4CB8838E296F781C00DC99E7 /* ReactionsView.swift */, + 6439E013296790CF0020672B /* ProfileZoomView.swift */, ); path = Views; sourceTree = "<group>"; @@ -949,6 +952,7 @@ 4C5F9114283D694D0052CD1C /* FollowTarget.swift in Sources */, 4CB8838629656C8B00DC99E7 /* NIP05.swift in Sources */, 4C5C7E6A284EDE2E00A22DF5 /* SearchResultsView.swift in Sources */, + 6439E014296790CF0020672B /* ProfileZoomView.swift in Sources */, 4CE6DF1627F8DEBF00C66700 /* RelayConnection.swift in Sources */, 4C3BEFD6281D995700B3DE84 /* ActionBarModel.swift in Sources */, 4C363AA428296DEE006E126D /* SearchModel.swift in Sources */, diff --git a/damus/Components/ImageCarousel.swift b/damus/Components/ImageCarousel.swift @@ -64,9 +64,50 @@ struct ImageContextMenuModifier: ViewModifier { } } -struct ImageViewer: View { +struct ImageView: View { let urls: [URL] + @Environment(\.presentationMode) var presentationMode + //let pubkey: String + //let profiles: Profiles + + @GestureState private var scaleState: CGFloat = 1 + @GestureState private var offsetState = CGSize.zero + + @State private var offset = CGSize.zero + @State private var scale: CGFloat = 1 + + func resetStatus(){ + self.offset = CGSize.zero + self.scale = 1 + } + + var zoomGesture: some Gesture { + MagnificationGesture() + .updating($scaleState) { currentState, gestureState, _ in + gestureState = currentState + } + .onEnded { value in + scale *= value + } + } + + var dragGesture: some Gesture { + DragGesture() + .updating($offsetState) { currentState, gestureState, _ in + gestureState = currentState.translation + }.onEnded { value in + offset.height += value.translation.height + offset.width += value.translation.width + } + } + + var doubleTapGesture : some Gesture { + TapGesture(count: 2).onEnded { value in + resetStatus() + } + } + private struct ImageHandler: ImageModifier { @Binding var handler: UIImage? @@ -86,30 +127,61 @@ struct ImageViewer: View { } var body: some View { - TabView { - ForEach(urls, id: \.absoluteString) { url in - VStack{ - Text(url.lastPathComponent) - - KFAnimatedImage(url) - .configure { view in - view.framePreloadCount = 3 - } - .cacheOriginalImage() - .imageModifier(ImageHandler(handler: $image)) - .loadDiskFileSynchronously() - .scaleFactor(UIScreen.main.scale) - .fade(duration: 0.1) - .aspectRatio(contentMode: .fit) - .tabItem { - Text(url.absoluteString) - } - .id(url.absoluteString) - .modifier(ImageContextMenuModifier(url: url, image: image, showShareSheet: $showShareSheet)) - .sheet(isPresented: $showShareSheet) { - ShareSheet(activityItems: [url]) - } - + ZStack(alignment: .topLeading) { + Color("DamusDarkGrey") // Or Color("DamusBlack") + .edgesIgnoringSafeArea(.all) + + HStack() { + Button { + presentationMode.wrappedValue.dismiss() + } label: { + Image(systemName: "xmark") + .foregroundColor(.white) + .font(.largeTitle) + .frame(width: 40, height: 40) + .padding(20) + } + } + .zIndex(1) + + VStack(alignment: .center) { + //Spacer() + //.frame(height: 120) + + TabView { + ForEach(urls, id: \.absoluteString) { url in + VStack{ + //Color("DamusDarkGrey") + Text(url.lastPathComponent) + .foregroundColor(Color("DamusWhite")) + + KFAnimatedImage(url) + .configure { view in + view.framePreloadCount = 3 + } + .cacheOriginalImage() + .imageModifier(ImageHandler(handler: $image)) + .loadDiskFileSynchronously() + .scaleFactor(UIScreen.main.scale) + .fade(duration: 0.1) + .aspectRatio(contentMode: .fit) + .tabItem { + Text(url.absoluteString) + } + .id(url.absoluteString) + .modifier(ImageContextMenuModifier(url: url, image: image, showShareSheet: $showShareSheet)) + .sheet(isPresented: $showShareSheet) { + ShareSheet(activityItems: [url]) + } + //.padding(100) + .scaledToFit() + .scaleEffect(self.scale * scaleState) + .offset(x: offset.width + offsetState.width, y: offset.height + offsetState.height) + .gesture(SimultaneousGesture(zoomGesture, dragGesture)) + .gesture(doubleTapGesture) + + }.padding(.bottom, 50) // Ensure carousel appears beneath + } } } } @@ -151,8 +223,8 @@ struct ImageCarousel: View { } } .cornerRadius(10) - .sheet(isPresented: $open_sheet) { - ImageViewer(urls: urls) + .fullScreenCover(isPresented: $open_sheet) { + ImageView(urls: urls) } .frame(height: 200) .onTapGesture { @@ -164,6 +236,6 @@ struct ImageCarousel: View { struct ImageCarousel_Previews: PreviewProvider { static var previews: some View { - ImageCarousel(urls: [URL(string: "https://jb55.com/red-me.jpg")!]) + ImageCarousel(urls: [URL(string: "https://jb55.com/red-me.jpg")!,URL(string: "https://jb55.com/red-me.jpg")!]) } } diff --git a/damus/Views/ImageView.swift b/damus/Views/ImageView.swift @@ -0,0 +1,20 @@ +// +// ImageView.swift +// damus +// +// Created by user232838 on 1/5/23. +// + +import SwiftUI + +struct ImageView: View { + var body: some View { + Text(/*@START_MENU_TOKEN@*/"Hello, World!"/*@END_MENU_TOKEN@*/) + } +} + +struct ImageView_Previews: PreviewProvider { + static var previews: some View { + ImageView() + } +} diff --git a/damus/Views/MagnificationGestureView.swift b/damus/Views/MagnificationGestureView.swift @@ -0,0 +1,33 @@ +// +// MagnificationGestureView.swift +// damus +// +// Created by user232838 on 1/5/23. +// + +import SwiftUI + +struct MagnificationGestureView: View { + + @GestureState var magnifyBy = 1.0 + + var magnification: some Gesture { + MagnificationGesture() + .updating($magnifyBy) { currentState, gestureState, transaction in + gestureState = currentState + } + } + + var body: some View { + Circle() + .frame(width: 100, height: 100) + .scaleEffect(magnifyBy) + .gesture(magnification) + } +} + +struct MagnificationGestureView_Previews: PreviewProvider { + static var previews: some View { + MagnificationGestureView() + } +} diff --git a/damus/Views/NoteContentView.swift b/damus/Views/NoteContentView.swift @@ -53,7 +53,7 @@ func render_note_content(ev: NostrEvent, profiles: Profiles, privkey: String?) - } func is_image_url(_ url: URL) -> Bool { - let str = url.lastPathComponent + let str = url.lastPathComponent.lowercased() return str.hasSuffix("png") || str.hasSuffix("jpg") || str.hasSuffix("jpeg") || str.hasSuffix("gif") } diff --git a/damus/Views/ProfileView.swift b/damus/Views/ProfileView.swift @@ -211,9 +211,8 @@ struct ProfileView: View { .onTapGesture { is_zoomed.toggle() } - .sheet(isPresented: $is_zoomed) { - ProfilePicView(pubkey: profile.pubkey, size: zoom_size, highlight: .none, profiles: damus_state.profiles) - } + .fullScreenCover(isPresented: $is_zoomed) { + ProfileZoomView(pubkey: profile.pubkey, profiles: damus_state.profiles) } .offset(y: -(pfp_size/2.0)) // Increase if set a frame Spacer() diff --git a/damus/Views/ProfileZoomView.swift b/damus/Views/ProfileZoomView.swift @@ -0,0 +1,95 @@ +// +// ProfileZoomView.swift +// damus +// +// Created by scoder1747 on 12/27/22. +// +import SwiftUI + +struct ProfileZoomView: View { + + @Environment(\.presentationMode) var presentationMode + let pubkey: String + let profiles: Profiles + + @GestureState private var scaleState: CGFloat = 1 + @GestureState private var offsetState = CGSize.zero + + @State private var offset = CGSize.zero + @State private var scale: CGFloat = 1 + + func resetStatus(){ + self.offset = CGSize.zero + self.scale = 1 + } + + var zoomGesture: some Gesture { + MagnificationGesture() + .updating($scaleState) { currentState, gestureState, _ in + gestureState = currentState + } + .onEnded { value in + scale *= value + } + } + + var dragGesture: some Gesture { + DragGesture() + .updating($offsetState) { currentState, gestureState, _ in + gestureState = currentState.translation + }.onEnded { value in + offset.height += value.translation.height + offset.width += value.translation.width + } + } + + var doubleTapGesture : some Gesture { + TapGesture(count: 2).onEnded { value in + resetStatus() + } + } + + var body: some View { + ZStack(alignment: .topLeading) { + Color("DamusDarkGrey") // Or Color("DamusBlack") + .edgesIgnoringSafeArea(.all) + + HStack() { + Button { + presentationMode.wrappedValue.dismiss() + } label: { + Image(systemName: "xmark") + .foregroundColor(.white) + .font(.largeTitle) + .frame(width: 40, height: 40) + .padding(20) + } + } + .zIndex(1) + + VStack(alignment: .center) { + Spacer() + .frame(height: 120) + + ProfilePicView(pubkey: pubkey, size: 200.0, highlight: .none, profiles: profiles) + .padding(100) + .scaledToFit() + .scaleEffect(self.scale * scaleState) + .offset(x: offset.width + offsetState.width, y: offset.height + offsetState.height) + .gesture(SimultaneousGesture(zoomGesture, dragGesture)) + .gesture(doubleTapGesture) + + } + } + } +} + +struct ProfileZoomView_Previews: PreviewProvider { + static let pubkey = "ca48854ac6555fed8e439ebb4fa2d928410e0eef13fa41164ec45aaaa132d846" + + static var previews: some View { + ProfileZoomView( + pubkey: pubkey, + profiles: make_preview_profiles(pubkey)) + } +}