Skip to content

Commit 21ab601

Browse files
authored
Merge branch 'develop' into feature/SPT-1478/add-interaction-to-message-view
2 parents e396510 + 9c1e698 commit 21ab601

File tree

5 files changed

+192
-26
lines changed

5 files changed

+192
-26
lines changed

.github/workflows/Build.yml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,9 @@ jobs:
2222
- name: Prepare Report
2323
run: make prepare_report
2424
- name: Upload Coverage
25-
uses: codecov/[email protected].0
25+
uses: codecov/[email protected].4
2626
with:
27+
token: ${{ secrets.CODECOV_TOKEN }}
2728
files: build/reports/cobertura.xml
2829
flags: unittests
2930
name: codecov-unit
@@ -45,8 +46,9 @@ jobs:
4546
- name: Prepare Report
4647
run: make prepare_example_report
4748
- name: Upload Coverage
48-
uses: codecov/[email protected].0
49+
uses: codecov/[email protected].4
4950
with:
51+
token: ${{ secrets.CODECOV_TOKEN }}
5052
files: build/reports/cobertura.xml
5153
flags: uitests
5254
name: codecov-ui

Example/ReactiveDataDisplayManager/Collection/CollectionCompositionalViewController/CollectionCompositionalViewController.swift

Lines changed: 20 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -14,16 +14,16 @@ final class CollectionCompositionalViewController: UIViewController {
1414

1515
// MARK: - Typealias
1616

17-
typealias ItemsInvalidationResult = (items: [NSCollectionLayoutVisibleItem], offset: CGPoint, environment: NSCollectionLayoutEnvironment)
17+
typealias ItemsInvalidationResult = CollectionScrollViewDelegateProxyPlugin.ItemsInvalidationResult
1818

1919
// MARK: - Constants
2020

2121
private enum Constants {
2222
static let boundaryItemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), heightDimension: .absolute(50.0))
2323
static let edgeInsets = NSDirectionalEdgeInsets(top: 10, leading: 10, bottom: 10, trailing: 10)
24-
static let fraction: CGFloat = 1.0 / 3.0
25-
static let minScale: CGFloat = 0.7
26-
static let maxScale: CGFloat = 1.1
24+
static let fraction: CGFloat = 1.0 / 2
25+
static let minScale: CGFloat = 0.8
26+
static let maxScale: CGFloat = 1
2727
}
2828

2929
// MARK: - IBOutlets
@@ -35,8 +35,11 @@ final class CollectionCompositionalViewController: UIViewController {
3535
private let prefetcher = NukeImagePrefetcher(placeholder: #imageLiteral(resourceName: "ReactiveLogo"))
3636
private lazy var prefetcherablePlugin: CollectionImagePrefetcherablePlugin = .prefetch(prefetcher: prefetcher)
3737

38+
private let scrrollPlugin = CollectionScrollViewDelegateProxyPlugin()
3839
private lazy var adapter = collectionView.rddm.baseBuilder
3940
.add(plugin: prefetcherablePlugin)
41+
.add(plugin: scrrollPlugin)
42+
.add(plugin: .scrollOnSelect(to: .centeredHorizontally))
4043
.build()
4144

4245
// MARK: - UIViewController
@@ -60,6 +63,18 @@ private extension CollectionCompositionalViewController {
6063
addGridSection()
6164
addCompositeGroupSection()
6265
adapter => .reload
66+
67+
// Handle table offset
68+
scrrollPlugin.didScroll += { collectionView in
69+
print(collectionView.contentOffset.y)
70+
}
71+
72+
// Handle section offset
73+
scrrollPlugin.didScrollCompositionLayoutSection += { [weak self] result in
74+
print(result.offset.x)
75+
result.applyScale(minScale: Constants.minScale, maxScale: Constants.maxScale, aligment: .center)
76+
result.applyCentredPosition(collectionView: self?.collectionView)
77+
}
6378
}
6479

6580
func addAnimationSection() {
@@ -155,30 +170,11 @@ private extension CollectionCompositionalViewController {
155170
// Section
156171
let section = NSCollectionLayoutSection(group: group)
157172
section.contentInsets = NSDirectionalEdgeInsets(top: 30, leading: 0, bottom: 0, trailing: 0)
158-
section.orthogonalScrollingBehavior = .groupPaging
159173
section.boundarySupplementaryItems = [header, footer] // add custom element (footer, header, ....)
160-
section.visibleItemsInvalidationHandler = { [weak self] in self?.handleVisibleItemsInvalidation(($0, $1, $2)) }
174+
section.setHorizontalScroll(type: .groupPagingCentered, with: scrrollPlugin)
161175
return section
162176
}
163177

164-
func handleVisibleItemsInvalidation(_ result: ItemsInvalidationResult) {
165-
// Remove header from cells
166-
let cellWithoutHeaderOrFooter = result.items.filter { $0.representedElementKind == .none }
167-
168-
let contentWidth = result.environment.container.contentSize.width
169-
170-
// Transform cells
171-
cellWithoutHeaderOrFooter.forEach { item in
172-
let height = item.bounds.height / 2
173-
let distanceFromCenter = abs(item.frame.midX - result.offset.x - contentWidth / 2.0)
174-
let scale = max(Constants.maxScale - distanceFromCenter / contentWidth, Constants.minScale)
175-
176-
item.transform = CGAffineTransform(translationX: 0, y: height)
177-
.scaledBy(x: scale, y: scale)
178-
.translatedBy(x: 0, y: -height)
179-
}
180-
}
181-
182178
// Grid section
183179
func gridLayout() -> NSCollectionLayoutSection {
184180
// Header

Source/Collection/Plugins/PluginAction/CollectionScrollViewDelegateProxyPlugin.swift

Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,3 +71,169 @@ public extension BaseCollectionPlugin {
7171
}
7272

7373
}
74+
75+
/// Proxy of `CollectionScrollViewDelegateProxyPlugin` events
76+
#if os(iOS)
77+
@available(iOS 13.0, *)
78+
public extension CollectionScrollViewDelegateProxyPlugin {
79+
80+
// MARK: - Private static properties
81+
82+
private static var didScrollCompositionLayoutSection = BaseEvent<ItemsInvalidationResult>()
83+
84+
// MARK: Nested types
85+
86+
struct ItemsInvalidationResult {
87+
public var items: [NSCollectionLayoutVisibleItem]
88+
public var offset: CGPoint
89+
public var environment: NSCollectionLayoutEnvironment
90+
91+
public init(items: [NSCollectionLayoutVisibleItem],
92+
offset: CGPoint,
93+
environment: NSCollectionLayoutEnvironment) {
94+
self.items = items
95+
self.offset = offset
96+
self.environment = environment
97+
}
98+
}
99+
100+
// MARK: - Ppublic properties
101+
102+
/// - Note: To enable events you should link your `NSCollectionLayoutSection` with `CollectionScrollViewDelegateProxyPlugin` using method `NSCollectionLayoutSection.setHorizontalScroll`
103+
var didScrollCompositionLayoutSection: BaseEvent<ItemsInvalidationResult> {
104+
get {
105+
Self.didScrollCompositionLayoutSection
106+
}
107+
set {
108+
Self.didScrollCompositionLayoutSection = newValue
109+
}
110+
}
111+
112+
}
113+
114+
@available(iOS 13.0, *)
115+
public extension NSCollectionLayoutSection {
116+
117+
/// Set horizontal scroll type
118+
/// - Parameters:
119+
/// - type: Type of horizontal scroll
120+
/// - plugin: Plugin `CollectionScrollViewDelegateProxyPlugin`
121+
func setHorizontalScroll(type: UICollectionLayoutSectionOrthogonalScrollingBehavior,
122+
with plugin: CollectionScrollViewDelegateProxyPlugin? = nil) {
123+
orthogonalScrollingBehavior = type
124+
visibleItemsInvalidationHandler = {
125+
let result = CollectionScrollViewDelegateProxyPlugin.ItemsInvalidationResult(items: $0, offset: $1, environment: $2)
126+
plugin?.didScrollCompositionLayoutSection.invoke(with: result)
127+
}
128+
}
129+
130+
}
131+
132+
// MARK: - CompositionLayout orthogonalScrollingBehavior animations
133+
134+
@available(iOS 13.0, *)
135+
public extension CollectionScrollViewDelegateProxyPlugin.ItemsInvalidationResult {
136+
137+
// MARK: - Private static properties
138+
139+
private static let debouncer = Debouncer(queue: .global(qos: .userInitiated), delay: .milliseconds(100))
140+
private static var oldOffsetX: CGFloat?
141+
142+
// MARK: - Nested types
143+
144+
enum Aligment {
145+
case top, bottom, center
146+
}
147+
148+
// MARK: - Public methods
149+
150+
/// Apply scale to cells
151+
/// - Parameters:
152+
/// - minScale: Minimum scale
153+
/// - maxScale: Maximum scale
154+
/// - aligment: Aligment of cells (top, bottom, center)
155+
func applyScale(minScale: CGFloat, maxScale: CGFloat, aligment: Aligment = .center) {
156+
// Remove header from cells
157+
let cellWithoutHeaderOrFooter = items.filter { $0.representedElementKind == .none }
158+
159+
let contentWidth = environment.container.contentSize.width
160+
161+
// Transform cells
162+
cellWithoutHeaderOrFooter.forEach { item in
163+
let height: CGFloat
164+
switch aligment {
165+
case .top:
166+
height = item.bounds.height / 2
167+
case .bottom:
168+
height = -(item.bounds.height / 2)
169+
case .center:
170+
height = .zero
171+
}
172+
let distanceFromCenter = abs(item.frame.midX - offset.x - contentWidth / 2.0)
173+
let scale = max(maxScale - distanceFromCenter / contentWidth, minScale)
174+
175+
item.transform = CGAffineTransform(translationX: 0, y: -height)
176+
.scaledBy(x: scale, y: scale)
177+
.translatedBy(x: 0, y: height)
178+
}
179+
}
180+
181+
/// Apply centred position to cells
182+
/// - Parameters:
183+
/// - collectionView: CollectionView. Need for scroll to item
184+
func applyCentredPosition(collectionView: UICollectionView?) {
185+
let velocity = collectionView?.panGestureRecognizer.velocity(in: collectionView)
186+
if velocity?.x != 0 {
187+
Self.debouncer.cancel()
188+
}
189+
let (_, indexPath) = getVisibleCenterItem()
190+
guard let indexPath = indexPath, Self.oldOffsetX != offset.x else {
191+
return
192+
}
193+
Self.oldOffsetX = offset.x
194+
Self.debouncer.run { [weak collectionView] in
195+
// waiting for main thread available
196+
DispatchQueue.main.async {
197+
collectionView?.scrollToItem(at: indexPath, at: .centeredHorizontally, animated: true)
198+
}
199+
}
200+
}
201+
202+
}
203+
204+
// MARK: - Private methods
205+
206+
@available(iOS 13.0, *)
207+
extension CollectionScrollViewDelegateProxyPlugin.ItemsInvalidationResult {
208+
209+
private func getVisibleCenterItem() -> (NSCollectionLayoutVisibleItem?, IndexPath?) {
210+
// Step 1: Get the visible cells
211+
let visibleCells = items.filter { $0.representedElementKind == .none }
212+
213+
// Step 2: Calculate the center point
214+
let contentWidth = environment.container.contentSize.width
215+
let contentHeight = environment.container.contentSize.height
216+
let centerX = contentWidth / 2
217+
let centerY = contentHeight / 2
218+
let centerPoint = CGPoint(x: centerX + offset.x,
219+
y: centerY + offset.y)
220+
221+
// Step 3: Iterate through the visible cells to find the center most
222+
var minDistance: CGFloat = .greatestFiniteMagnitude
223+
var centerCell: NSCollectionLayoutVisibleItem?
224+
for cell in visibleCells {
225+
let cellCenter = cell.center
226+
let distance = hypot(centerPoint.x - cellCenter.x, centerPoint.y - cellCenter.y)
227+
if distance < minDistance {
228+
minDistance = distance
229+
centerCell = cell
230+
}
231+
}
232+
233+
// Return the center cell and its indexPath
234+
return (centerCell, centerCell?.indexPath)
235+
}
236+
237+
}
238+
239+
#endif

Source/Collection/Plugins/PluginAction/CollectionScrollablePlugin.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ public extension BaseCollectionPlugin {
4848
/// - parameter scrollProvider: allows you to organize the scroll inside the collection
4949
///
5050
/// For example that the beginning of a new element always appears on the left of the screen.
51+
/// - Warning: Do not work with UICollectionViewCompositionalLayout (use CollectionScrollViewDelegateProxyPlugin with didScrollCompositionLayoutSection event
5152
static func scrollableBehaviour(scrollProvider: CollectionScrollProvider) -> CollectionScrollablePlugin {
5253
.init(scrollProvider: scrollProvider)
5354
}

Source/Table/Manager/ManualTableManager.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -349,6 +349,7 @@ private extension ManualTableManager {
349349

350350
let indexPaths = generators.enumerated().map { IndexPath(row: $0.offset, section: sectionIndex) }
351351

352+
sections.registerAllIfNeeded(with: view, using: registrator)
352353
modifier?.insertSectionsAndRows(at: [sectionIndex: indexPaths], with: animation.value)
353354
}
354355

0 commit comments

Comments
 (0)