Skip to content

Commit

Permalink
Merge pull request #33 from payan-app/image-viewer
Browse files Browse the repository at this point in the history
refactor: update image viewer for multiple images
  • Loading branch information
juandahurt authored Aug 28, 2022
2 parents 8f63e50 + 9bac3e4 commit 729ac91
Show file tree
Hide file tree
Showing 5 changed files with 129 additions and 99 deletions.
12 changes: 10 additions & 2 deletions PuraceDemo/PuraceDemo/Examples/Complex/ImageViewerExample.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,23 @@ 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") {
isVisible.toggle()
}
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)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"images" : [
{
"filename" : "arrow_back.svg",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
182 changes: 91 additions & 91 deletions Sources/Purace/Views/Complex/Image Viewer/PuraceImageViewer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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<Bool>) {
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<Bool>, 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..<numberOfImages) { index in
PuraceImageView(url: urls[index])
.scaledToFit()
.tag(index)
.scaleEffect(scale)
.offset(y: dragOffset)
.gesture(
MagnificationGesture()
.updating($scale) { value, scale, _ in
if value >= 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
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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<Bool>) {
self.url = url
public init(urls: [URL?], isVisible: Binding<Bool>, 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<Bool>) -> some View {
modifier(PuraceImageViewerModifier(url: url, isVisible: isVisible))
func imageViewer(urls: [URL?], isVisible: Binding<Bool>, selectedIndex: Int = 0) -> some View {
modifier(PuraceImageViewerModifier(urls: urls, isVisible: isVisible, selectedIndex: selectedIndex))
}
}

0 comments on commit 729ac91

Please sign in to comment.