zkit-ui 2.0.3 → 2.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +56 -0
- package/android/build.gradle +100 -0
- package/android/src/main/AndroidManifest.xml +1 -0
- package/android/src/main/java/cn/fontree/zkit/ui/preview/GaleriaModule.kt +43 -0
- package/android/src/main/java/cn/fontree/zkit/ui/preview/GaleriaView.kt +305 -0
- package/android/src/main/java/cn/fontree/zkit/ui/preview/GalleryLayoutSupport.java +450 -0
- package/android/src/main/java/cn/fontree/zkit/ui/preview/GalleryMediaItem.java +36 -0
- package/android/src/main/java/cn/fontree/zkit/ui/preview/GalleryPagerAdapter.java +140 -0
- package/android/src/main/java/cn/fontree/zkit/ui/preview/GallerySharedElementGeometry.java +25 -0
- package/android/src/main/java/cn/fontree/zkit/ui/preview/GallerySharedElementNames.java +39 -0
- package/android/src/main/java/cn/fontree/zkit/ui/preview/GallerySharedElementState.java +25 -0
- package/android/src/main/java/cn/fontree/zkit/ui/preview/GallerySourceViewRegistry.java +115 -0
- package/android/src/main/java/cn/fontree/zkit/ui/preview/GalleryTransitionSnapshotView.java +60 -0
- package/android/src/main/java/cn/fontree/zkit/ui/preview/GalleryVideoPlayerView.java +338 -0
- package/android/src/main/java/cn/fontree/zkit/ui/preview/GalleryViewerOverlayView.java +636 -0
- package/android/src/main/java/cn/fontree/zkit/ui/preview/GalleryViewerPageView.java +357 -0
- package/android/src/main/java/cn/fontree/zkit/ui/preview/GalleryViewerTransitionController.java +223 -0
- package/android/src/main/res/layout/galeria_player_view.xml +9 -0
- package/dist/all.d.ts +2 -0
- package/dist/all.d.ts.map +1 -1
- package/dist/all.js +24 -22
- package/dist/ui/NativeImagePreview/NativeImagePreview.d.ts +3 -0
- package/dist/ui/NativeImagePreview/NativeImagePreview.d.ts.map +1 -0
- package/dist/ui/NativeImagePreview/NativeImagePreview.js +99 -0
- package/dist/ui/NativeImagePreview/NativeImagePreview.web.d.ts +3 -0
- package/dist/ui/NativeImagePreview/NativeImagePreview.web.d.ts.map +1 -0
- package/dist/ui/NativeImagePreview/NativeImagePreview.web.js +56 -0
- package/dist/ui/NativeImagePreview/context.d.ts +14 -0
- package/dist/ui/NativeImagePreview/context.d.ts.map +1 -0
- package/dist/ui/NativeImagePreview/context.js +38 -0
- package/dist/ui/NativeImagePreview/index.d.ts +3 -0
- package/dist/ui/NativeImagePreview/index.d.ts.map +1 -0
- package/dist/ui/NativeImagePreview/index.js +5 -0
- package/dist/ui/NativeImagePreview/types.d.ts +66 -0
- package/dist/ui/NativeImagePreview/types.d.ts.map +1 -0
- package/dist/ui/NativeImagePreview/types.js +2 -0
- package/dist/ui/NativeImagePreview/utils.d.ts +8 -0
- package/dist/ui/NativeImagePreview/utils.d.ts.map +1 -0
- package/dist/ui/NativeImagePreview/utils.js +82 -0
- package/expo-module.config.json +11 -0
- package/ios/GaleriaModule.swift +46 -0
- package/ios/GaleriaView.swift +253 -0
- package/ios/GalleryViewer/GaleriaImageLoader.swift +73 -0
- package/ios/GalleryViewer/GaleriaImageViewInteraction.swift +245 -0
- package/ios/GalleryViewer/GaleriaLayoutSupport.swift +143 -0
- package/ios/GalleryViewer/GaleriaMedia.swift +28 -0
- package/ios/GalleryViewer/GaleriaSourceViewRegistry.swift +80 -0
- package/ios/GalleryViewer/GaleriaVideoPlayerView.swift +437 -0
- package/ios/GalleryViewer/GaleriaViewerConfiguration.swift +51 -0
- package/ios/GalleryViewer/GaleriaViewerController.swift +745 -0
- package/ios/GalleryViewer/GaleriaViewerPageView.swift +623 -0
- package/ios/GalleryViewer/GaleriaViewerTransitionCoordinator.swift +312 -0
- package/package.json +21 -13
- package/src/all.ts +18 -0
- package/src/services/ImagePreviewService/README.md +19 -0
- package/src/ui/NativeImagePreview/NativeImagePreview.tsx +146 -0
- package/src/ui/NativeImagePreview/NativeImagePreview.web.tsx +37 -0
- package/src/ui/NativeImagePreview/README.md +111 -0
- package/src/ui/NativeImagePreview/context.tsx +22 -0
- package/src/ui/NativeImagePreview/index.tsx +18 -0
- package/src/ui/NativeImagePreview/types.ts +92 -0
- package/src/ui/NativeImagePreview/utils.ts +107 -0
- package/zkit-ui.podspec +13 -1
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
import ExpoModulesCore
|
|
2
|
+
import UIKit
|
|
3
|
+
|
|
4
|
+
class GaleriaView: ExpoView {
|
|
5
|
+
private var childImageView: UIImageView?
|
|
6
|
+
private weak var registeredAnchorImageView: UIImageView?
|
|
7
|
+
private var registeredGalleryId: String?
|
|
8
|
+
private var registeredIndex: Int?
|
|
9
|
+
|
|
10
|
+
deinit {
|
|
11
|
+
unregisterCurrentAnchor()
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
override func didMoveToWindow() {
|
|
15
|
+
super.didMoveToWindow()
|
|
16
|
+
if window == nil {
|
|
17
|
+
unregisterCurrentAnchor()
|
|
18
|
+
} else {
|
|
19
|
+
registerCurrentAnchorIfNeeded()
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
func getChildImageView() -> UIImageView? {
|
|
24
|
+
var reactSubviews: [UIView]? = nil
|
|
25
|
+
if RCTIsNewArchEnabled() {
|
|
26
|
+
reactSubviews = self.subviews
|
|
27
|
+
} else {
|
|
28
|
+
reactSubviews = self.reactSubviews()
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
guard let reactSubviews = reactSubviews else { return nil }
|
|
32
|
+
|
|
33
|
+
for reactSubview in reactSubviews {
|
|
34
|
+
if let iv = findImageView(in: reactSubview) {
|
|
35
|
+
childImageView = iv
|
|
36
|
+
return iv
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return nil
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
private func findImageView(in view: UIView) -> UIImageView? {
|
|
44
|
+
if let imageView = view as? UIImageView { return imageView }
|
|
45
|
+
for subview in view.subviews {
|
|
46
|
+
if let imageView = subview as? UIImageView { return imageView }
|
|
47
|
+
if let nested = findImageView(in: subview) { return nested }
|
|
48
|
+
}
|
|
49
|
+
return nil
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
#if !RCT_NEW_ARCH_ENABLED
|
|
53
|
+
override func insertReactSubview(_ subview: UIView!, at atIndex: Int) {
|
|
54
|
+
super.insertReactSubview(subview, at: atIndex)
|
|
55
|
+
setupImageView()
|
|
56
|
+
}
|
|
57
|
+
#endif
|
|
58
|
+
|
|
59
|
+
#if RCT_NEW_ARCH_ENABLED
|
|
60
|
+
// https://github.com/nandorojo/galeria/issues/19
|
|
61
|
+
// Cleanup gesture recognizers from the image view to work with fabric view recycling
|
|
62
|
+
override func unmountChildComponentView(_ childComponentView: UIView, index: Int) {
|
|
63
|
+
unregisterCurrentAnchor()
|
|
64
|
+
childImageView?.removeGaleriaViewerInteraction()
|
|
65
|
+
childImageView = nil
|
|
66
|
+
super.unmountChildComponentView(childComponentView, index: index)
|
|
67
|
+
}
|
|
68
|
+
#endif
|
|
69
|
+
|
|
70
|
+
var theme: Theme = .dark
|
|
71
|
+
var urls: [String]?
|
|
72
|
+
var items: [[String: Any]]?
|
|
73
|
+
var initialIndex: Int?
|
|
74
|
+
var galleryId: String?
|
|
75
|
+
var closeIconName: String?
|
|
76
|
+
var rightNavItemIconName: String?
|
|
77
|
+
let onPressRightNavItemIcon = EventDispatcher()
|
|
78
|
+
let onIndexChange = EventDispatcher()
|
|
79
|
+
|
|
80
|
+
public func setupImageView() {
|
|
81
|
+
registerCurrentAnchorIfNeeded()
|
|
82
|
+
guard let childImage = getChildImageView() else {
|
|
83
|
+
return
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
if let items = self.items, let initialIndex = self.initialIndex {
|
|
87
|
+
setupViewerWithItems(childImage, items: items, initialIndex: initialIndex)
|
|
88
|
+
} else if let urls = self.urls, let initialIndex = self.initialIndex {
|
|
89
|
+
setupViewerWithUrls(childImage, urls: urls, initialIndex: initialIndex)
|
|
90
|
+
} else {
|
|
91
|
+
setupViewerWithSingleImage(childImage)
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
private func setupViewerWithItems(
|
|
96
|
+
_ childImage: UIImageView,
|
|
97
|
+
items: [[String: Any]],
|
|
98
|
+
initialIndex: Int
|
|
99
|
+
) {
|
|
100
|
+
let configuration = buildViewerConfiguration()
|
|
101
|
+
|
|
102
|
+
let imageItems: [GaleriaMediaItem] = items.compactMap { item in
|
|
103
|
+
guard let type = item["type"] as? String else { return nil }
|
|
104
|
+
|
|
105
|
+
if type == "video", let urlStr = item["url"] as? String, let url = URL(string: urlStr) {
|
|
106
|
+
var posterUrl: URL? = nil
|
|
107
|
+
if let posterStr = item["poster"] as? String {
|
|
108
|
+
posterUrl = URL(string: posterStr)
|
|
109
|
+
}
|
|
110
|
+
return GaleriaMediaItem.video(url: url, poster: posterUrl)
|
|
111
|
+
} else if type == "image", let urlStr = item["url"] as? String {
|
|
112
|
+
if urlStr.hasPrefix("http://") || urlStr.hasPrefix("https://") || urlStr.hasPrefix("file://") {
|
|
113
|
+
if let url = URL(string: urlStr) {
|
|
114
|
+
return GaleriaMediaItem.imageURL(url, placeholder: nil)
|
|
115
|
+
}
|
|
116
|
+
} else {
|
|
117
|
+
return GaleriaMediaItem.imageURL(URL(fileURLWithPath: urlStr), placeholder: nil)
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
return nil
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
let datasource = GaleriaArrayDataSource(items: imageItems)
|
|
124
|
+
childImage.setupGaleriaViewer(
|
|
125
|
+
dataSource: datasource,
|
|
126
|
+
initialIndex: initialIndex,
|
|
127
|
+
configuration: configuration,
|
|
128
|
+
galleryId: galleryId)
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
private func setupViewerWithUrls(
|
|
133
|
+
_ childImage: UIImageView,
|
|
134
|
+
urls: [String],
|
|
135
|
+
initialIndex: Int
|
|
136
|
+
) {
|
|
137
|
+
let configuration = buildViewerConfiguration()
|
|
138
|
+
|
|
139
|
+
let urlObjects: [URL] = urls.compactMap { string in
|
|
140
|
+
if string.hasPrefix("http://") || string.hasPrefix("https://") || string.hasPrefix("file://") {
|
|
141
|
+
return URL(string: string)
|
|
142
|
+
}
|
|
143
|
+
return URL(fileURLWithPath: string)
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
childImage.setupGaleriaViewer(
|
|
147
|
+
urls: urlObjects,
|
|
148
|
+
initialIndex: initialIndex,
|
|
149
|
+
configuration: configuration,
|
|
150
|
+
galleryId: galleryId)
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
private func setupViewerWithSingleImage(_ childImage: UIImageView) {
|
|
154
|
+
guard let img = childImage.image else {
|
|
155
|
+
print("Missing image in childImage: \(childImage)")
|
|
156
|
+
return
|
|
157
|
+
}
|
|
158
|
+
let configuration = buildViewerConfiguration()
|
|
159
|
+
|
|
160
|
+
childImage.setupGaleriaViewer(images: [img], configuration: configuration, galleryId: galleryId)
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
private func buildViewerConfiguration() -> GaleriaViewerConfiguration {
|
|
164
|
+
var configuration = GaleriaViewerConfiguration(theme: theme.toViewerTheme())
|
|
165
|
+
let iconColor = theme.iconColor()
|
|
166
|
+
|
|
167
|
+
if let closeIconName = closeIconName,
|
|
168
|
+
let closeIconImage = UIImage(systemName: closeIconName)?.withTintColor(
|
|
169
|
+
iconColor, renderingMode: .alwaysOriginal)
|
|
170
|
+
{
|
|
171
|
+
configuration.closeIcon = closeIconImage
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
if let rightIconName = rightNavItemIconName,
|
|
175
|
+
let rightIconImage = UIImage(systemName: rightIconName)?.withTintColor(
|
|
176
|
+
iconColor, renderingMode: .alwaysOriginal)
|
|
177
|
+
{
|
|
178
|
+
configuration.rightBarButton = .icon(
|
|
179
|
+
rightIconImage,
|
|
180
|
+
onTap: { index in
|
|
181
|
+
self.onPressRightNavItemIcon(["index": index])
|
|
182
|
+
})
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
configuration.onIndexChange = { [weak self] index in
|
|
186
|
+
self?.onIndexChange(["currentIndex": index])
|
|
187
|
+
}
|
|
188
|
+
return configuration
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
private func registerCurrentAnchorIfNeeded() {
|
|
192
|
+
guard
|
|
193
|
+
window != nil,
|
|
194
|
+
let galleryId,
|
|
195
|
+
let initialIndex,
|
|
196
|
+
let childImage = getChildImageView()
|
|
197
|
+
else {
|
|
198
|
+
unregisterCurrentAnchor()
|
|
199
|
+
return
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
if
|
|
203
|
+
let registeredAnchorImageView,
|
|
204
|
+
(registeredAnchorImageView !== childImage
|
|
205
|
+
|| registeredGalleryId != galleryId
|
|
206
|
+
|| registeredIndex != initialIndex)
|
|
207
|
+
{
|
|
208
|
+
unregisterCurrentAnchor()
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
GaleriaSourceViewRegistry.shared.register(
|
|
212
|
+
childImage,
|
|
213
|
+
galleryId: galleryId,
|
|
214
|
+
index: initialIndex)
|
|
215
|
+
registeredAnchorImageView = childImage
|
|
216
|
+
registeredGalleryId = galleryId
|
|
217
|
+
registeredIndex = initialIndex
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
private func unregisterCurrentAnchor() {
|
|
221
|
+
if let registeredAnchorImageView = registeredAnchorImageView {
|
|
222
|
+
GaleriaSourceViewRegistry.shared.unregister(
|
|
223
|
+
registeredAnchorImageView,
|
|
224
|
+
galleryId: registeredGalleryId,
|
|
225
|
+
index: registeredIndex)
|
|
226
|
+
}
|
|
227
|
+
registeredAnchorImageView = nil
|
|
228
|
+
registeredGalleryId = nil
|
|
229
|
+
registeredIndex = nil
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
enum Theme: String, Enumerable {
|
|
234
|
+
case dark
|
|
235
|
+
case light
|
|
236
|
+
|
|
237
|
+
func toViewerTheme() -> GaleriaViewerTheme {
|
|
238
|
+
switch self {
|
|
239
|
+
case .dark:
|
|
240
|
+
return .dark
|
|
241
|
+
case .light:
|
|
242
|
+
return .light
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
func iconColor() -> UIColor {
|
|
246
|
+
switch self {
|
|
247
|
+
case .dark:
|
|
248
|
+
return .white
|
|
249
|
+
case .light:
|
|
250
|
+
return .black
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import Foundation
|
|
2
|
+
import UIKit
|
|
3
|
+
#if canImport(SDWebImage)
|
|
4
|
+
import SDWebImage
|
|
5
|
+
#endif
|
|
6
|
+
|
|
7
|
+
public protocol GaleriaImageLoading {
|
|
8
|
+
func loadImage(
|
|
9
|
+
_ url: URL,
|
|
10
|
+
placeholder: UIImage?,
|
|
11
|
+
imageView: UIImageView,
|
|
12
|
+
completion: @escaping (_ image: UIImage?) -> Void
|
|
13
|
+
)
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
public struct GaleriaURLSessionImageLoader: GaleriaImageLoading {
|
|
17
|
+
public init() {}
|
|
18
|
+
|
|
19
|
+
public func loadImage(
|
|
20
|
+
_ url: URL,
|
|
21
|
+
placeholder: UIImage?,
|
|
22
|
+
imageView: UIImageView,
|
|
23
|
+
completion: @escaping (UIImage?) -> Void
|
|
24
|
+
) {
|
|
25
|
+
if let placeholder = placeholder {
|
|
26
|
+
imageView.image = placeholder
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
DispatchQueue.global(qos: .background).async {
|
|
30
|
+
guard let data = try? Data(contentsOf: url), let image = UIImage(data: data) else {
|
|
31
|
+
completion(nil)
|
|
32
|
+
return
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
DispatchQueue.main.async {
|
|
36
|
+
imageView.image = image
|
|
37
|
+
completion(image)
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
#if canImport(SDWebImage)
|
|
44
|
+
struct GaleriaSDWebImageLoader: GaleriaImageLoading {
|
|
45
|
+
func loadImage(
|
|
46
|
+
_ url: URL,
|
|
47
|
+
placeholder: UIImage?,
|
|
48
|
+
imageView: UIImageView,
|
|
49
|
+
completion: @escaping (UIImage?) -> Void
|
|
50
|
+
) {
|
|
51
|
+
imageView.sd_setImage(
|
|
52
|
+
with: url,
|
|
53
|
+
placeholderImage: placeholder,
|
|
54
|
+
options: [],
|
|
55
|
+
progress: nil
|
|
56
|
+
) { image, _, _, _ in
|
|
57
|
+
DispatchQueue.main.async {
|
|
58
|
+
completion(image)
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
#endif
|
|
64
|
+
|
|
65
|
+
public enum GaleriaImageLoaderFactory {
|
|
66
|
+
public static func makeDefault() -> GaleriaImageLoading {
|
|
67
|
+
#if canImport(SDWebImage)
|
|
68
|
+
return GaleriaSDWebImageLoader()
|
|
69
|
+
#else
|
|
70
|
+
return GaleriaURLSessionImageLoader()
|
|
71
|
+
#endif
|
|
72
|
+
}
|
|
73
|
+
}
|
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
import ObjectiveC
|
|
2
|
+
import UIKit
|
|
3
|
+
|
|
4
|
+
extension UIImageView {
|
|
5
|
+
private struct GaleriaAnchorRegistration {
|
|
6
|
+
let galleryId: String
|
|
7
|
+
let index: Int
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
private final class GaleriaTapGestureRecognizer: UITapGestureRecognizer {
|
|
11
|
+
weak var fromViewController: UIViewController?
|
|
12
|
+
var dataSource: GaleriaDataSource?
|
|
13
|
+
var imageLoader: GaleriaImageLoading?
|
|
14
|
+
var initialIndex = 0
|
|
15
|
+
var galleryId: String?
|
|
16
|
+
var configuration = GaleriaViewerConfiguration()
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
private static var anchorRegistrationKey: UInt8 = 0
|
|
20
|
+
private static var gestureRecognizerKey: UInt8 = 0
|
|
21
|
+
|
|
22
|
+
private var topMostHostViewController: UIViewController? {
|
|
23
|
+
let keyWindow: UIWindow? = {
|
|
24
|
+
if #available(iOS 13.0, *) {
|
|
25
|
+
let scenes = UIApplication.shared.connectedScenes.compactMap { $0 as? UIWindowScene }
|
|
26
|
+
let activeScene = scenes.first(where: { $0.activationState == .foregroundActive }) ?? scenes.first
|
|
27
|
+
return activeScene?.windows.first(where: { $0.isKeyWindow }) ?? activeScene?.windows.first
|
|
28
|
+
} else {
|
|
29
|
+
return UIApplication.shared.windows.first(where: { $0.isKeyWindow }) ?? UIApplication.shared.windows.first
|
|
30
|
+
}
|
|
31
|
+
}()
|
|
32
|
+
guard let rootViewController = keyWindow?.rootViewController else { return nil }
|
|
33
|
+
return rootViewController.presentedViewController ?? rootViewController
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
public func setupGaleriaViewer(
|
|
37
|
+
configuration: GaleriaViewerConfiguration = GaleriaViewerConfiguration(),
|
|
38
|
+
from viewController: UIViewController? = nil,
|
|
39
|
+
imageLoader: GaleriaImageLoading? = nil,
|
|
40
|
+
galleryId: String? = nil
|
|
41
|
+
) {
|
|
42
|
+
configureGaleriaViewer(
|
|
43
|
+
dataSource: GaleriaArrayDataSource(items: [.image(image)]),
|
|
44
|
+
initialIndex: 0,
|
|
45
|
+
configuration: configuration,
|
|
46
|
+
from: viewController,
|
|
47
|
+
imageLoader: imageLoader,
|
|
48
|
+
galleryId: galleryId
|
|
49
|
+
)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
public func setupGaleriaViewer(
|
|
53
|
+
url: URL,
|
|
54
|
+
initialIndex: Int = 0,
|
|
55
|
+
placeholder: UIImage? = nil,
|
|
56
|
+
configuration: GaleriaViewerConfiguration = GaleriaViewerConfiguration(),
|
|
57
|
+
from viewController: UIViewController? = nil,
|
|
58
|
+
imageLoader: GaleriaImageLoading? = nil,
|
|
59
|
+
galleryId: String? = nil
|
|
60
|
+
) {
|
|
61
|
+
configureGaleriaViewer(
|
|
62
|
+
dataSource: GaleriaArrayDataSource(items: [.imageURL(url, placeholder: placeholder)]),
|
|
63
|
+
initialIndex: initialIndex,
|
|
64
|
+
configuration: configuration,
|
|
65
|
+
from: viewController,
|
|
66
|
+
imageLoader: imageLoader,
|
|
67
|
+
galleryId: galleryId
|
|
68
|
+
)
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
public func setupGaleriaViewer(
|
|
72
|
+
images: [UIImage],
|
|
73
|
+
initialIndex: Int = 0,
|
|
74
|
+
configuration: GaleriaViewerConfiguration = GaleriaViewerConfiguration(),
|
|
75
|
+
from viewController: UIViewController? = nil,
|
|
76
|
+
imageLoader: GaleriaImageLoading? = nil,
|
|
77
|
+
galleryId: String? = nil
|
|
78
|
+
) {
|
|
79
|
+
let items = images.map { GaleriaMediaItem.image($0) }
|
|
80
|
+
configureGaleriaViewer(
|
|
81
|
+
dataSource: GaleriaArrayDataSource(items: items),
|
|
82
|
+
initialIndex: initialIndex,
|
|
83
|
+
configuration: configuration,
|
|
84
|
+
from: viewController,
|
|
85
|
+
imageLoader: imageLoader,
|
|
86
|
+
galleryId: galleryId
|
|
87
|
+
)
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
public func setupGaleriaViewer(
|
|
91
|
+
urls: [URL],
|
|
92
|
+
initialIndex: Int = 0,
|
|
93
|
+
placeholder: UIImage? = nil,
|
|
94
|
+
configuration: GaleriaViewerConfiguration = GaleriaViewerConfiguration(),
|
|
95
|
+
from viewController: UIViewController? = nil,
|
|
96
|
+
imageLoader: GaleriaImageLoading? = nil,
|
|
97
|
+
galleryId: String? = nil
|
|
98
|
+
) {
|
|
99
|
+
let items = urls.map { GaleriaMediaItem.imageURL($0, placeholder: placeholder) }
|
|
100
|
+
configureGaleriaViewer(
|
|
101
|
+
dataSource: GaleriaArrayDataSource(items: items),
|
|
102
|
+
initialIndex: initialIndex,
|
|
103
|
+
configuration: configuration,
|
|
104
|
+
from: viewController,
|
|
105
|
+
imageLoader: imageLoader,
|
|
106
|
+
galleryId: galleryId
|
|
107
|
+
)
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
public func setupGaleriaViewer(
|
|
111
|
+
dataSource: GaleriaDataSource,
|
|
112
|
+
initialIndex: Int = 0,
|
|
113
|
+
configuration: GaleriaViewerConfiguration = GaleriaViewerConfiguration(),
|
|
114
|
+
from viewController: UIViewController? = nil,
|
|
115
|
+
imageLoader: GaleriaImageLoading? = nil,
|
|
116
|
+
galleryId: String? = nil
|
|
117
|
+
) {
|
|
118
|
+
configureGaleriaViewer(
|
|
119
|
+
dataSource: dataSource,
|
|
120
|
+
initialIndex: initialIndex,
|
|
121
|
+
configuration: configuration,
|
|
122
|
+
from: viewController,
|
|
123
|
+
imageLoader: imageLoader,
|
|
124
|
+
galleryId: galleryId
|
|
125
|
+
)
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
func removeGaleriaViewerInteraction() {
|
|
129
|
+
if let recognizer = galeriaTapGestureRecognizer {
|
|
130
|
+
removeGestureRecognizer(recognizer)
|
|
131
|
+
}
|
|
132
|
+
galeriaTapGestureRecognizer = nil
|
|
133
|
+
|
|
134
|
+
if let registration = galeriaAnchorRegistration {
|
|
135
|
+
GaleriaSourceViewRegistry.shared.unregister(
|
|
136
|
+
self,
|
|
137
|
+
galleryId: registration.galleryId,
|
|
138
|
+
index: registration.index
|
|
139
|
+
)
|
|
140
|
+
}
|
|
141
|
+
galeriaAnchorRegistration = nil
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
private func configureGaleriaViewer(
|
|
145
|
+
dataSource: GaleriaDataSource?,
|
|
146
|
+
initialIndex: Int,
|
|
147
|
+
configuration: GaleriaViewerConfiguration,
|
|
148
|
+
from viewController: UIViewController?,
|
|
149
|
+
imageLoader: GaleriaImageLoading?,
|
|
150
|
+
galleryId: String?
|
|
151
|
+
) {
|
|
152
|
+
let recognizer = galeriaTapGestureRecognizer ?? {
|
|
153
|
+
let recognizer = GaleriaTapGestureRecognizer(target: self, action: #selector(showGaleriaViewer(_:)))
|
|
154
|
+
recognizer.numberOfTouchesRequired = 1
|
|
155
|
+
recognizer.numberOfTapsRequired = 1
|
|
156
|
+
galeriaTapGestureRecognizer = recognizer
|
|
157
|
+
return recognizer
|
|
158
|
+
}()
|
|
159
|
+
|
|
160
|
+
isUserInteractionEnabled = true
|
|
161
|
+
clipsToBounds = true
|
|
162
|
+
|
|
163
|
+
recognizer.dataSource = dataSource
|
|
164
|
+
recognizer.imageLoader = imageLoader
|
|
165
|
+
recognizer.initialIndex = initialIndex
|
|
166
|
+
recognizer.galleryId = galleryId
|
|
167
|
+
recognizer.configuration = configuration
|
|
168
|
+
recognizer.fromViewController = viewController
|
|
169
|
+
|
|
170
|
+
if recognizer.view !== self {
|
|
171
|
+
addGestureRecognizer(recognizer)
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
updateGaleriaAnchorRegistration(galleryId: galleryId, index: initialIndex)
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
private var galeriaTapGestureRecognizer: GaleriaTapGestureRecognizer? {
|
|
178
|
+
get {
|
|
179
|
+
objc_getAssociatedObject(self, &Self.gestureRecognizerKey) as? GaleriaTapGestureRecognizer
|
|
180
|
+
}
|
|
181
|
+
set {
|
|
182
|
+
objc_setAssociatedObject(
|
|
183
|
+
self,
|
|
184
|
+
&Self.gestureRecognizerKey,
|
|
185
|
+
newValue,
|
|
186
|
+
.OBJC_ASSOCIATION_RETAIN_NONATOMIC
|
|
187
|
+
)
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
private var galeriaAnchorRegistration: GaleriaAnchorRegistration? {
|
|
192
|
+
get {
|
|
193
|
+
objc_getAssociatedObject(self, &Self.anchorRegistrationKey) as? GaleriaAnchorRegistration
|
|
194
|
+
}
|
|
195
|
+
set {
|
|
196
|
+
objc_setAssociatedObject(
|
|
197
|
+
self,
|
|
198
|
+
&Self.anchorRegistrationKey,
|
|
199
|
+
newValue,
|
|
200
|
+
.OBJC_ASSOCIATION_RETAIN_NONATOMIC
|
|
201
|
+
)
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
@objc
|
|
206
|
+
private func showGaleriaViewer(_ recognizer: GaleriaTapGestureRecognizer) {
|
|
207
|
+
guard let sourceView = recognizer.view as? UIImageView else { return }
|
|
208
|
+
let resolvedImageLoader = recognizer.imageLoader ?? GaleriaImageLoaderFactory.makeDefault()
|
|
209
|
+
let viewerController = GaleriaViewerController(
|
|
210
|
+
sourceView: sourceView,
|
|
211
|
+
dataSource: recognizer.dataSource,
|
|
212
|
+
imageLoader: resolvedImageLoader,
|
|
213
|
+
configuration: recognizer.configuration,
|
|
214
|
+
initialIndex: recognizer.initialIndex,
|
|
215
|
+
galleryId: recognizer.galleryId
|
|
216
|
+
)
|
|
217
|
+
|
|
218
|
+
let rootViewController = recognizer.fromViewController ?? topMostHostViewController
|
|
219
|
+
let presenter = rootViewController.flatMap(topMostPresentedController(from:))
|
|
220
|
+
presenter?.present(viewerController, animated: false)
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
private func topMostPresentedController(from rootViewController: UIViewController) -> UIViewController {
|
|
224
|
+
var controller = rootViewController
|
|
225
|
+
while let presented = controller.presentedViewController {
|
|
226
|
+
controller = presented
|
|
227
|
+
}
|
|
228
|
+
return controller
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
private func updateGaleriaAnchorRegistration(galleryId: String?, index: Int) {
|
|
232
|
+
if let existing = galeriaAnchorRegistration {
|
|
233
|
+
GaleriaSourceViewRegistry.shared.unregister(
|
|
234
|
+
self,
|
|
235
|
+
galleryId: existing.galleryId,
|
|
236
|
+
index: existing.index
|
|
237
|
+
)
|
|
238
|
+
galeriaAnchorRegistration = nil
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
guard let galleryId = galleryId, galleryId.isEmpty == false else { return }
|
|
242
|
+
GaleriaSourceViewRegistry.shared.register(self, galleryId: galleryId, index: index)
|
|
243
|
+
galeriaAnchorRegistration = GaleriaAnchorRegistration(galleryId: galleryId, index: index)
|
|
244
|
+
}
|
|
245
|
+
}
|