diff --git a/PuraceDemo/PuraceDemo/Examples/Complex/ImageViewerExample.swift b/PuraceDemo/PuraceDemo/Examples/Complex/ImageViewerExample.swift index ce44bf4..65d9b82 100644 --- a/PuraceDemo/PuraceDemo/Examples/Complex/ImageViewerExample.swift +++ b/PuraceDemo/PuraceDemo/Examples/Complex/ImageViewerExample.swift @@ -12,6 +12,12 @@ import Purace struct ImageViewerExample: View { @State var isVisible = false + let urls: [URL?] = [ + URL(string: "https://payan-dev-images.s3.us-east-2.amazonaws.com/santo-domingo.jpg"), + URL(string: "https://payan-dev-images.s3.us-east-2.amazonaws.com/santo-domingo.jpg"), + URL(string: "https://payan-places.s3.us-east-2.amazonaws.com/001/001.jpg") + ] + var body: some View { VStack { PuraceButtonView("Mostrar imagen") { @@ -19,8 +25,10 @@ struct ImageViewerExample: View { } Spacer() }.imageViewer( - url: URL(string: "https://payan-dev-images.s3.us-east-2.amazonaws.com/santo-domingo.jpg"), - isVisible: $isVisible + urls: urls, + isVisible: $isVisible, + selectedIndex: 1 ) + .navigationBarHidden(true) } } diff --git a/Sources/Purace/Resources/Media.xcassets/arrow_back.imageset/Contents.json b/Sources/Purace/Resources/Media.xcassets/arrow_back.imageset/Contents.json new file mode 100644 index 0000000..7c6179f --- /dev/null +++ b/Sources/Purace/Resources/Media.xcassets/arrow_back.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "arrow_back.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Sources/Purace/Resources/Media.xcassets/arrow_back.imageset/arrow_back.svg b/Sources/Purace/Resources/Media.xcassets/arrow_back.imageset/arrow_back.svg new file mode 100644 index 0000000..1d069ae --- /dev/null +++ b/Sources/Purace/Resources/Media.xcassets/arrow_back.imageset/arrow_back.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/Sources/Purace/Views/Complex/Image Viewer/PuraceImageViewer.swift b/Sources/Purace/Views/Complex/Image Viewer/PuraceImageViewer.swift index aacd169..6d2838a 100644 --- a/Sources/Purace/Views/Complex/Image Viewer/PuraceImageViewer.swift +++ b/Sources/Purace/Views/Complex/Image Viewer/PuraceImageViewer.swift @@ -11,117 +11,117 @@ import Kingfisher public struct PuraceImageViewer: View { @Binding var isVisible: Bool - let backgroundColor: Color - let url: URL? - @State var opacity: Double = 0 - @State var dragOffset: CGSize = .zero - @State var backgroundOpacity: Double = 1 - @State var hasDraggedTheImage = false - @State var dragInitialTime: Date? - + let urls: [URL?] + @State var currentIndex: Int @GestureState var scale: CGFloat = 1 + @State var dragOffset: CGFloat = .zero + @State var backgroundOpacity = 1.0 + + private let numberOfImages: Int private let maximumImageHeight = UIScreen.main.bounds.height * 0.65 - public init(url: URL?, isVisible: Binding) { - let colors: [Color] = [ - PuraceStyle.Color.X1, - PuraceStyle.Color.X2, - PuraceStyle.Color.X3, - PuraceStyle.Color.X4 - ] - backgroundColor = colors.randomElement()! - self.url = url + public init(urls: [URL?], isVisible: Binding, index: Int = 0) { + self.urls = urls self._isVisible = isVisible + self._currentIndex = .init(initialValue: index) + self.numberOfImages = urls.count } - private func differenceBeetwenInitialDragTime(and date: Date) -> Double { - guard let dragInitialTime = dragInitialTime else { - return .zero + var indicator: some View { + HStack { + if numberOfImages > 1 { + PuraceTextView("\(currentIndex + 1)/\(numberOfImages)", fontSize: 14, textColor: .white, weight: .medium) + .padding(.trailing) + } } - return date.timeIntervalSince(dragInitialTime) * 1000 } - var draggableArea: some View { - Color.black - .animation(.none) - .opacity(0.001) - .gesture( + var backButton: some View { + Image(systemName: "chevron.left") + .foregroundColor(.white) + .scaleEffect(1.2) + .padding() + .highPriorityGesture( + TapGesture() + .onEnded { + withAnimation { + isVisible = false + } + } + ) + } + + var topBar: some View { + VStack { + HStack(alignment: .center) { + backButton + + Spacer() + + indicator + }.frame(height: 60) + .opacity((abs(dragOffset) >= .zero && abs(dragOffset) <= 5) ? 1 : 0.0002) + } + } + + var viewer: some View { + VStack(spacing: 0) { + topBar + + TabView(selection: $currentIndex) { + ForEach(0..= 0.9 { + scale = value + } + } + ) + } + } + .tabViewStyle(PageTabViewStyle(indexDisplayMode: .never)) + .simultaneousGesture( DragGesture() .onChanged { value in - hasDraggedTheImage = true - if scale == 1 { - if dragInitialTime == nil { - dragInitialTime = Date() - } - let translation = value.translation.height - dragOffset.height = translation - backgroundOpacity = 1 - abs(translation) * 0.001 + dragOffset = value.translation.height + let screenHeight = UIScreen.main.bounds.height + let progress = abs(dragOffset) / screenHeight / 2 + withAnimation { + backgroundOpacity = 1 - progress } } - .onEnded { value in - if scale == 1 { - let diff = differenceBeetwenInitialDragTime(and: Date()) - if diff <= 150 || abs(dragOffset.height) >= UIScreen.main.bounds.height * 0.4 { - hideView() - } else { - withAnimation { - backgroundOpacity = 1 - } - dragOffset = .zero + .onEnded { _ in + if abs(dragOffset) < 200 { + withAnimation { + dragOffset = 0 + backgroundOpacity = 1 + } + } else { + withAnimation { + isVisible = false } - dragInitialTime = nil } } ) - .simultaneousGesture( - MagnificationGesture() - .updating($scale, body: { value, state, _ in - guard value > 0.7 else { return } - state = value - }) - ) - } - - var image: some View { - PuraceImageView(url: url) - .scaledToFit() - .offset(x: dragOffset.width, y: dragOffset.height) - .animation(hasDraggedTheImage ? .easeOut(duration: 0.35) : .none) - .scaleEffect(scale) - .frame(maxHeight: maximumImageHeight) - } - - public var body: some View { - ZStack { - backgroundColor + .onChange(of: currentIndex) { _ in + dragOffset = .zero + } + + }.background( + PuraceStyle.Color.X1 + .edgesIgnoringSafeArea(.all) .opacity(backgroundOpacity) - image - draggableArea - } - .edgesIgnoringSafeArea(.all) - .transition(.opacity.animation(.linear)) - } -} - -extension PuraceImageViewer { - private func hideImage() { - withAnimation { - if dragOffset.height > 0 { - dragOffset.height = UIScreen.main.bounds.height - } else { - dragOffset.height = -UIScreen.main.bounds.height - } - } + ) } - private func hideView() { - hideImage() - withAnimation { - backgroundOpacity = 0 - } - DispatchQueue.main.asyncAfter(deadline: .now() + 0.25) { - isVisible = false - } + public var body: some View { + viewer } } diff --git a/Sources/Purace/Views/Complex/Image Viewer/PuraceImageViewerModifier.swift b/Sources/Purace/Views/Complex/Image Viewer/PuraceImageViewerModifier.swift index 0c69e29..257d8c3 100644 --- a/Sources/Purace/Views/Complex/Image Viewer/PuraceImageViewerModifier.swift +++ b/Sources/Purace/Views/Complex/Image Viewer/PuraceImageViewerModifier.swift @@ -9,26 +9,28 @@ import Foundation import SwiftUI public struct PuraceImageViewerModifier: ViewModifier { - let url: URL? + let urls: [URL?] @Binding var isVisible: Bool + let selectedIndex: Int - public init(url: URL?, isVisible: Binding) { - self.url = url + public init(urls: [URL?], isVisible: Binding, selectedIndex: Int) { + self.urls = urls self._isVisible = isVisible + self.selectedIndex = selectedIndex } public func body(content: Content) -> some View { ZStack { content if isVisible { - PuraceImageViewer(url: url, isVisible: $isVisible) + PuraceImageViewer(urls: urls, isVisible: $isVisible, index: selectedIndex) } } } } public extension View { - func imageViewer(url: URL?, isVisible: Binding) -> some View { - modifier(PuraceImageViewerModifier(url: url, isVisible: isVisible)) + func imageViewer(urls: [URL?], isVisible: Binding, selectedIndex: Int = 0) -> some View { + modifier(PuraceImageViewerModifier(urls: urls, isVisible: isVisible, selectedIndex: selectedIndex)) } }