Clean up image views
This commit is contained in:
@@ -222,6 +222,9 @@
|
|||||||
4CF0ABEE29844B5500D66079 /* AnyEncodable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CF0ABED29844B5500D66079 /* AnyEncodable.swift */; };
|
4CF0ABEE29844B5500D66079 /* AnyEncodable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CF0ABED29844B5500D66079 /* AnyEncodable.swift */; };
|
||||||
4CF0ABF029857E9200D66079 /* Bech32Object.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CF0ABEF29857E9200D66079 /* Bech32Object.swift */; };
|
4CF0ABF029857E9200D66079 /* Bech32Object.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CF0ABEF29857E9200D66079 /* Bech32Object.swift */; };
|
||||||
4CF0ABF62985CD5500D66079 /* UserSearch.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CF0ABF52985CD5500D66079 /* UserSearch.swift */; };
|
4CF0ABF62985CD5500D66079 /* UserSearch.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CF0ABF52985CD5500D66079 /* UserSearch.swift */; };
|
||||||
|
4CFF8F6329CC9AD7008DB934 /* ImageContextMenuModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CFF8F6229CC9AD7008DB934 /* ImageContextMenuModifier.swift */; };
|
||||||
|
4CFF8F6729CC9E3A008DB934 /* ImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CFF8F6629CC9E3A008DB934 /* ImageView.swift */; };
|
||||||
|
4CFF8F6929CC9ED1008DB934 /* ImageContainerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CFF8F6829CC9ED1008DB934 /* ImageContainerView.swift */; };
|
||||||
4FE60CDD295E1C5E00105A1F /* Wallet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4FE60CDC295E1C5E00105A1F /* Wallet.swift */; };
|
4FE60CDD295E1C5E00105A1F /* Wallet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4FE60CDC295E1C5E00105A1F /* Wallet.swift */; };
|
||||||
50A50A8D29A09E1C00C01BE7 /* RequestTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50A50A8C29A09E1C00C01BE7 /* RequestTests.swift */; };
|
50A50A8D29A09E1C00C01BE7 /* RequestTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50A50A8C29A09E1C00C01BE7 /* RequestTests.swift */; };
|
||||||
5C513FBA297F72980072348F /* CustomPicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C513FB9297F72980072348F /* CustomPicker.swift */; };
|
5C513FBA297F72980072348F /* CustomPicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C513FB9297F72980072348F /* CustomPicker.swift */; };
|
||||||
@@ -595,6 +598,9 @@
|
|||||||
4CF0ABED29844B5500D66079 /* AnyEncodable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnyEncodable.swift; sourceTree = "<group>"; };
|
4CF0ABED29844B5500D66079 /* AnyEncodable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnyEncodable.swift; sourceTree = "<group>"; };
|
||||||
4CF0ABEF29857E9200D66079 /* Bech32Object.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Bech32Object.swift; sourceTree = "<group>"; };
|
4CF0ABEF29857E9200D66079 /* Bech32Object.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Bech32Object.swift; sourceTree = "<group>"; };
|
||||||
4CF0ABF52985CD5500D66079 /* UserSearch.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserSearch.swift; sourceTree = "<group>"; };
|
4CF0ABF52985CD5500D66079 /* UserSearch.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserSearch.swift; sourceTree = "<group>"; };
|
||||||
|
4CFF8F6229CC9AD7008DB934 /* ImageContextMenuModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageContextMenuModifier.swift; sourceTree = "<group>"; };
|
||||||
|
4CFF8F6629CC9E3A008DB934 /* ImageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageView.swift; sourceTree = "<group>"; };
|
||||||
|
4CFF8F6829CC9ED1008DB934 /* ImageContainerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageContainerView.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>"; };
|
||||||
50A50A8C29A09E1C00C01BE7 /* RequestTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequestTests.swift; sourceTree = "<group>"; };
|
50A50A8C29A09E1C00C01BE7 /* RequestTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequestTests.swift; sourceTree = "<group>"; };
|
||||||
5C513FB9297F72980072348F /* CustomPicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomPicker.swift; sourceTree = "<group>"; };
|
5C513FB9297F72980072348F /* CustomPicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomPicker.swift; sourceTree = "<group>"; };
|
||||||
@@ -797,6 +803,7 @@
|
|||||||
4C75EFA227FA576C0006080F /* Views */ = {
|
4C75EFA227FA576C0006080F /* Views */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
4CFF8F6129CC9A80008DB934 /* Images */,
|
||||||
4CCEB7AC29B53D180078AA28 /* Search */,
|
4CCEB7AC29B53D180078AA28 /* Search */,
|
||||||
4C30AC7029A5676F00E2BD5A /* Notifications */,
|
4C30AC7029A5676F00E2BD5A /* Notifications */,
|
||||||
4CE0E2B029A3DF4700DB4CA2 /* Timeline */,
|
4CE0E2B029A3DF4700DB4CA2 /* Timeline */,
|
||||||
@@ -834,10 +841,6 @@
|
|||||||
9CA876E129A00CE90003B9A3 /* AttachMediaUtility.swift */,
|
9CA876E129A00CE90003B9A3 /* AttachMediaUtility.swift */,
|
||||||
9C83F89229A937B900136C08 /* TextViewWrapper.swift */,
|
9C83F89229A937B900136C08 /* TextViewWrapper.swift */,
|
||||||
4CEE2AF8280B2EAC00AB5EEF /* PowView.swift */,
|
4CEE2AF8280B2EAC00AB5EEF /* PowView.swift */,
|
||||||
4CEE2AF6280B2DEA00AB5EEF /* ProfileName.swift */,
|
|
||||||
4C285C892838B985008A31F1 /* ProfilePictureSelector.swift */,
|
|
||||||
4CEE2AF2280B25C500AB5EEF /* ProfilePicView.swift */,
|
|
||||||
4C8682862814DE470026224F /* ProfileView.swift */,
|
|
||||||
4C3AC7A42836987600E1F516 /* MainTabView.swift */,
|
4C3AC7A42836987600E1F516 /* MainTabView.swift */,
|
||||||
4C363A8B28236B92006E126D /* PubkeyView.swift */,
|
4C363A8B28236B92006E126D /* PubkeyView.swift */,
|
||||||
4CACA9D4280C31E100D9BBE8 /* ReplyView.swift */,
|
4CACA9D4280C31E100D9BBE8 /* ReplyView.swift */,
|
||||||
@@ -959,6 +962,10 @@
|
|||||||
4CB9D4A52992D01900A9A7E4 /* Profile */ = {
|
4CB9D4A52992D01900A9A7E4 /* Profile */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
4CEE2AF6280B2DEA00AB5EEF /* ProfileName.swift */,
|
||||||
|
4C285C892838B985008A31F1 /* ProfilePictureSelector.swift */,
|
||||||
|
4CEE2AF2280B25C500AB5EEF /* ProfilePicView.swift */,
|
||||||
|
4C8682862814DE470026224F /* ProfileView.swift */,
|
||||||
4CB9D4A62992D02B00A9A7E4 /* ProfileNameView.swift */,
|
4CB9D4A62992D02B00A9A7E4 /* ProfileNameView.swift */,
|
||||||
4CB9D4A82992D2F400A9A7E4 /* FollowsYou.swift */,
|
4CB9D4A82992D2F400A9A7E4 /* FollowsYou.swift */,
|
||||||
4C9F18E329ABDE6D008C55EC /* MaybeAnonPfpView.swift */,
|
4C9F18E329ABDE6D008C55EC /* MaybeAnonPfpView.swift */,
|
||||||
@@ -1177,6 +1184,16 @@
|
|||||||
path = Posting;
|
path = Posting;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
|
4CFF8F6129CC9A80008DB934 /* Images */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
4CFF8F6229CC9AD7008DB934 /* ImageContextMenuModifier.swift */,
|
||||||
|
4CFF8F6629CC9E3A008DB934 /* ImageView.swift */,
|
||||||
|
4CFF8F6829CC9ED1008DB934 /* ImageContainerView.swift */,
|
||||||
|
);
|
||||||
|
path = Images;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
7C0F392D29B57C8F0039859C /* Extensions */ = {
|
7C0F392D29B57C8F0039859C /* Extensions */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
@@ -1434,6 +1451,7 @@
|
|||||||
4CE4F9E328528C5200C00DD9 /* AddRelayView.swift in Sources */,
|
4CE4F9E328528C5200C00DD9 /* AddRelayView.swift in Sources */,
|
||||||
4C363A9A28283854006E126D /* Reply.swift in Sources */,
|
4C363A9A28283854006E126D /* Reply.swift in Sources */,
|
||||||
BA693074295D649800ADDB87 /* UserSettingsStore.swift in Sources */,
|
BA693074295D649800ADDB87 /* UserSettingsStore.swift in Sources */,
|
||||||
|
4CFF8F6729CC9E3A008DB934 /* ImageView.swift in Sources */,
|
||||||
4C90BD18283A9EE5008EE7EF /* LoginView.swift in Sources */,
|
4C90BD18283A9EE5008EE7EF /* LoginView.swift in Sources */,
|
||||||
4CB8838B296F6E1E00DC99E7 /* NIP05Badge.swift in Sources */,
|
4CB8838B296F6E1E00DC99E7 /* NIP05Badge.swift in Sources */,
|
||||||
4C3EA66828FF5F9900C48A62 /* hex.c in Sources */,
|
4C3EA66828FF5F9900C48A62 /* hex.c in Sources */,
|
||||||
@@ -1488,6 +1506,7 @@
|
|||||||
4C363A94282704FA006E126D /* Post.swift in Sources */,
|
4C363A94282704FA006E126D /* Post.swift in Sources */,
|
||||||
4C216F32286E388800040376 /* DMChatView.swift in Sources */,
|
4C216F32286E388800040376 /* DMChatView.swift in Sources */,
|
||||||
4CAAD8AD298851D000060CEA /* AccountDeletion.swift in Sources */,
|
4CAAD8AD298851D000060CEA /* AccountDeletion.swift in Sources */,
|
||||||
|
4CFF8F6329CC9AD7008DB934 /* ImageContextMenuModifier.swift in Sources */,
|
||||||
4C54AA0A29A55429003E4487 /* EventGroup.swift in Sources */,
|
4C54AA0A29A55429003E4487 /* EventGroup.swift in Sources */,
|
||||||
4C3EA67928FF7ABF00C48A62 /* list.c in Sources */,
|
4C3EA67928FF7ABF00C48A62 /* list.c in Sources */,
|
||||||
4C64987E286D082C00EAE2B3 /* DirectMessagesModel.swift in Sources */,
|
4C64987E286D082C00EAE2B3 /* DirectMessagesModel.swift in Sources */,
|
||||||
@@ -1547,6 +1566,7 @@
|
|||||||
4C3EA63D28FF52D600C48A62 /* bolt11.c in Sources */,
|
4C3EA63D28FF52D600C48A62 /* bolt11.c in Sources */,
|
||||||
4C9BB83129C0ED4F00FC4E37 /* DisplayName.swift in Sources */,
|
4C9BB83129C0ED4F00FC4E37 /* DisplayName.swift in Sources */,
|
||||||
7CFF6317299FEFE5005D382A /* SelectableText.swift in Sources */,
|
7CFF6317299FEFE5005D382A /* SelectableText.swift in Sources */,
|
||||||
|
4CFF8F6929CC9ED1008DB934 /* ImageContainerView.swift in Sources */,
|
||||||
4C54AA0729A540BA003E4487 /* NotificationsModel.swift in Sources */,
|
4C54AA0729A540BA003E4487 /* NotificationsModel.swift in Sources */,
|
||||||
4CB55EF3295E5D59007FD187 /* RecommendedRelayView.swift in Sources */,
|
4CB55EF3295E5D59007FD187 /* RecommendedRelayView.swift in Sources */,
|
||||||
4CF0ABEC29844B4700D66079 /* AnyDecodable.swift in Sources */,
|
4CF0ABEC29844B4700D66079 /* AnyDecodable.swift in Sources */,
|
||||||
|
|||||||
@@ -31,158 +31,9 @@ struct ShareSheet: UIViewControllerRepresentable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ImageContextMenuModifier: ViewModifier {
|
|
||||||
let url: URL?
|
|
||||||
let image: UIImage?
|
|
||||||
@Binding var showShareSheet: Bool
|
|
||||||
|
|
||||||
func body(content: Content) -> some View {
|
|
||||||
return content.contextMenu {
|
|
||||||
Button {
|
|
||||||
UIPasteboard.general.url = url
|
|
||||||
} label: {
|
|
||||||
Label(NSLocalizedString("Copy Image URL", comment: "Context menu option to copy the URL of an image into clipboard."), systemImage: "doc.on.doc")
|
|
||||||
}
|
|
||||||
if let someImage = image {
|
|
||||||
Button {
|
|
||||||
UIPasteboard.general.image = someImage
|
|
||||||
} label: {
|
|
||||||
Label(NSLocalizedString("Copy Image", comment: "Context menu option to copy an image into clipboard."), systemImage: "photo.on.rectangle")
|
|
||||||
}
|
|
||||||
Button {
|
|
||||||
UIImageWriteToSavedPhotosAlbum(someImage, nil, nil, nil)
|
|
||||||
} label: {
|
|
||||||
Label(NSLocalizedString("Save Image", comment: "Context menu option to save an image."), systemImage: "square.and.arrow.down")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Button {
|
|
||||||
showShareSheet = true
|
|
||||||
} label: {
|
|
||||||
Label(NSLocalizedString("Share", comment: "Button to share an image."), systemImage: "square.and.arrow.up")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private struct ImageContainerView: View {
|
|
||||||
|
|
||||||
let url: URL?
|
|
||||||
|
|
||||||
@State private var image: UIImage?
|
|
||||||
@State private var showShareSheet = false
|
|
||||||
|
|
||||||
private struct ImageHandler: ImageModifier {
|
|
||||||
@Binding var handler: UIImage?
|
|
||||||
|
|
||||||
func modify(_ image: UIImage) -> UIImage {
|
|
||||||
handler = image
|
|
||||||
return image
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var body: some View {
|
|
||||||
|
|
||||||
KFAnimatedImage(url)
|
|
||||||
.imageContext(.note)
|
|
||||||
.configure { view in
|
|
||||||
view.framePreloadCount = 3
|
|
||||||
}
|
|
||||||
.imageModifier(ImageHandler(handler: $image))
|
|
||||||
.clipped()
|
|
||||||
.modifier(ImageContextMenuModifier(url: url, image: image, showShareSheet: $showShareSheet))
|
|
||||||
.sheet(isPresented: $showShareSheet) {
|
|
||||||
ShareSheet(activityItems: [url])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct ImageView: View {
|
|
||||||
|
|
||||||
let urls: [URL?]
|
|
||||||
|
|
||||||
@Environment(\.presentationMode) var presentationMode
|
|
||||||
|
|
||||||
@State private var selectedIndex = 0
|
|
||||||
@State var showMenu = true
|
|
||||||
|
|
||||||
var navBarView: some View {
|
|
||||||
VStack {
|
|
||||||
HStack {
|
|
||||||
/*
|
|
||||||
Text(urls[selectedIndex]?.lastPathComponent ?? "")
|
|
||||||
.bold()
|
|
||||||
*/
|
|
||||||
|
|
||||||
Spacer()
|
|
||||||
|
|
||||||
Button(action: {
|
|
||||||
presentationMode.wrappedValue.dismiss()
|
|
||||||
}, label: {
|
|
||||||
Image(systemName: "xmark")
|
|
||||||
})
|
|
||||||
}
|
|
||||||
.padding()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var tabViewIndicator: some View {
|
|
||||||
HStack(spacing: 10) {
|
|
||||||
ForEach(urls.indices, id: \.self) { index in
|
|
||||||
Capsule()
|
|
||||||
.fill(index == selectedIndex ? Color(UIColor.label) : Color.secondary)
|
|
||||||
.frame(width: 7, height: 7)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.padding()
|
|
||||||
.background(.regularMaterial)
|
|
||||||
.clipShape(Capsule())
|
|
||||||
}
|
|
||||||
|
|
||||||
var body: some View {
|
|
||||||
ZStack {
|
|
||||||
Color(.systemBackground)
|
|
||||||
.ignoresSafeArea()
|
|
||||||
|
|
||||||
TabView(selection: $selectedIndex) {
|
|
||||||
ForEach(urls.indices, id: \.self) { index in
|
|
||||||
ZoomableScrollView {
|
|
||||||
ImageContainerView(url: urls[index])
|
|
||||||
.aspectRatio(contentMode: .fit)
|
|
||||||
.padding(.top, Theme.safeAreaInsets?.top)
|
|
||||||
.padding(.bottom, Theme.safeAreaInsets?.bottom)
|
|
||||||
}
|
|
||||||
.modifier(SwipeToDismissModifier(minDistance: 50, onDismiss: {
|
|
||||||
presentationMode.wrappedValue.dismiss()
|
|
||||||
}))
|
|
||||||
.ignoresSafeArea()
|
|
||||||
.tag(index)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.ignoresSafeArea()
|
|
||||||
.tabViewStyle(PageTabViewStyle(indexDisplayMode: .never))
|
|
||||||
.gesture(TapGesture(count: 2).onEnded {
|
|
||||||
// Prevents menu from hiding on double tap
|
|
||||||
})
|
|
||||||
.gesture(TapGesture(count: 1).onEnded {
|
|
||||||
showMenu.toggle()
|
|
||||||
})
|
|
||||||
.overlay(
|
|
||||||
VStack {
|
|
||||||
if showMenu {
|
|
||||||
navBarView
|
|
||||||
Spacer()
|
|
||||||
|
|
||||||
if (urls.count > 1) {
|
|
||||||
tabViewIndicator
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.animation(.easeInOut, value: showMenu)
|
|
||||||
.padding(.bottom, Theme.safeAreaInsets?.bottom)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct ImageCarousel: View {
|
struct ImageCarousel: View {
|
||||||
var urls: [URL]
|
var urls: [URL]
|
||||||
|
|||||||
51
damus/Views/Images/ImageContainerView.swift
Normal file
51
damus/Views/Images/ImageContainerView.swift
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
//
|
||||||
|
// CarouselImageContainerView.swift
|
||||||
|
// damus
|
||||||
|
//
|
||||||
|
// Created by William Casarin on 2023-03-23.
|
||||||
|
//
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
import Kingfisher
|
||||||
|
|
||||||
|
|
||||||
|
// lots of overlap between this and ImageContainerView
|
||||||
|
struct ImageContainerView: View {
|
||||||
|
|
||||||
|
let url: URL?
|
||||||
|
|
||||||
|
@State private var image: UIImage?
|
||||||
|
@State private var showShareSheet = false
|
||||||
|
|
||||||
|
private struct ImageHandler: ImageModifier {
|
||||||
|
@Binding var handler: UIImage?
|
||||||
|
|
||||||
|
func modify(_ image: UIImage) -> UIImage {
|
||||||
|
handler = image
|
||||||
|
return image
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
|
||||||
|
KFAnimatedImage(url)
|
||||||
|
.imageContext(.note)
|
||||||
|
.configure { view in
|
||||||
|
view.framePreloadCount = 3
|
||||||
|
}
|
||||||
|
.imageModifier(ImageHandler(handler: $image))
|
||||||
|
.clipped()
|
||||||
|
.modifier(ImageContextMenuModifier(url: url, image: image, showShareSheet: $showShareSheet))
|
||||||
|
.sheet(isPresented: $showShareSheet) {
|
||||||
|
ShareSheet(activityItems: [url])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let test_image_url = URL(string: "https://jb55.com/red-me.jpg")!
|
||||||
|
|
||||||
|
struct ImageContainerView_Previews: PreviewProvider {
|
||||||
|
static var previews: some View {
|
||||||
|
ImageContainerView(url: test_image_url)
|
||||||
|
}
|
||||||
|
}
|
||||||
43
damus/Views/Images/ImageContextMenuModifier.swift
Normal file
43
damus/Views/Images/ImageContextMenuModifier.swift
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
//
|
||||||
|
// ImageContextMenuModifier.swift
|
||||||
|
// damus
|
||||||
|
//
|
||||||
|
// Created by William Casarin on 2023-03-23.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import SwiftUI
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
struct ImageContextMenuModifier: ViewModifier {
|
||||||
|
let url: URL?
|
||||||
|
let image: UIImage?
|
||||||
|
@Binding var showShareSheet: Bool
|
||||||
|
|
||||||
|
func body(content: Content) -> some View {
|
||||||
|
return content.contextMenu {
|
||||||
|
Button {
|
||||||
|
UIPasteboard.general.url = url
|
||||||
|
} label: {
|
||||||
|
Label(NSLocalizedString("Copy Image URL", comment: "Context menu option to copy the URL of an image into clipboard."), systemImage: "doc.on.doc")
|
||||||
|
}
|
||||||
|
if let someImage = image {
|
||||||
|
Button {
|
||||||
|
UIPasteboard.general.image = someImage
|
||||||
|
} label: {
|
||||||
|
Label(NSLocalizedString("Copy Image", comment: "Context menu option to copy an image into clipboard."), systemImage: "photo.on.rectangle")
|
||||||
|
}
|
||||||
|
Button {
|
||||||
|
UIImageWriteToSavedPhotosAlbum(someImage, nil, nil, nil)
|
||||||
|
} label: {
|
||||||
|
Label(NSLocalizedString("Save Image", comment: "Context menu option to save an image."), systemImage: "square.and.arrow.down")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Button {
|
||||||
|
showShareSheet = true
|
||||||
|
} label: {
|
||||||
|
Label(NSLocalizedString("Share", comment: "Button to share an image."), systemImage: "square.and.arrow.up")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
102
damus/Views/Images/ImageView.swift
Normal file
102
damus/Views/Images/ImageView.swift
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
//
|
||||||
|
// ImageView.swift
|
||||||
|
// damus
|
||||||
|
//
|
||||||
|
// Created by William Casarin on 2023-03-23.
|
||||||
|
//
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
struct ImageView: View {
|
||||||
|
|
||||||
|
let urls: [URL?]
|
||||||
|
|
||||||
|
@Environment(\.presentationMode) var presentationMode
|
||||||
|
|
||||||
|
@State private var selectedIndex = 0
|
||||||
|
@State var showMenu = true
|
||||||
|
|
||||||
|
var navBarView: some View {
|
||||||
|
VStack {
|
||||||
|
HStack {
|
||||||
|
/*
|
||||||
|
Text(urls[selectedIndex]?.lastPathComponent ?? "")
|
||||||
|
.bold()
|
||||||
|
*/
|
||||||
|
|
||||||
|
Spacer()
|
||||||
|
|
||||||
|
Button(action: {
|
||||||
|
presentationMode.wrappedValue.dismiss()
|
||||||
|
}, label: {
|
||||||
|
Image(systemName: "xmark")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
.padding()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var tabViewIndicator: some View {
|
||||||
|
HStack(spacing: 10) {
|
||||||
|
ForEach(urls.indices, id: \.self) { index in
|
||||||
|
Capsule()
|
||||||
|
.fill(index == selectedIndex ? Color(UIColor.label) : Color.secondary)
|
||||||
|
.frame(width: 7, height: 7)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.padding()
|
||||||
|
.background(.regularMaterial)
|
||||||
|
.clipShape(Capsule())
|
||||||
|
}
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
ZStack {
|
||||||
|
Color(.systemBackground)
|
||||||
|
.ignoresSafeArea()
|
||||||
|
|
||||||
|
TabView(selection: $selectedIndex) {
|
||||||
|
ForEach(urls.indices, id: \.self) { index in
|
||||||
|
ZoomableScrollView {
|
||||||
|
ImageContainerView(url: urls[index])
|
||||||
|
.aspectRatio(contentMode: .fit)
|
||||||
|
.padding(.top, Theme.safeAreaInsets?.top)
|
||||||
|
.padding(.bottom, Theme.safeAreaInsets?.bottom)
|
||||||
|
}
|
||||||
|
.modifier(SwipeToDismissModifier(minDistance: 50, onDismiss: {
|
||||||
|
presentationMode.wrappedValue.dismiss()
|
||||||
|
}))
|
||||||
|
.ignoresSafeArea()
|
||||||
|
.tag(index)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.ignoresSafeArea()
|
||||||
|
.tabViewStyle(PageTabViewStyle(indexDisplayMode: .never))
|
||||||
|
.gesture(TapGesture(count: 2).onEnded {
|
||||||
|
// Prevents menu from hiding on double tap
|
||||||
|
})
|
||||||
|
.gesture(TapGesture(count: 1).onEnded {
|
||||||
|
showMenu.toggle()
|
||||||
|
})
|
||||||
|
.overlay(
|
||||||
|
VStack {
|
||||||
|
if showMenu {
|
||||||
|
navBarView
|
||||||
|
Spacer()
|
||||||
|
|
||||||
|
if (urls.count > 1) {
|
||||||
|
tabViewIndicator
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.animation(.easeInOut, value: showMenu)
|
||||||
|
.padding(.bottom, Theme.safeAreaInsets?.bottom)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ImageView_Previews: PreviewProvider {
|
||||||
|
static var previews: some View {
|
||||||
|
ImageView(urls: [URL(string: "https://jb55.com/red-me.jpg")])
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7,7 +7,7 @@
|
|||||||
import SwiftUI
|
import SwiftUI
|
||||||
import Kingfisher
|
import Kingfisher
|
||||||
|
|
||||||
private struct ImageContainerView: View {
|
struct ProfileImageContainerView: View {
|
||||||
|
|
||||||
let url: URL?
|
let url: URL?
|
||||||
|
|
||||||
@@ -68,7 +68,7 @@ struct ProfileZoomView: View {
|
|||||||
.ignoresSafeArea()
|
.ignoresSafeArea()
|
||||||
|
|
||||||
ZoomableScrollView {
|
ZoomableScrollView {
|
||||||
ImageContainerView(url: get_profile_url(picture: nil, pubkey: pubkey, profiles: profiles))
|
ProfileImageContainerView(url: get_profile_url(picture: nil, pubkey: pubkey, profiles: profiles))
|
||||||
.aspectRatio(contentMode: .fit)
|
.aspectRatio(contentMode: .fit)
|
||||||
.padding(.top, Theme.safeAreaInsets?.top)
|
.padding(.top, Theme.safeAreaInsets?.top)
|
||||||
.padding(.bottom, Theme.safeAreaInsets?.bottom)
|
.padding(.bottom, Theme.safeAreaInsets?.bottom)
|
||||||
|
|||||||
Reference in New Issue
Block a user