Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

UI/PhotoPreview #330

Open
wants to merge 29 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
fc389c9
Model.
nagavcindriqim Aug 13, 2024
44e7cf0
Introduce PhotoPReviewItemView.
nagavcindriqim Aug 13, 2024
1f7fb48
Introduce PhotoPreview.
nagavcindriqim Aug 13, 2024
3af133d
Swipe down to dismiss.
nagavcindriqim Aug 14, 2024
92e211a
Modifier.
nagavcindriqim Aug 14, 2024
8d605df
Ensure new index within bounds.
nagavcindriqim Sep 9, 2024
39e6eb8
Limit zoom in.
nagavcindriqim Sep 9, 2024
71c86a6
Animate vertical out of bounds offset.
nagavcindriqim Sep 9, 2024
1cc132a
Scale to fit remote image.
nagavcindriqim Sep 9, 2024
25619d8
Configuration.
nagavcindriqim Sep 9, 2024
fe907b7
Dismiss button.
nagavcindriqim Sep 9, 2024
fc21ca0
Background opacity.
nagavcindriqim Sep 9, 2024
ce0b821
README.
nagavcindriqim Sep 9, 2024
910fe57
Address PR comments.
nagavcindriqim Sep 10, 2024
cd02de4
Support from iOS 15.
nagavcindriqim Sep 19, 2024
6d271b1
Storybook.
nagavcindriqim Sep 19, 2024
4d50139
Introduce PhotoPreviewImageLoader.
nagavcindriqim Sep 23, 2024
764778f
Load remote images.
nagavcindriqim Sep 23, 2024
513bced
Drawing group.
nagavcindriqim Sep 23, 2024
abee235
Show loader.
nagavcindriqim Sep 23, 2024
6be41b2
Drag outside image bounds.
nagavcindriqim Sep 23, 2024
811c909
Disable zooming out.
nagavcindriqim Sep 23, 2024
647eb3f
Animate loading.
nagavcindriqim Sep 24, 2024
c671cf2
Files under PhotoPreview namespace.
nagavcindriqim Sep 24, 2024
448bd37
Clear presentation background.
nagavcindriqim Sep 24, 2024
e249957
Hide close button when dismissing.
nagavcindriqim Sep 24, 2024
fd2c590
File naming.
nagavcindriqim Sep 24, 2024
8fcdb4a
Fix drag gesture.
nagavcindriqim Sep 24, 2024
960df31
Switch to TabView mid work commit.
nagavcindriqim Sep 26, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 9 additions & 5 deletions Demo/Storybook/Storybook.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,10 @@
objects = {

/* Begin PBXBuildFile section */
2D6768C12C9C45CD007CC431 /* PovioKitSwiftUI in Frameworks */ = {isa = PBXBuildFile; productRef = 2D6768C02C9C45CD007CC431 /* PovioKitSwiftUI */; };
2D6768C32C9C4606007CC431 /* PhotoPreviewComponentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D6768C22C9C4606007CC431 /* PhotoPreviewComponentView.swift */; };
990C176E297E98C2005EC7B7 /* PaddingLabelComponentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 990C176C297E98C2005EC7B7 /* PaddingLabelComponentView.swift */; };
990C176F297E98C2005EC7B7 /* ActionButtonComponentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 990C176D297E98C2005EC7B7 /* ActionButtonComponentView.swift */; };
990C1779297E9D96005EC7B7 /* PovioKitUI in Frameworks */ = {isa = PBXBuildFile; productRef = 990C1778297E9D96005EC7B7 /* PovioKitUI */; };
998E6EF7297E82F700D33909 /* StorybookApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 998E6EF6297E82F700D33909 /* StorybookApp.swift */; };
998E6EF9297E82F700D33909 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 998E6EF8297E82F700D33909 /* ContentView.swift */; };
998E6EFB297E82F800D33909 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 998E6EFA297E82F800D33909 /* Assets.xcassets */; };
Expand All @@ -18,6 +19,7 @@
/* End PBXBuildFile section */

/* Begin PBXFileReference section */
2D6768C22C9C4606007CC431 /* PhotoPreviewComponentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhotoPreviewComponentView.swift; sourceTree = "<group>"; };
990C176C297E98C2005EC7B7 /* PaddingLabelComponentView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PaddingLabelComponentView.swift; sourceTree = "<group>"; };
990C176D297E98C2005EC7B7 /* ActionButtonComponentView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ActionButtonComponentView.swift; sourceTree = "<group>"; };
990C1776297E9D54005EC7B7 /* PovioKit */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = PovioKit; path = ../..; sourceTree = "<group>"; };
Expand All @@ -35,7 +37,7 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
990C1779297E9D96005EC7B7 /* PovioKitUI in Frameworks */,
2D6768C12C9C45CD007CC431 /* PovioKitSwiftUI in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down Expand Up @@ -102,6 +104,7 @@
children = (
990C176D297E98C2005EC7B7 /* ActionButtonComponentView.swift */,
990C176C297E98C2005EC7B7 /* PaddingLabelComponentView.swift */,
2D6768C22C9C4606007CC431 /* PhotoPreviewComponentView.swift */,
);
path = Components;
sourceTree = "<group>";
Expand All @@ -123,7 +126,7 @@
);
name = Storybook;
packageProductDependencies = (
990C1778297E9D96005EC7B7 /* PovioKitUI */,
2D6768C02C9C45CD007CC431 /* PovioKitSwiftUI */,
);
productName = Storybook;
productReference = 998E6EF3297E82F700D33909 /* Storybook.app */;
Expand Down Expand Up @@ -181,6 +184,7 @@
files = (
998E6EF9297E82F700D33909 /* ContentView.swift in Sources */,
998E6F08297E8AE000D33909 /* Component.swift in Sources */,
2D6768C32C9C4606007CC431 /* PhotoPreviewComponentView.swift in Sources */,
998E6EF7297E82F700D33909 /* StorybookApp.swift in Sources */,
990C176E297E98C2005EC7B7 /* PaddingLabelComponentView.swift in Sources */,
990C176F297E98C2005EC7B7 /* ActionButtonComponentView.swift in Sources */,
Expand Down Expand Up @@ -399,9 +403,9 @@
/* End XCConfigurationList section */

/* Begin XCSwiftPackageProductDependency section */
990C1778297E9D96005EC7B7 /* PovioKitUI */ = {
2D6768C02C9C45CD007CC431 /* PovioKitSwiftUI */ = {
isa = XCSwiftPackageProductDependency;
productName = PovioKitUI;
productName = PovioKitSwiftUI;
};
/* End XCSwiftPackageProductDependency section */
};
Expand Down
3 changes: 3 additions & 0 deletions Demo/Storybook/Storybook/Component.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import Foundation
enum Component: CaseIterable {
case actionButton
case paddingLabel
case photoPreview
}

extension Component {
Expand All @@ -19,6 +20,8 @@ extension Component {
return "Action Button"
case .paddingLabel:
return "Padding Label"
case.photoPreview:
return "Photo Preview"
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
//

import SwiftUI
import PovioKitUI
import PovioKitSwiftUI

struct ActionButtonComponentView: View {
var body: some View {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
//
// PhotoPreviewComponentView.swift
// Storybook
//
// Created by Ndriqim Nagavci on 19/09/2024.
//

import PovioKitSwiftUI
import SwiftUI

struct PhotoPreviewComponentView: View {
@State private var showPhotoPreview = false
private var items: [PhotoPreview.Item] = [
.init(image: Image("PovioKit")),
.init(url: .init(string: "https://raw.githubusercontent.com/poviolabs/PovioKit/develop/Resources/PovioKit.png"))
]

var body: some View {
Button {
showPhotoPreview.toggle()
} label: {
Text("Show Photo Preview")
}
.photoPreview(
present: $showPhotoPreview,
items: items
)
}
}
4 changes: 4 additions & 0 deletions Demo/Storybook/Storybook/ContentView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,10 @@ struct ContentView: View {
PaddingLabelComponentView()
.navigationTitle(component.name)
.navigationBarTitleDisplayMode(.large)
case .photoPreview:
PhotoPreviewComponentView()
.navigationTitle(component.name)
.navigationBarTitleDisplayMode(.large)
}
}
}
Expand Down
28 changes: 28 additions & 0 deletions Resources/UI/SwiftUI/PhotoPreview/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# PhotoPreview

SwiftUI view intended to present an image or set of images in full-screen mode.

## Usage

### Example: Implementation in SwiftUI
```swift
struct ContentView: View {
@State private var showPhotoPreview = false
private var items: [PhotoPreviewItem] = [
.init(image: Image("image1")),
.init(url: .init(string: "remote-url"))
]

var body: some View {
Button {
showPhotoPreview.toggle()
} label: {
Text("Show images")
}
.photoPreview(
present: $showPhotoPreview,
items: items
)
}
}
```
1 change: 1 addition & 0 deletions Resources/UI/SwiftUI/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ A package including components to help you out developing for SwiftUI framework.
| :--- | :--- |
| [ActionButton](ActionButton) | Generic and customizable CTA button |
| [PhotoPickerView](/Sources/UI/SwiftUI/Views/PhotoPickerView/PhotoPickerView.swift) | Photo and Camera picker view used in combination with `PhotoPickerModifier` |
| [PhotoPreview](/Sources/UI/SwiftUI/Views/PhotoPreview/PhotoPreview.swift) | Present an image or set of images in full-screen mode. |
| [ProfileImageView](ProfileImageView) | Generic and customizable profile image view component |
| [ProgressStyle](/Sources/UI/SwiftUI/Views/ProgressStyle/ProgressStyle.swift) | Customizable ProgressViewStyle |
| [RemoteImage](/Sources/UI/SwiftUI/Views/RemoteImage/RemoteImage.swift) | Fetching remote images using Kingfisher |
Expand Down
66 changes: 66 additions & 0 deletions Sources/UI/SwiftUI/View Modifiers/PhotoPreviewModifier.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
//
// PhotoPreviewModifier.swift
// PovioKit
//
// Created by Ndriqim Nagavci on 14/08/2024.
// Copyright © 2024 Povio Inc. All rights reserved.
//

import SwiftUI

@available(iOS 16.0, *)
struct PhotoPreviewModifier: ViewModifier {
@Binding var presented: Bool
let items: [PhotoPreview.Item]
let configuration: PhotoPreview.Configuration

public func body(content: Content) -> some View {
content
.fullScreenCover(isPresented: $presented) {
PhotoPreview(
items: items,
configuration: configuration,
presented: $presented
)
.presentationBackgroundIfAvailable(.black.opacity(0.01))
}
}
}

@available(iOS 16.0, *)
public extension View {
func photoPreview(
present: Binding<Bool>,
items: [PhotoPreview.Item],
configuration: PhotoPreview.Configuration = .default
) -> some View {
modifier(PhotoPreviewModifier(
presented: present,
items: items,
configuration: configuration
))
}
}

@available(iOS 16.0, *)
extension PhotoPreviewModifier {
struct PresentationBackgroundModifier: ViewModifier {
let color: Color

func body(content: Content) -> some View {
if #available(iOS 16.4, *) {
content
.presentationBackground(color)
} else {
content
}
}
}
}

@available(iOS 16.0, *)
extension View {
func presentationBackgroundIfAvailable(_ color: Color) -> some View {
modifier(PhotoPreviewModifier.PresentationBackgroundModifier(color: color))
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
//
// PhotoPreview+Configuration.swift
// PovioKit
//
// Created by Ndriqim Nagavci on 09/09/2024.
// Copyright © 2024 Povio Inc. All rights reserved.
//

import SwiftUI

@available(iOS 16.0, *)
public extension PhotoPreview {
struct Configuration {
public var backgroundColor: Color
public var showDismissButton: Bool
public var velocityThreshold: CGSize
public var offsetThreshold: CGFloat

public init(
backgroundColor: Color,
showDismissButton: Bool,
velocityThreshold: CGSize,
offsetThreshold: CGFloat
) {
self.backgroundColor = backgroundColor
self.showDismissButton = showDismissButton
self.velocityThreshold = velocityThreshold
self.offsetThreshold = offsetThreshold
}
}
}

@available(iOS 16.0, *)
public extension PhotoPreview.Configuration {
static var `default`: PhotoPreview.Configuration {
.init(
backgroundColor: .black,
showDismissButton: true,
velocityThreshold: .init(width: 200, height: 1000),
offsetThreshold: 80
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
//
// PhotoPreview+ImageLoader.swift
// PovioKit
//
// Created by Ndriqim Nagavci on 23/09/2024.
// Copyright © 2024 Povio Inc. All rights reserved.
//

import SwiftUI

@available(iOS 16.0, *)
extension PhotoPreview {
@MainActor
class ImageLoader: ObservableObject {
@Published private(set) var state: State = .initial

func loadImage(from url: URL?) async {
guard let url else { return }
state = .loading
let request = URLRequest(url: url, cachePolicy: .returnCacheDataElseLoad)
do {
let (data, _) = try await URLSession.shared.data(for: request)
await getImageFrom(data: data)
} catch {
state = .failed
}
}

func getImageFrom(data: Data) async {
guard let loadedImage = UIImage(data: data) else {
state = .failed
return
}
withAnimation(.linear(duration: 1)) {
state = .loaded(loadedImage)
}
}
}
}

// MARK: - State
@available(iOS 16.0, *)
extension PhotoPreview.ImageLoader {
enum State: Equatable {
case initial
case loading
case loaded(UIImage)
case failed
}
}
28 changes: 28 additions & 0 deletions Sources/UI/SwiftUI/Views/PhotoPreview/PhotoPreview+Item.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
//
// PhotoPreview+Item.swift
// PovioKit
//
// Created by Ndriqim Nagavci on 12/08/2024.
// Copyright © 2024 Povio Inc. All rights reserved.
//

import SwiftUI

@available(iOS 16.0, *)
public extension PhotoPreview {
struct Item {
public var image: Image?
public var url: URL?
public var placeholder: Image?

public init(
image: Image? = nil,
url: URL? = nil,
placeholder: Image? = nil
) {
self.image = image
self.url = url
self.placeholder = placeholder
}
}
}
Loading
Loading