zkit-ui 2.0.4 → 2.0.6
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/NativeImagePreviewLayoutSupport.java +450 -0
- package/android/src/main/java/cn/fontree/zkit/ui/preview/NativeImagePreviewMediaItem.java +36 -0
- package/android/src/main/java/cn/fontree/zkit/ui/preview/NativeImagePreviewModule.kt +43 -0
- package/android/src/main/java/cn/fontree/zkit/ui/preview/NativeImagePreviewPagerAdapter.java +140 -0
- package/android/src/main/java/cn/fontree/zkit/ui/preview/NativeImagePreviewSharedElementGeometry.java +25 -0
- package/android/src/main/java/cn/fontree/zkit/ui/preview/NativeImagePreviewSharedElementNames.java +39 -0
- package/android/src/main/java/cn/fontree/zkit/ui/preview/NativeImagePreviewSharedElementState.java +25 -0
- package/android/src/main/java/cn/fontree/zkit/ui/preview/NativeImagePreviewSourceViewRegistry.java +115 -0
- package/android/src/main/java/cn/fontree/zkit/ui/preview/NativeImagePreviewTransitionSnapshotView.java +60 -0
- package/android/src/main/java/cn/fontree/zkit/ui/preview/NativeImagePreviewVideoPlayerView.java +338 -0
- package/android/src/main/java/cn/fontree/zkit/ui/preview/NativeImagePreviewView.kt +305 -0
- package/android/src/main/java/cn/fontree/zkit/ui/preview/NativeImagePreviewViewerOverlayView.java +636 -0
- package/android/src/main/java/cn/fontree/zkit/ui/preview/NativeImagePreviewViewerPageView.java +357 -0
- package/android/src/main/java/cn/fontree/zkit/ui/preview/NativeImagePreviewViewerTransitionController.java +223 -0
- package/android/src/main/res/layout/native_image_preview_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 +4 -0
- package/dist/ui/NativeImagePreview/NativeImagePreview.web.d.ts.map +1 -0
- package/dist/ui/NativeImagePreview/NativeImagePreview.web.js +150 -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 +68 -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 +83 -0
- package/dist/ui/NativeImagePreview/web/geometry.d.ts +20 -0
- package/dist/ui/NativeImagePreview/web/geometry.d.ts.map +1 -0
- package/dist/ui/NativeImagePreview/web/geometry.js +121 -0
- package/dist/ui/NativeImagePreview/web/media.d.ts +6 -0
- package/dist/ui/NativeImagePreview/web/media.d.ts.map +1 -0
- package/dist/ui/NativeImagePreview/web/media.js +90 -0
- package/dist/ui/NativeImagePreview/web/styles.css +137 -0
- package/dist/ui/NativeImagePreview/web/transition.d.ts +15 -0
- package/dist/ui/NativeImagePreview/web/transition.d.ts.map +1 -0
- package/dist/ui/NativeImagePreview/web/transition.js +77 -0
- package/dist/ui/NativeImagePreview/web/types.d.ts +74 -0
- package/dist/ui/NativeImagePreview/web/types.d.ts.map +1 -0
- package/dist/ui/NativeImagePreview/web/types.js +3 -0
- package/dist/ui/NativeImagePreview/web/viewer.d.ts +104 -0
- package/dist/ui/NativeImagePreview/web/viewer.d.ts.map +1 -0
- package/dist/ui/NativeImagePreview/web/viewer.js +894 -0
- package/expo-module.config.json +11 -0
- package/ios/NativeImagePreview/NativeImagePreviewImageLoader.swift +73 -0
- package/ios/NativeImagePreview/NativeImagePreviewImageViewInteraction.swift +245 -0
- package/ios/NativeImagePreview/NativeImagePreviewLayoutSupport.swift +143 -0
- package/ios/NativeImagePreview/NativeImagePreviewMedia.swift +28 -0
- package/ios/NativeImagePreview/NativeImagePreviewSourceViewRegistry.swift +80 -0
- package/ios/NativeImagePreview/NativeImagePreviewVideoPlayerView.swift +437 -0
- package/ios/NativeImagePreview/NativeImagePreviewViewerConfiguration.swift +51 -0
- package/ios/NativeImagePreview/NativeImagePreviewViewerController.swift +745 -0
- package/ios/NativeImagePreview/NativeImagePreviewViewerPageView.swift +623 -0
- package/ios/NativeImagePreview/NativeImagePreviewViewerTransitionCoordinator.swift +312 -0
- package/ios/NativeImagePreviewModule.swift +46 -0
- package/ios/NativeImagePreviewView.swift +252 -0
- package/package.json +16 -2
- package/src/all.ts +18 -0
- package/src/services/ImagePreviewService/README.md +19 -0
- package/src/types.d.ts +1 -0
- package/src/ui/NativeImagePreview/NativeImagePreview.tsx +146 -0
- package/src/ui/NativeImagePreview/NativeImagePreview.web.tsx +201 -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 +94 -0
- package/src/ui/NativeImagePreview/utils.ts +108 -0
- package/src/ui/NativeImagePreview/web/geometry.ts +134 -0
- package/src/ui/NativeImagePreview/web/media.ts +91 -0
- package/src/ui/NativeImagePreview/web/styles.css +137 -0
- package/src/ui/NativeImagePreview/web/transition.ts +111 -0
- package/src/ui/NativeImagePreview/web/types.ts +88 -0
- package/src/ui/NativeImagePreview/web/viewer.ts +1024 -0
- package/zkit-ui.podspec +13 -1
|
@@ -0,0 +1,745 @@
|
|
|
1
|
+
import UIKit
|
|
2
|
+
|
|
3
|
+
final class NativeImagePreviewViewerController: UIViewController {
|
|
4
|
+
private weak var dataSource: NativeImagePreviewDataSource?
|
|
5
|
+
private weak var initialSourceView: UIImageView?
|
|
6
|
+
private let imageLoader: NativeImagePreviewImageLoading
|
|
7
|
+
private let previewGroupId: String?
|
|
8
|
+
private let initialIndex: Int
|
|
9
|
+
private let configuration: NativeImagePreviewViewerConfiguration
|
|
10
|
+
|
|
11
|
+
private let backgroundView = UIView()
|
|
12
|
+
private let contentView = UIView()
|
|
13
|
+
private let collectionView: UICollectionView
|
|
14
|
+
private let navigationBar = UINavigationBar(frame: .zero)
|
|
15
|
+
private let navigationItemRef = UINavigationItem()
|
|
16
|
+
|
|
17
|
+
private var transitionCoordinatorRef: NativeImagePreviewViewerTransitionCoordinator?
|
|
18
|
+
private var hasPerformedOpenTransition = false
|
|
19
|
+
private var pendingDismissal = false
|
|
20
|
+
private var pendingInitialScroll = false
|
|
21
|
+
|
|
22
|
+
private var currentIndex = 0
|
|
23
|
+
private var pageCount = 0
|
|
24
|
+
private var horizontalPagingEnabled = true {
|
|
25
|
+
didSet {
|
|
26
|
+
collectionView.isScrollEnabled = horizontalPagingEnabled
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
private var verticalDismissLocked = false
|
|
31
|
+
private var verticalDismissNeedsReanchor = false
|
|
32
|
+
private var isRestoringDismissDrag = false
|
|
33
|
+
private var isDraggingToDismiss = false
|
|
34
|
+
private var dragStartPoint = CGPoint.zero
|
|
35
|
+
private var dragTargetView: UIView?
|
|
36
|
+
private weak var dragPageView: NativeImagePreviewViewerPageView?
|
|
37
|
+
private weak var hiddenActiveSourceView: UIImageView?
|
|
38
|
+
private var hiddenActiveSourcePreviousAlpha: CGFloat = 1
|
|
39
|
+
|
|
40
|
+
private lazy var panGestureRecognizer: UIPanGestureRecognizer = {
|
|
41
|
+
let recognizer = UIPanGestureRecognizer(target: self, action: #selector(handlePan(_:)))
|
|
42
|
+
recognizer.delegate = self
|
|
43
|
+
recognizer.cancelsTouchesInView = false
|
|
44
|
+
return recognizer
|
|
45
|
+
}()
|
|
46
|
+
|
|
47
|
+
private struct RightBarButtonConfiguration {
|
|
48
|
+
let image: UIImage?
|
|
49
|
+
let title: String?
|
|
50
|
+
let onTap: ((Int) -> Void)?
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
private lazy var theme = configuration.theme
|
|
54
|
+
private lazy var mediaContentMode = configuration.contentMode
|
|
55
|
+
private lazy var closeIcon = configuration.closeIcon ?? UIImage(systemName: "xmark")
|
|
56
|
+
private lazy var rightBarButtonConfiguration: RightBarButtonConfiguration? = {
|
|
57
|
+
switch configuration.rightBarButton {
|
|
58
|
+
case .title(let title, let onTap):
|
|
59
|
+
return RightBarButtonConfiguration(image: nil, title: title, onTap: onTap)
|
|
60
|
+
case .icon(let image, let onTap):
|
|
61
|
+
return RightBarButtonConfiguration(image: image, title: nil, onTap: onTap)
|
|
62
|
+
case .none:
|
|
63
|
+
return nil
|
|
64
|
+
}
|
|
65
|
+
}()
|
|
66
|
+
|
|
67
|
+
private var prefersDefaultVideoCloseButton: Bool {
|
|
68
|
+
configuration.closeIcon == nil
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
init(
|
|
72
|
+
sourceView: UIImageView,
|
|
73
|
+
dataSource: NativeImagePreviewDataSource?,
|
|
74
|
+
imageLoader: NativeImagePreviewImageLoading,
|
|
75
|
+
configuration: NativeImagePreviewViewerConfiguration = NativeImagePreviewViewerConfiguration(),
|
|
76
|
+
initialIndex: Int = 0,
|
|
77
|
+
previewGroupId: String? = nil
|
|
78
|
+
) {
|
|
79
|
+
self.initialSourceView = sourceView
|
|
80
|
+
self.dataSource = dataSource
|
|
81
|
+
self.imageLoader = imageLoader
|
|
82
|
+
self.configuration = configuration
|
|
83
|
+
self.initialIndex = initialIndex
|
|
84
|
+
self.previewGroupId = previewGroupId
|
|
85
|
+
|
|
86
|
+
let layout = UICollectionViewFlowLayout()
|
|
87
|
+
layout.scrollDirection = .horizontal
|
|
88
|
+
layout.minimumLineSpacing = 0
|
|
89
|
+
layout.minimumInteritemSpacing = 0
|
|
90
|
+
layout.sectionInset = .zero
|
|
91
|
+
collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout)
|
|
92
|
+
|
|
93
|
+
super.init(nibName: nil, bundle: nil)
|
|
94
|
+
modalPresentationStyle = .overFullScreen
|
|
95
|
+
modalPresentationCapturesStatusBarAppearance = true
|
|
96
|
+
definesPresentationContext = true
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
required init?(coder: NSCoder) {
|
|
100
|
+
fatalError("init(coder:) has not been implemented")
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
override func viewDidLoad() {
|
|
104
|
+
super.viewDidLoad()
|
|
105
|
+
overrideUserInterfaceStyle = theme == .dark ? .dark : .light
|
|
106
|
+
setupUI()
|
|
107
|
+
currentIndex = clampedIndex(initialIndex)
|
|
108
|
+
pageCount = dataSource?.numberOfItems() ?? 0
|
|
109
|
+
collectionView.reloadData()
|
|
110
|
+
refreshNavigationItems()
|
|
111
|
+
setNavigationBarHidden(shouldHideNavigationBarForCurrentPage, animated: false)
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
override func viewDidLayoutSubviews() {
|
|
115
|
+
super.viewDidLayoutSubviews()
|
|
116
|
+
|
|
117
|
+
guard let layout = collectionView.collectionViewLayout as? UICollectionViewFlowLayout else { return }
|
|
118
|
+
if layout.itemSize != collectionView.bounds.size {
|
|
119
|
+
layout.itemSize = collectionView.bounds.size
|
|
120
|
+
layout.invalidateLayout()
|
|
121
|
+
scrollToIndex(currentIndex, animated: false)
|
|
122
|
+
} else if pendingInitialScroll {
|
|
123
|
+
scrollToIndex(currentIndex, animated: false)
|
|
124
|
+
pendingInitialScroll = false
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
override func viewDidAppear(_ animated: Bool) {
|
|
129
|
+
super.viewDidAppear(animated)
|
|
130
|
+
if hasPerformedOpenTransition == false {
|
|
131
|
+
hasPerformedOpenTransition = true
|
|
132
|
+
scrollToIndex(currentIndex, animated: false)
|
|
133
|
+
transitionCoordinatorRef = NativeImagePreviewViewerTransitionCoordinator(containerView: view)
|
|
134
|
+
performOpenTransition()
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
override var prefersStatusBarHidden: Bool {
|
|
139
|
+
true
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
override var preferredStatusBarStyle: UIStatusBarStyle {
|
|
143
|
+
theme == .dark ? .lightContent : .darkContent
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
deinit {
|
|
147
|
+
restoreHiddenActiveSourceView()
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
private var currentPageView: NativeImagePreviewViewerPageView? {
|
|
151
|
+
pageView(at: currentIndex)
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
private var shouldHideNavigationBarForCurrentPage: Bool {
|
|
155
|
+
guard let currentPageView = currentPageView else { return true }
|
|
156
|
+
guard currentPageView.isVideoPage else { return true }
|
|
157
|
+
return !currentPageView.areVideoControlsVisible
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
private func setupUI() {
|
|
161
|
+
view.backgroundColor = .clear
|
|
162
|
+
view.overrideUserInterfaceStyle = theme == .dark ? .dark : .light
|
|
163
|
+
|
|
164
|
+
backgroundView.backgroundColor = theme.backgroundColor
|
|
165
|
+
backgroundView.alpha = 0
|
|
166
|
+
view.addSubview(backgroundView)
|
|
167
|
+
backgroundView.pinToSuperviewEdges()
|
|
168
|
+
|
|
169
|
+
contentView.backgroundColor = .clear
|
|
170
|
+
contentView.alpha = 0
|
|
171
|
+
contentView.overrideUserInterfaceStyle = theme == .dark ? .dark : .light
|
|
172
|
+
view.addSubview(contentView)
|
|
173
|
+
contentView.pinToSuperviewEdges()
|
|
174
|
+
|
|
175
|
+
collectionView.backgroundColor = .clear
|
|
176
|
+
collectionView.alwaysBounceVertical = false
|
|
177
|
+
collectionView.alwaysBounceHorizontal = true
|
|
178
|
+
collectionView.showsHorizontalScrollIndicator = false
|
|
179
|
+
collectionView.showsVerticalScrollIndicator = false
|
|
180
|
+
collectionView.isPagingEnabled = true
|
|
181
|
+
collectionView.decelerationRate = .fast
|
|
182
|
+
collectionView.contentInsetAdjustmentBehavior = .never
|
|
183
|
+
collectionView.delegate = self
|
|
184
|
+
collectionView.dataSource = self
|
|
185
|
+
collectionView.register(NativeImagePreviewViewerPageCell.self, forCellWithReuseIdentifier: NativeImagePreviewViewerPageCell.reuseIdentifier)
|
|
186
|
+
contentView.addSubview(collectionView)
|
|
187
|
+
collectionView.pinToSuperviewEdges()
|
|
188
|
+
collectionView.addGestureRecognizer(panGestureRecognizer)
|
|
189
|
+
|
|
190
|
+
navigationBar.isTranslucent = true
|
|
191
|
+
navigationBar.setBackgroundImage(UIImage(), for: .default)
|
|
192
|
+
navigationBar.shadowImage = UIImage()
|
|
193
|
+
navigationBar.tintColor = theme.tintColor
|
|
194
|
+
navigationBar.overrideUserInterfaceStyle = theme == .dark ? .dark : .light
|
|
195
|
+
navigationBar.items = [navigationItemRef]
|
|
196
|
+
navigationBar.alpha = 0
|
|
197
|
+
navigationBar.isHidden = true
|
|
198
|
+
navigationBar.isUserInteractionEnabled = false
|
|
199
|
+
contentView.addSubview(navigationBar)
|
|
200
|
+
navigationBar.translatesAutoresizingMaskIntoConstraints = false
|
|
201
|
+
NSLayoutConstraint.activate([
|
|
202
|
+
navigationBar.leadingAnchor.constraint(equalTo: contentView.leadingAnchor),
|
|
203
|
+
navigationBar.trailingAnchor.constraint(equalTo: contentView.trailingAnchor),
|
|
204
|
+
navigationBar.topAnchor.constraint(equalTo: contentView.safeAreaLayoutGuide.topAnchor),
|
|
205
|
+
])
|
|
206
|
+
|
|
207
|
+
refreshNavigationItems()
|
|
208
|
+
|
|
209
|
+
if let rightBarButtonConfiguration = rightBarButtonConfiguration {
|
|
210
|
+
if let image = rightBarButtonConfiguration.image {
|
|
211
|
+
navigationItemRef.rightBarButtonItem = UIBarButtonItem(
|
|
212
|
+
image: image,
|
|
213
|
+
style: .plain,
|
|
214
|
+
target: self,
|
|
215
|
+
action: #selector(handleRightBarButton)
|
|
216
|
+
)
|
|
217
|
+
} else if let title = rightBarButtonConfiguration.title {
|
|
218
|
+
navigationItemRef.rightBarButtonItem = UIBarButtonItem(
|
|
219
|
+
title: title,
|
|
220
|
+
style: .plain,
|
|
221
|
+
target: self,
|
|
222
|
+
action: #selector(handleRightBarButton)
|
|
223
|
+
)
|
|
224
|
+
}
|
|
225
|
+
navigationItemRef.rightBarButtonItem?.tintColor = theme.tintColor
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
private func performOpenTransition() {
|
|
230
|
+
collectionView.layoutIfNeeded()
|
|
231
|
+
let pageView = currentPageView
|
|
232
|
+
refreshNavigationItems()
|
|
233
|
+
setNavigationBarHidden(shouldHideNavigationBarForCurrentPage, animated: false)
|
|
234
|
+
transitionCoordinatorRef?.performOpenTransition(
|
|
235
|
+
from: initialSourceView,
|
|
236
|
+
to: pageView,
|
|
237
|
+
backgroundView: backgroundView,
|
|
238
|
+
contentView: contentView
|
|
239
|
+
) { [weak self] in
|
|
240
|
+
guard let self = self else { return }
|
|
241
|
+
self.setVideoRevealAllowedForVisiblePages(true)
|
|
242
|
+
self.pageView(at: self.currentIndex)?.setActive(true)
|
|
243
|
+
self.refreshNavigationItems()
|
|
244
|
+
self.setNavigationBarHidden(self.shouldHideNavigationBarForCurrentPage, animated: false)
|
|
245
|
+
self.hideActiveSourceViewForCurrentIndex()
|
|
246
|
+
self.configuration.onIndexChange?(self.currentIndex)
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
private func performDismissTransition(anchorOverride: UIImageView? = nil) {
|
|
251
|
+
guard pendingDismissal == false else { return }
|
|
252
|
+
pendingDismissal = true
|
|
253
|
+
|
|
254
|
+
let pageView = currentPageView
|
|
255
|
+
pageView?.prepareForReturnAnimation()
|
|
256
|
+
|
|
257
|
+
let anchorView = anchorOverride ?? anchorView(for: currentIndex)
|
|
258
|
+
transitionCoordinatorRef?.performCloseTransition(
|
|
259
|
+
from: pageView,
|
|
260
|
+
to: anchorView,
|
|
261
|
+
backgroundView: backgroundView,
|
|
262
|
+
contentView: contentView
|
|
263
|
+
) { [weak self] in
|
|
264
|
+
self?.restoreHiddenActiveSourceView()
|
|
265
|
+
self?.dismiss(animated: false)
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
private func anchorView(for index: Int) -> UIImageView? {
|
|
270
|
+
if let previewGroupId = previewGroupId,
|
|
271
|
+
let sourceView = NativeImagePreviewSourceViewRegistry.shared.sourceView(for: previewGroupId, index: index) {
|
|
272
|
+
return sourceView
|
|
273
|
+
}
|
|
274
|
+
if index == initialIndex {
|
|
275
|
+
return initialSourceView
|
|
276
|
+
}
|
|
277
|
+
return nil
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
private func clampedIndex(_ index: Int) -> Int {
|
|
281
|
+
let count = dataSource?.numberOfItems() ?? 0
|
|
282
|
+
guard count > 0 else { return 0 }
|
|
283
|
+
return min(max(index, 0), count - 1)
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
private func pageView(at index: Int) -> NativeImagePreviewViewerPageView? {
|
|
287
|
+
let indexPath = IndexPath(item: index, section: 0)
|
|
288
|
+
return collectionView.cellForItem(at: indexPath).flatMap { $0 as? NativeImagePreviewViewerPageCell }?.pageView
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
private func hideActiveSourceViewForCurrentIndex() {
|
|
292
|
+
guard hasPerformedOpenTransition, pendingDismissal == false else { return }
|
|
293
|
+
let sourceView = anchorView(for: currentIndex)
|
|
294
|
+
if hiddenActiveSourceView !== sourceView {
|
|
295
|
+
restoreHiddenActiveSourceView()
|
|
296
|
+
hiddenActiveSourcePreviousAlpha = sourceView?.alpha ?? 1
|
|
297
|
+
hiddenActiveSourceView = sourceView
|
|
298
|
+
}
|
|
299
|
+
sourceView?.alpha = 0
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
private func restoreHiddenActiveSourceView() {
|
|
303
|
+
hiddenActiveSourceView?.alpha = hiddenActiveSourcePreviousAlpha
|
|
304
|
+
hiddenActiveSourceView = nil
|
|
305
|
+
hiddenActiveSourcePreviousAlpha = 1
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
private func scrollToIndex(_ index: Int, animated: Bool) {
|
|
309
|
+
guard pageCount > 0 else { return }
|
|
310
|
+
guard collectionView.bounds.width > 0 else {
|
|
311
|
+
pendingInitialScroll = true
|
|
312
|
+
return
|
|
313
|
+
}
|
|
314
|
+
let indexPath = IndexPath(item: clampedIndex(index), section: 0)
|
|
315
|
+
collectionView.scrollToItem(at: indexPath, at: .centeredHorizontally, animated: animated)
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
private func updateCurrentIndexFromVisiblePage() {
|
|
319
|
+
let center = CGPoint(
|
|
320
|
+
x: collectionView.contentOffset.x + collectionView.bounds.width * 0.5,
|
|
321
|
+
y: collectionView.bounds.midY
|
|
322
|
+
)
|
|
323
|
+
if let indexPath = collectionView.indexPathForItem(at: center) {
|
|
324
|
+
setCurrentIndex(indexPath.item, notify: true)
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
private func setCurrentIndex(_ newIndex: Int, notify: Bool) {
|
|
329
|
+
let safeIndex = clampedIndex(newIndex)
|
|
330
|
+
guard safeIndex != currentIndex || notify == false else {
|
|
331
|
+
updateVisiblePageStates()
|
|
332
|
+
return
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
currentIndex = safeIndex
|
|
336
|
+
updateVisiblePageStates()
|
|
337
|
+
hideActiveSourceViewForCurrentIndex()
|
|
338
|
+
if notify {
|
|
339
|
+
configuration.onIndexChange?(currentIndex)
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
private func updateVisiblePageStates() {
|
|
344
|
+
for cell in collectionView.visibleCells {
|
|
345
|
+
guard let pageCell = cell as? NativeImagePreviewViewerPageCell else { continue }
|
|
346
|
+
let index = collectionView.indexPath(for: pageCell)?.item ?? -1
|
|
347
|
+
pageCell.pageView.setActive(index == currentIndex)
|
|
348
|
+
pageCell.pageView.setVideoRevealAllowed(index == currentIndex && hasPerformedOpenTransition)
|
|
349
|
+
if index != currentIndex {
|
|
350
|
+
pageCell.pageView.setVideoControlsVisible(false, animated: false)
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
refreshNavigationItems()
|
|
354
|
+
setNavigationBarHidden(shouldHideNavigationBarForCurrentPage, animated: true)
|
|
355
|
+
recalculateHorizontalPagingEnabled()
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
private func setVideoRevealAllowedForVisiblePages(_ allowed: Bool) {
|
|
359
|
+
for cell in collectionView.visibleCells {
|
|
360
|
+
guard let pageCell = cell as? NativeImagePreviewViewerPageCell else { continue }
|
|
361
|
+
let index = collectionView.indexPath(for: pageCell)?.item ?? -1
|
|
362
|
+
pageCell.pageView.setVideoRevealAllowed(allowed && index == currentIndex)
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
private func setNavigationBarHidden(_ hidden: Bool, animated: Bool) {
|
|
367
|
+
let shouldHide = hidden
|
|
368
|
+
let updates = {
|
|
369
|
+
self.navigationBar.alpha = shouldHide ? 0 : 1
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
guard animated else {
|
|
373
|
+
navigationBar.layer.removeAllAnimations()
|
|
374
|
+
updates()
|
|
375
|
+
navigationBar.isHidden = shouldHide
|
|
376
|
+
navigationBar.isUserInteractionEnabled = !shouldHide
|
|
377
|
+
return
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
navigationBar.layer.removeAllAnimations()
|
|
381
|
+
if shouldHide {
|
|
382
|
+
navigationBar.isUserInteractionEnabled = false
|
|
383
|
+
UIView.animate(
|
|
384
|
+
withDuration: 0.18,
|
|
385
|
+
delay: 0,
|
|
386
|
+
options: [.curveEaseInOut, .beginFromCurrentState],
|
|
387
|
+
animations: updates
|
|
388
|
+
) { _ in
|
|
389
|
+
self.navigationBar.isHidden = true
|
|
390
|
+
}
|
|
391
|
+
return
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
navigationBar.isHidden = false
|
|
395
|
+
navigationBar.isUserInteractionEnabled = true
|
|
396
|
+
UIView.animate(
|
|
397
|
+
withDuration: 0.18,
|
|
398
|
+
delay: 0,
|
|
399
|
+
options: [.curveEaseInOut, .beginFromCurrentState],
|
|
400
|
+
animations: updates
|
|
401
|
+
)
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
private func recalculateHorizontalPagingEnabled() {
|
|
405
|
+
let canPageHorizontally = currentPageView?.canPageHorizontally ?? true
|
|
406
|
+
horizontalPagingEnabled = canPageHorizontally
|
|
407
|
+
&& !isDraggingToDismiss
|
|
408
|
+
&& !isRestoringDismissDrag
|
|
409
|
+
&& !verticalDismissLocked
|
|
410
|
+
&& !verticalDismissNeedsReanchor
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
private func refreshNavigationItems() {
|
|
414
|
+
let currentIsVideoPage = currentPageView?.isVideoPage == true
|
|
415
|
+
guard currentIsVideoPage else {
|
|
416
|
+
navigationItemRef.leftBarButtonItem = nil
|
|
417
|
+
return
|
|
418
|
+
}
|
|
419
|
+
let closeButtonItem: UIBarButtonItem
|
|
420
|
+
if currentIsVideoPage && prefersDefaultVideoCloseButton {
|
|
421
|
+
closeButtonItem = UIBarButtonItem(
|
|
422
|
+
barButtonSystemItem: .close,
|
|
423
|
+
target: self,
|
|
424
|
+
action: #selector(handleCloseButton)
|
|
425
|
+
)
|
|
426
|
+
} else {
|
|
427
|
+
closeButtonItem = UIBarButtonItem(
|
|
428
|
+
image: closeIcon,
|
|
429
|
+
style: .plain,
|
|
430
|
+
target: self,
|
|
431
|
+
action: #selector(handleCloseButton)
|
|
432
|
+
)
|
|
433
|
+
}
|
|
434
|
+
closeButtonItem.tintColor = theme.tintColor
|
|
435
|
+
navigationItemRef.leftBarButtonItem = closeButtonItem
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
private func beginVerticalDismissDrag() {
|
|
439
|
+
guard let pageView = currentPageView else { return }
|
|
440
|
+
isDraggingToDismiss = true
|
|
441
|
+
dragTargetView = pageView.dragTargetView
|
|
442
|
+
dragStartPoint = dragTargetView?.center ?? pageView.dragTargetView.center
|
|
443
|
+
dragPageView = pageView
|
|
444
|
+
setNavigationBarHidden(true, animated: true)
|
|
445
|
+
pageView.prepareForDismissDrag()
|
|
446
|
+
recalculateHorizontalPagingEnabled()
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
private func updateVerticalDismissDrag(_ recognizer: UIPanGestureRecognizer) {
|
|
450
|
+
guard let dragTargetView = dragTargetView else { return }
|
|
451
|
+
let translation = recognizer.translation(in: view)
|
|
452
|
+
let height = max(view.bounds.height, 1)
|
|
453
|
+
let progress = min(abs(translation.y) / height, 1)
|
|
454
|
+
let scale = max(0.84, 1 - progress * 0.16)
|
|
455
|
+
|
|
456
|
+
dragTargetView.center = CGPoint(
|
|
457
|
+
x: dragStartPoint.x + translation.x,
|
|
458
|
+
y: dragStartPoint.y + translation.y
|
|
459
|
+
)
|
|
460
|
+
dragTargetView.transform = CGAffineTransform(scaleX: scale, y: scale)
|
|
461
|
+
backgroundView.alpha = max(0, 1 - min(0.9, progress * 1.25))
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
private func finishVerticalDismissDrag(_ recognizer: UIPanGestureRecognizer) {
|
|
465
|
+
guard let dragTargetView = dragTargetView else {
|
|
466
|
+
isDraggingToDismiss = false
|
|
467
|
+
resetDragState()
|
|
468
|
+
recalculateHorizontalPagingEnabled()
|
|
469
|
+
return
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
let translation = recognizer.translation(in: view)
|
|
473
|
+
let velocity = recognizer.velocity(in: view)
|
|
474
|
+
let dismissDistance = view.bounds.height * 0.16
|
|
475
|
+
let quickFlingDistance = view.bounds.height * 0.06
|
|
476
|
+
let shouldDismissByDistance = abs(translation.y) > dismissDistance
|
|
477
|
+
let shouldDismissByFling = abs(translation.y) > quickFlingDistance && abs(velocity.y) > 1300
|
|
478
|
+
|
|
479
|
+
isDraggingToDismiss = false
|
|
480
|
+
recalculateHorizontalPagingEnabled()
|
|
481
|
+
|
|
482
|
+
if shouldDismissByDistance || shouldDismissByFling {
|
|
483
|
+
dragPageView?.prepareForReturnAnimation()
|
|
484
|
+
performDismissTransition()
|
|
485
|
+
resetDragState()
|
|
486
|
+
return
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
isRestoringDismissDrag = true
|
|
490
|
+
recalculateHorizontalPagingEnabled()
|
|
491
|
+
UIView.animate(
|
|
492
|
+
withDuration: 0.2,
|
|
493
|
+
delay: 0,
|
|
494
|
+
options: [.curveEaseOut, .allowUserInteraction]
|
|
495
|
+
) {
|
|
496
|
+
dragTargetView.center = self.dragStartPoint
|
|
497
|
+
dragTargetView.transform = .identity
|
|
498
|
+
self.backgroundView.alpha = 1
|
|
499
|
+
} completion: { _ in
|
|
500
|
+
self.isRestoringDismissDrag = false
|
|
501
|
+
self.dragPageView?.restoreAfterDismissCancelled()
|
|
502
|
+
self.setNavigationBarHidden(self.shouldHideNavigationBarForCurrentPage, animated: false)
|
|
503
|
+
self.resetDragState()
|
|
504
|
+
self.recalculateHorizontalPagingEnabled()
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
private func cancelVerticalDismissDrag() {
|
|
509
|
+
guard let dragTargetView = dragTargetView else {
|
|
510
|
+
isDraggingToDismiss = false
|
|
511
|
+
resetDragState()
|
|
512
|
+
recalculateHorizontalPagingEnabled()
|
|
513
|
+
return
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
isDraggingToDismiss = false
|
|
517
|
+
isRestoringDismissDrag = true
|
|
518
|
+
recalculateHorizontalPagingEnabled()
|
|
519
|
+
UIView.animate(
|
|
520
|
+
withDuration: 0.2,
|
|
521
|
+
delay: 0,
|
|
522
|
+
options: [.curveEaseOut, .allowUserInteraction]
|
|
523
|
+
) {
|
|
524
|
+
dragTargetView.center = self.dragStartPoint
|
|
525
|
+
dragTargetView.transform = .identity
|
|
526
|
+
self.backgroundView.alpha = 1
|
|
527
|
+
} completion: { _ in
|
|
528
|
+
self.isRestoringDismissDrag = false
|
|
529
|
+
self.dragPageView?.restoreAfterDismissCancelled()
|
|
530
|
+
self.setNavigationBarHidden(self.shouldHideNavigationBarForCurrentPage, animated: false)
|
|
531
|
+
self.resetDragState()
|
|
532
|
+
self.recalculateHorizontalPagingEnabled()
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
private func resetDragState() {
|
|
537
|
+
dragTargetView = nil
|
|
538
|
+
dragPageView = nil
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
@objc
|
|
542
|
+
private func handlePan(_ recognizer: UIPanGestureRecognizer) {
|
|
543
|
+
if pendingDismissal {
|
|
544
|
+
return
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
let location = recognizer.location(in: view.window)
|
|
548
|
+
switch recognizer.state {
|
|
549
|
+
case .began:
|
|
550
|
+
if verticalDismissNeedsReanchor {
|
|
551
|
+
verticalDismissNeedsReanchor = false
|
|
552
|
+
recalculateHorizontalPagingEnabled()
|
|
553
|
+
}
|
|
554
|
+
case .changed:
|
|
555
|
+
guard isRestoringDismissDrag == false else { return }
|
|
556
|
+
guard verticalDismissLocked == false else { return }
|
|
557
|
+
guard let pageView = currentPageView else { return }
|
|
558
|
+
|
|
559
|
+
if isDraggingToDismiss == false {
|
|
560
|
+
let translation = recognizer.translation(in: view)
|
|
561
|
+
let velocity = recognizer.velocity(in: view)
|
|
562
|
+
let isVerticalCandidate = abs(translation.y) > 8 && abs(translation.y) > abs(translation.x) * 1.02
|
|
563
|
+
if isVerticalCandidate && pageView.canBeginVerticalDismiss(at: location, velocity: velocity) {
|
|
564
|
+
beginVerticalDismissDrag()
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
if isDraggingToDismiss {
|
|
569
|
+
updateVerticalDismissDrag(recognizer)
|
|
570
|
+
}
|
|
571
|
+
case .ended:
|
|
572
|
+
if verticalDismissNeedsReanchor {
|
|
573
|
+
recalculateHorizontalPagingEnabled()
|
|
574
|
+
return
|
|
575
|
+
}
|
|
576
|
+
if isDraggingToDismiss {
|
|
577
|
+
finishVerticalDismissDrag(recognizer)
|
|
578
|
+
}
|
|
579
|
+
case .cancelled, .failed:
|
|
580
|
+
if isDraggingToDismiss {
|
|
581
|
+
cancelVerticalDismissDrag()
|
|
582
|
+
}
|
|
583
|
+
if verticalDismissNeedsReanchor {
|
|
584
|
+
recalculateHorizontalPagingEnabled()
|
|
585
|
+
}
|
|
586
|
+
default:
|
|
587
|
+
break
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
@objc
|
|
592
|
+
private func handleCloseButton() {
|
|
593
|
+
performDismissTransition()
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
@objc
|
|
597
|
+
private func handleRightBarButton() {
|
|
598
|
+
rightBarButtonConfiguration?.onTap?(currentIndex)
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
extension NativeImagePreviewViewerController: UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout {
|
|
603
|
+
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
|
|
604
|
+
pageCount = dataSource?.numberOfItems() ?? 0
|
|
605
|
+
return pageCount
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
|
|
609
|
+
guard let cell = collectionView.dequeueReusableCell(
|
|
610
|
+
withReuseIdentifier: NativeImagePreviewViewerPageCell.reuseIdentifier,
|
|
611
|
+
for: indexPath
|
|
612
|
+
) as? NativeImagePreviewViewerPageCell else {
|
|
613
|
+
return UICollectionViewCell()
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
if let item = dataSource?.item(at: indexPath.item) {
|
|
617
|
+
cell.pageView.delegate = self
|
|
618
|
+
cell.pageView.configure(
|
|
619
|
+
index: indexPath.item,
|
|
620
|
+
item: item,
|
|
621
|
+
imageLoader: imageLoader,
|
|
622
|
+
mediaContentMode: mediaContentMode,
|
|
623
|
+
sourcePreviewImage: anchorView(for: indexPath.item)?.image
|
|
624
|
+
)
|
|
625
|
+
cell.pageView.setActive(indexPath.item == currentIndex)
|
|
626
|
+
cell.pageView.setVideoRevealAllowed(hasPerformedOpenTransition && indexPath.item == currentIndex)
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
return cell
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
|
|
633
|
+
guard let pageCell = cell as? NativeImagePreviewViewerPageCell else { return }
|
|
634
|
+
pageCell.pageView.setActive(indexPath.item == currentIndex)
|
|
635
|
+
pageCell.pageView.setVideoRevealAllowed(hasPerformedOpenTransition && indexPath.item == currentIndex)
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
func collectionView(_ collectionView: UICollectionView, didEndDisplaying cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
|
|
639
|
+
guard let pageCell = cell as? NativeImagePreviewViewerPageCell else { return }
|
|
640
|
+
pageCell.pageView.setActive(false)
|
|
641
|
+
pageCell.pageView.setVideoRevealAllowed(false)
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
|
|
645
|
+
updateCurrentIndexFromVisiblePage()
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
func scrollViewDidEndScrollingAnimation(_ scrollView: UIScrollView) {
|
|
649
|
+
updateCurrentIndexFromVisiblePage()
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
|
|
653
|
+
if decelerate == false {
|
|
654
|
+
updateCurrentIndexFromVisiblePage()
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
extension NativeImagePreviewViewerController: UIGestureRecognizerDelegate {
|
|
660
|
+
func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
|
|
661
|
+
if gestureRecognizer === panGestureRecognizer {
|
|
662
|
+
recalculateHorizontalPagingEnabled()
|
|
663
|
+
if verticalDismissLocked || verticalDismissNeedsReanchor || isRestoringDismissDrag {
|
|
664
|
+
return false
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
guard let pageView = currentPageView else { return false }
|
|
668
|
+
let velocity = panGestureRecognizer.velocity(in: view)
|
|
669
|
+
let location = panGestureRecognizer.location(in: view.window)
|
|
670
|
+
return pageView.canBeginVerticalDismiss(at: location, velocity: velocity)
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
return true
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
func gestureRecognizer(
|
|
677
|
+
_ gestureRecognizer: UIGestureRecognizer,
|
|
678
|
+
shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer
|
|
679
|
+
) -> Bool {
|
|
680
|
+
gestureRecognizer === panGestureRecognizer
|
|
681
|
+
}
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
extension NativeImagePreviewViewerController: NativeImagePreviewViewerPageViewDelegate {
|
|
685
|
+
func nativeImagePreviewViewerPageViewDidRequestDismiss(_ pageView: NativeImagePreviewViewerPageView) {
|
|
686
|
+
guard pageView.index == currentIndex else { return }
|
|
687
|
+
performDismissTransition()
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
func nativeImagePreviewViewerPageViewDidToggleVideoChrome(_ pageView: NativeImagePreviewViewerPageView) {
|
|
691
|
+
guard pageView.index == currentIndex else { return }
|
|
692
|
+
_ = pageView.toggleVideoControls()
|
|
693
|
+
setNavigationBarHidden(shouldHideNavigationBarForCurrentPage, animated: true)
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
func nativeImagePreviewViewerPageView(_ pageView: NativeImagePreviewViewerPageView, setHorizontalPagingEnabled enabled: Bool) {
|
|
697
|
+
guard pageView.index == currentIndex else { return }
|
|
698
|
+
recalculateHorizontalPagingEnabled()
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
func nativeImagePreviewViewerPageViewDidBeginMultiTouch(_ pageView: NativeImagePreviewViewerPageView) {
|
|
702
|
+
guard pageView.index == currentIndex else { return }
|
|
703
|
+
verticalDismissLocked = true
|
|
704
|
+
verticalDismissNeedsReanchor = false
|
|
705
|
+
if isDraggingToDismiss {
|
|
706
|
+
cancelVerticalDismissDrag()
|
|
707
|
+
}
|
|
708
|
+
recalculateHorizontalPagingEnabled()
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
func nativeImagePreviewViewerPageViewDidEndMultiTouch(_ pageView: NativeImagePreviewViewerPageView) {
|
|
712
|
+
guard pageView.index == currentIndex else { return }
|
|
713
|
+
verticalDismissLocked = false
|
|
714
|
+
verticalDismissNeedsReanchor = true
|
|
715
|
+
recalculateHorizontalPagingEnabled()
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
private final class NativeImagePreviewViewerPageCell: UICollectionViewCell {
|
|
720
|
+
static let reuseIdentifier = "NativeImagePreviewViewerPageCell"
|
|
721
|
+
|
|
722
|
+
let pageView = NativeImagePreviewViewerPageView()
|
|
723
|
+
|
|
724
|
+
override init(frame: CGRect) {
|
|
725
|
+
super.init(frame: frame)
|
|
726
|
+
contentView.addSubview(pageView)
|
|
727
|
+
pageView.pinToSuperviewEdges()
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
required init?(coder: NSCoder) {
|
|
731
|
+
fatalError("init(coder:) has not been implemented")
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
override func prepareForReuse() {
|
|
735
|
+
super.prepareForReuse()
|
|
736
|
+
layer.removeAllAnimations()
|
|
737
|
+
contentView.layer.removeAllAnimations()
|
|
738
|
+
transform = .identity
|
|
739
|
+
contentView.transform = .identity
|
|
740
|
+
alpha = 1
|
|
741
|
+
contentView.alpha = 1
|
|
742
|
+
contentView.center = CGPoint(x: bounds.midX, y: bounds.midY)
|
|
743
|
+
pageView.prepareForReuse()
|
|
744
|
+
}
|
|
745
|
+
}
|