Image Pinch Zooming
Changelog-Added: Added pinch to zoom on images
This commit is contained in:
@@ -148,6 +148,7 @@
|
|||||||
4CEE2B02280B39E800AB5EEF /* EventActionBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CEE2B01280B39E800AB5EEF /* EventActionBar.swift */; };
|
4CEE2B02280B39E800AB5EEF /* EventActionBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CEE2B01280B39E800AB5EEF /* EventActionBar.swift */; };
|
||||||
4FE60CDD295E1C5E00105A1F /* Wallet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4FE60CDC295E1C5E00105A1F /* Wallet.swift */; };
|
4FE60CDD295E1C5E00105A1F /* Wallet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4FE60CDC295E1C5E00105A1F /* Wallet.swift */; };
|
||||||
647D9A8D2968520300A295DE /* SideMenuView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 647D9A8C2968520300A295DE /* SideMenuView.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 */; };
|
64FBD06F296255C400D9D3B2 /* Theme.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64FBD06E296255C400D9D3B2 /* Theme.swift */; };
|
||||||
6C7DE41F2955169800E66263 /* Vault in Frameworks */ = {isa = PBXBuildFile; productRef = 6C7DE41E2955169800E66263 /* Vault */; };
|
6C7DE41F2955169800E66263 /* Vault in Frameworks */ = {isa = PBXBuildFile; productRef = 6C7DE41E2955169800E66263 /* Vault */; };
|
||||||
7C45AE6D297352F90031D7BC /* SVGKit in Frameworks */ = {isa = PBXBuildFile; productRef = 7C45AE6C297352F90031D7BC /* SVGKit */; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
9609F057296E220800069BF3 /* BannerImageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BannerImageView.swift; sourceTree = "<group>"; };
|
||||||
@@ -514,8 +516,8 @@
|
|||||||
4C216F33286F5ACD00040376 /* DMView.swift */,
|
4C216F33286F5ACD00040376 /* DMView.swift */,
|
||||||
E990020E2955F837003BBC5A /* EditMetadataView.swift */,
|
E990020E2955F837003BBC5A /* EditMetadataView.swift */,
|
||||||
3169CAE4294E699400EE4006 /* Empty Views */,
|
3169CAE4294E699400EE4006 /* Empty Views */,
|
||||||
4CEE2AF0280B216B00AB5EEF /* EventDetailView.swift */,
|
|
||||||
4C75EFB82804A2740006080F /* EventView.swift */,
|
4C75EFB82804A2740006080F /* EventView.swift */,
|
||||||
|
4CEE2AF0280B216B00AB5EEF /* EventDetailView.swift */,
|
||||||
4C3AC79E2833115300E1F516 /* FollowButtonView.swift */,
|
4C3AC79E2833115300E1F516 /* FollowButtonView.swift */,
|
||||||
4C3AC79C2833036D00E1F516 /* FollowingView.swift */,
|
4C3AC79C2833036D00E1F516 /* FollowingView.swift */,
|
||||||
4C90BD17283A9EE5008EE7EF /* LoginView.swift */,
|
4C90BD17283A9EE5008EE7EF /* LoginView.swift */,
|
||||||
@@ -547,6 +549,7 @@
|
|||||||
647D9A8C2968520300A295DE /* SideMenuView.swift */,
|
647D9A8C2968520300A295DE /* SideMenuView.swift */,
|
||||||
9609F057296E220800069BF3 /* BannerImageView.swift */,
|
9609F057296E220800069BF3 /* BannerImageView.swift */,
|
||||||
4CB8838E296F781C00DC99E7 /* ReactionsView.swift */,
|
4CB8838E296F781C00DC99E7 /* ReactionsView.swift */,
|
||||||
|
6439E013296790CF0020672B /* ProfileZoomView.swift */,
|
||||||
);
|
);
|
||||||
path = Views;
|
path = Views;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -949,6 +952,7 @@
|
|||||||
4C5F9114283D694D0052CD1C /* FollowTarget.swift in Sources */,
|
4C5F9114283D694D0052CD1C /* FollowTarget.swift in Sources */,
|
||||||
4CB8838629656C8B00DC99E7 /* NIP05.swift in Sources */,
|
4CB8838629656C8B00DC99E7 /* NIP05.swift in Sources */,
|
||||||
4C5C7E6A284EDE2E00A22DF5 /* SearchResultsView.swift in Sources */,
|
4C5C7E6A284EDE2E00A22DF5 /* SearchResultsView.swift in Sources */,
|
||||||
|
6439E014296790CF0020672B /* ProfileZoomView.swift in Sources */,
|
||||||
4CE6DF1627F8DEBF00C66700 /* RelayConnection.swift in Sources */,
|
4CE6DF1627F8DEBF00C66700 /* RelayConnection.swift in Sources */,
|
||||||
4C3BEFD6281D995700B3DE84 /* ActionBarModel.swift in Sources */,
|
4C3BEFD6281D995700B3DE84 /* ActionBarModel.swift in Sources */,
|
||||||
4C363AA428296DEE006E126D /* SearchModel.swift in Sources */,
|
4C363AA428296DEE006E126D /* SearchModel.swift in Sources */,
|
||||||
|
|||||||
@@ -64,9 +64,50 @@ struct ImageContextMenuModifier: ViewModifier {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ImageViewer: View {
|
struct ImageView: View {
|
||||||
let urls: [URL]
|
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 {
|
private struct ImageHandler: ImageModifier {
|
||||||
@Binding var handler: UIImage?
|
@Binding var handler: UIImage?
|
||||||
|
|
||||||
@@ -86,30 +127,61 @@ struct ImageViewer: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
TabView {
|
ZStack(alignment: .topLeading) {
|
||||||
ForEach(urls, id: \.absoluteString) { url in
|
Color("DamusDarkGrey") // Or Color("DamusBlack")
|
||||||
VStack{
|
.edgesIgnoringSafeArea(.all)
|
||||||
Text(url.lastPathComponent)
|
|
||||||
|
HStack() {
|
||||||
KFAnimatedImage(url)
|
Button {
|
||||||
.configure { view in
|
presentationMode.wrappedValue.dismiss()
|
||||||
view.framePreloadCount = 3
|
} label: {
|
||||||
}
|
Image(systemName: "xmark")
|
||||||
.cacheOriginalImage()
|
.foregroundColor(.white)
|
||||||
.imageModifier(ImageHandler(handler: $image))
|
.font(.largeTitle)
|
||||||
.loadDiskFileSynchronously()
|
.frame(width: 40, height: 40)
|
||||||
.scaleFactor(UIScreen.main.scale)
|
.padding(20)
|
||||||
.fade(duration: 0.1)
|
}
|
||||||
.aspectRatio(contentMode: .fit)
|
}
|
||||||
.tabItem {
|
.zIndex(1)
|
||||||
Text(url.absoluteString)
|
|
||||||
}
|
VStack(alignment: .center) {
|
||||||
.id(url.absoluteString)
|
//Spacer()
|
||||||
.modifier(ImageContextMenuModifier(url: url, image: image, showShareSheet: $showShareSheet))
|
//.frame(height: 120)
|
||||||
.sheet(isPresented: $showShareSheet) {
|
|
||||||
ShareSheet(activityItems: [url])
|
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)
|
.cornerRadius(10)
|
||||||
.sheet(isPresented: $open_sheet) {
|
.fullScreenCover(isPresented: $open_sheet) {
|
||||||
ImageViewer(urls: urls)
|
ImageView(urls: urls)
|
||||||
}
|
}
|
||||||
.frame(height: 200)
|
.frame(height: 200)
|
||||||
.onTapGesture {
|
.onTapGesture {
|
||||||
@@ -164,6 +236,6 @@ struct ImageCarousel: View {
|
|||||||
|
|
||||||
struct ImageCarousel_Previews: PreviewProvider {
|
struct ImageCarousel_Previews: PreviewProvider {
|
||||||
static var previews: some View {
|
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")!])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
20
damus/Views/ImageView.swift
Normal file
20
damus/Views/ImageView.swift
Normal file
@@ -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()
|
||||||
|
}
|
||||||
|
}
|
||||||
33
damus/Views/MagnificationGestureView.swift
Normal file
33
damus/Views/MagnificationGestureView.swift
Normal file
@@ -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()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -53,7 +53,7 @@ func render_note_content(ev: NostrEvent, profiles: Profiles, privkey: String?) -
|
|||||||
}
|
}
|
||||||
|
|
||||||
func is_image_url(_ url: URL) -> Bool {
|
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")
|
return str.hasSuffix("png") || str.hasSuffix("jpg") || str.hasSuffix("jpeg") || str.hasSuffix("gif")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -211,9 +211,8 @@ struct ProfileView: View {
|
|||||||
.onTapGesture {
|
.onTapGesture {
|
||||||
is_zoomed.toggle()
|
is_zoomed.toggle()
|
||||||
}
|
}
|
||||||
.sheet(isPresented: $is_zoomed) {
|
.fullScreenCover(isPresented: $is_zoomed) {
|
||||||
ProfilePicView(pubkey: profile.pubkey, size: zoom_size, highlight: .none, profiles: damus_state.profiles)
|
ProfileZoomView(pubkey: profile.pubkey, profiles: damus_state.profiles) }
|
||||||
}
|
|
||||||
.offset(y: -(pfp_size/2.0)) // Increase if set a frame
|
.offset(y: -(pfp_size/2.0)) // Increase if set a frame
|
||||||
|
|
||||||
Spacer()
|
Spacer()
|
||||||
|
|||||||
95
damus/Views/ProfileZoomView.swift
Normal file
95
damus/Views/ProfileZoomView.swift
Normal file
@@ -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))
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user