Skip to content

Commit 8eb1de0

Browse files
committed
Introduce MediaUI
1 parent a3321b1 commit 8eb1de0

File tree

6 files changed

+97
-36
lines changed

6 files changed

+97
-36
lines changed

App/IcySkyApp.swift

+7
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import ATProtoKit
22
import Auth
33
import AuthUI
44
import DesignSystem
5+
import MediaUI
56
import Models
67
import Network
78
import Router
@@ -45,6 +46,12 @@ struct IcySkyApp: App {
4546
case .auth:
4647
AuthView()
4748
.environment(auth)
49+
case let .fullScreenMedia(images, preloadedImage, namespace):
50+
FullScreenMediaView(
51+
images: images,
52+
preloadedImage: preloadedImage,
53+
namespace: namespace
54+
)
4855
}
4956
}
5057
)

IcySky.xcodeproj/project.pbxproj

+7
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
9F1552EF2CEF35C000DD9F6E /* DesignSystem in Frameworks */ = {isa = PBXBuildFile; productRef = 9F1552EE2CEF35C000DD9F6E /* DesignSystem */; };
1515
9F1552F12CEF35C000DD9F6E /* FeedUI in Frameworks */ = {isa = PBXBuildFile; productRef = 9F1552F02CEF35C000DD9F6E /* FeedUI */; };
1616
9F1552F32CEF35C000DD9F6E /* PostUI in Frameworks */ = {isa = PBXBuildFile; productRef = 9F1552F22CEF35C000DD9F6E /* PostUI */; };
17+
9F21F9B72CFDC288003B9250 /* MediaUI in Frameworks */ = {isa = PBXBuildFile; productRef = 9F21F9B62CFDC288003B9250 /* MediaUI */; };
1718
9F5892032CEB2E2D00798943 /* Network in Frameworks */ = {isa = PBXBuildFile; productRef = 9F5892022CEB2E2D00798943 /* Network */; };
1819
9F5892972CEC703B00798943 /* DesignSystem in Frameworks */ = {isa = PBXBuildFile; productRef = 9F5892962CEC703B00798943 /* DesignSystem */; };
1920
9F58939D2CEDF04200798943 /* Auth in Frameworks */ = {isa = PBXBuildFile; productRef = 9F58939C2CEDF04200798943 /* Auth */; };
@@ -56,6 +57,7 @@
5657
9FA557082CF61EE4008A62B4 /* NotificationsUI in Frameworks */,
5758
9F5892032CEB2E2D00798943 /* Network in Frameworks */,
5859
9F1552E82CEF35B200DD9F6E /* Models in Frameworks */,
60+
9F21F9B72CFDC288003B9250 /* MediaUI in Frameworks */,
5961
9F58939D2CEDF04200798943 /* Auth in Frameworks */,
6062
9FD63DE02CEF1F5B00243D9A /* PostUI in Frameworks */,
6163
9F5892972CEC703B00798943 /* DesignSystem in Frameworks */,
@@ -132,6 +134,7 @@
132134
9F1552F22CEF35C000DD9F6E /* PostUI */,
133135
9FAEE3B92CF09BB400F86CDC /* SettingsUI */,
134136
9FA557072CF61EE4008A62B4 /* NotificationsUI */,
137+
9F21F9B62CFDC288003B9250 /* MediaUI */,
135138
);
136139
productName = IcySky;
137140
productReference = 9F2142232CE9DAA1004167D7 /* IcySky.app */;
@@ -450,6 +453,10 @@
450453
isa = XCSwiftPackageProductDependency;
451454
productName = PostUI;
452455
};
456+
9F21F9B62CFDC288003B9250 /* MediaUI */ = {
457+
isa = XCSwiftPackageProductDependency;
458+
productName = MediaUI;
459+
};
453460
9F5892022CEB2E2D00798943 /* Network */ = {
454461
isa = XCSwiftPackageProductDependency;
455462
productName = Network;

Packages/Features/Package.swift

+8
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ let package = Package(
2222
.library(name: "SettingsUI", targets: ["SettingsUI"]),
2323
.library(name: "NotificationsUI", targets: ["NotificationsUI"]),
2424
.library(name: "DesignSystem", targets: ["DesignSystem"]),
25+
.library(name: "MediaUI", targets: ["MediaUI"]),
2526
],
2627
dependencies: [
2728
.package(name: "Model", path: "../Model"),
@@ -57,6 +58,13 @@ let package = Package(
5758
name: "SettingsUI",
5859
dependencies: baseDeps
5960
),
61+
.target(
62+
name: "MediaUI",
63+
dependencies: baseDeps + [
64+
.product(name: "Nuke", package: "Nuke"),
65+
.product(name: "NukeUI", package: "Nuke"),
66+
]
67+
),
6068
.target(
6169
name: "NotificationsUI",
6270
dependencies: baseDeps + ["PostUI"]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import Foundation
2+
import Nuke
3+
import NukeUI
4+
import SwiftUI
5+
6+
public struct FullScreenMediaView: View {
7+
let images: [URL]
8+
let preloadedImage: URL?
9+
let namespace: Namespace.ID
10+
11+
@State private var isFirstImageLoaded: Bool = false
12+
13+
public init(images: [URL], preloadedImage: URL?, namespace: Namespace.ID) {
14+
self.images = images
15+
self.preloadedImage = preloadedImage
16+
self.namespace = namespace
17+
}
18+
19+
var firstImageURL: URL? {
20+
if let preloadedImage, !isFirstImageLoaded {
21+
return preloadedImage
22+
}
23+
return images.first
24+
}
25+
26+
public var body: some View {
27+
ScrollView(.horizontal) {
28+
LazyHStack {
29+
ForEach(images.indices, id: \.self) { index in
30+
LazyImage(url: index == 0 ? firstImageURL : images[index]) { state in
31+
if let image = state.image {
32+
image
33+
.resizable()
34+
.scaledToFill()
35+
.aspectRatio(contentMode: .fit)
36+
} else {
37+
RoundedRectangle(cornerRadius: 8)
38+
.fill(.thinMaterial)
39+
}
40+
}
41+
.containerRelativeFrame([.horizontal, .vertical])
42+
}
43+
}
44+
.scrollTargetLayout()
45+
}
46+
.scrollContentBackground(.hidden)
47+
.scrollTargetBehavior(.viewAligned)
48+
.navigationTransition(.zoom(sourceID: images[0], in: namespace))
49+
.task {
50+
do {
51+
let data = try await Nuke.ImagePipeline.shared.data(for: .init(url: images.first))
52+
if !data.0.isEmpty {
53+
self.isFirstImageLoaded = true
54+
}
55+
} catch {}
56+
}
57+
}
58+
}

Packages/Features/Sources/PostUI/Row/PostRowImagesView.swift

+12-35
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,19 @@ import ATProtoKit
22
import DesignSystem
33
import Models
44
import NukeUI
5+
import Router
56
import SwiftUI
67

78
struct PostRowImagesView: View {
8-
@Namespace private var namespace
99
@Environment(\.isQuote) var isQuote
10+
@Environment(Router.self) var router
11+
12+
@Namespace private var namespace
1013

1114
let quoteMaxSize: CGFloat = 100
12-
1315
let images: AppBskyLexicon.Embed.ImagesDefinition.View
14-
@State private var firstImageSize: CGSize?
1516

17+
@State private var firstImageSize: CGSize?
1618
@State private var isMediaExpanded: Bool = false
1719

1820
var body: some View {
@@ -26,12 +28,13 @@ struct PostRowImagesView: View {
2628
)
2729
}
2830
}
29-
.padding(.bottom, images.images.count > 1 && !isQuote ? 15 : 0)
31+
.padding(.bottom, images.images.count > 1 && !isQuote ? CGFloat(images.images.count) * 7 : 0)
3032
.onTapGesture {
31-
isMediaExpanded.toggle()
32-
}
33-
.fullScreenCover(isPresented: $isMediaExpanded) {
34-
expandedView
33+
router.presentedSheet = .fullScreenMedia(
34+
images: images.images.map(\.fullSizeImageURL),
35+
preloadedImage: images.images.first?.thumbnailImageURL,
36+
namespace: namespace
37+
)
3538
}
3639
}
3740

@@ -51,15 +54,14 @@ struct PostRowImagesView: View {
5154
image
5255
.resizable()
5356
.scaledToFill()
54-
.frame(width: finalWidth, height: finalHeight)
5557
.aspectRatio(contentMode: .fit)
5658
} else {
5759
RoundedRectangle(cornerRadius: 8)
5860
.fill(.thinMaterial)
59-
.frame(width: finalWidth, height: finalHeight)
6061
}
6162
}
6263
.processors([.resize(size: .init(width: finalWidth, height: finalHeight))])
64+
.frame(width: finalWidth, height: finalHeight)
6365
.matchedTransitionSource(id: image.fullSizeImageURL, in: namespace)
6466
.glowingRoundedRectangle()
6567
.onAppear {
@@ -72,29 +74,4 @@ struct PostRowImagesView: View {
7274
isQuote ? 1 : (firstImageSize?.width ?? width) / (firstImageSize?.height ?? height),
7375
contentMode: .fit)
7476
}
75-
76-
private var expandedView: some View {
77-
ScrollView(.horizontal) {
78-
LazyHStack {
79-
ForEach(images.images, id: \.thumbnailImageURL) { image in
80-
LazyImage(url: image.fullSizeImageURL) { state in
81-
if let image = state.image {
82-
image
83-
.resizable()
84-
.scaledToFill()
85-
.aspectRatio(contentMode: .fit)
86-
} else {
87-
RoundedRectangle(cornerRadius: 8)
88-
.fill(.thinMaterial)
89-
}
90-
}
91-
.containerRelativeFrame([.horizontal, .vertical])
92-
}
93-
}
94-
.scrollTargetLayout()
95-
}
96-
.scrollContentBackground(.hidden)
97-
.scrollTargetBehavior(.viewAligned)
98-
.navigationTransition(.zoom(sourceID: images.images[0].fullSizeImageURL, in: namespace))
99-
}
10077
}

Packages/Model/Sources/Router/SheetDestination.swift

+5-1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@ import SwiftUI
33

44
public enum SheetDestination: Hashable, Identifiable {
55
public var id: Int { self.hashValue }
6-
6+
77
case auth
8+
case fullScreenMedia(
9+
images: [URL],
10+
preloadedImage: URL?,
11+
namespace: Namespace.ID)
812
}

0 commit comments

Comments
 (0)