From d07064e4029b4e2b0a5251b4e9e8df3ebc30b590 Mon Sep 17 00:00:00 2001 From: Daniele Margutti Date: Thu, 30 Nov 2023 11:08:45 +0100 Subject: [PATCH 1/8] Update README.md with SwiftUI --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index e2849d6..4d31e64 100644 --- a/README.md +++ b/README.md @@ -32,6 +32,7 @@ You can think of it as `UITableView` but with several differences: | ⏱ | Compact code base, less than 1k LOC with no external dependencies. | | 🎯 | Easy to use and extensible APIs set. | | 🧬 | It uses standard UIKit components at its core. No magic, just a combination of `UIScrollView`+`UIStackView`. | +| 🧨 | Support SwiftUI's View and autosizing based upon View's content | | 🐦 | Fully made in Swift 5 from Swift ❥ lovers | ## ❤️ Your Support From 57ee65fb76971dd86bc294474a5c89665c005625 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Fontana?= Date: Tue, 7 May 2024 16:52:57 +0200 Subject: [PATCH 2/8] fix: `ScrollStack` `dispatchRowsVisibilityChangesTo(_:)` calls and implementation Also move back `ScrollStackRow` call to `askForCutomizedSizeOfContentView(animated:)` in `updateConstraints()` method. --- .../ScrollStackController/ScrollStack.swift | 57 ++++++++++++------- .../ScrollStackRow.swift | 5 +- 2 files changed, 41 insertions(+), 21 deletions(-) diff --git a/Sources/ScrollStackController/ScrollStack.swift b/Sources/ScrollStackController/ScrollStack.swift index 992f3a3..737ba3d 100644 --- a/Sources/ScrollStackController/ScrollStack.swift +++ b/Sources/ScrollStackController/ScrollStack.swift @@ -860,6 +860,8 @@ open class ScrollStack: UIScrollView, UIScrollViewDelegate { return createRow(newRow, at: index, cellToRemove: cellToRemove, animated: animated, completion: completion) } + private var rowVisibilityChangesDispatchWorkItem: DispatchWorkItem? + /// Private implementation to add new row. private func createRow(_ newRow: ScrollStackRow, at index: Int, cellToRemove: ScrollStackRow?, @@ -878,7 +880,21 @@ open class ScrollStack: UIScrollView, UIScrollViewDelegate { }, completion: nil) } - scrollViewDidScroll(self) + if rowVisibilityChangesDispatchWorkItem == nil { + + rowVisibilityChangesDispatchWorkItem = DispatchWorkItem(block: { [weak self] in + if let stackDelegate = self?.stackDelegate { + self?.dispatchRowsVisibilityChangesTo(stackDelegate) + } + + self?.rowVisibilityChangesDispatchWorkItem = nil + }) + + /// Schedule a single `dispatchRowsVisibilityChangesTo(_:)` call. + /// + /// In this way, when rows are created inside a for-loop, the delegate is called only once after the `ScrollStack` has been fully layed out. + DispatchQueue.main.async(execute: rowVisibilityChangesDispatchWorkItem!) + } return newRow } @@ -979,25 +995,27 @@ open class ScrollStack: UIScrollView, UIScrollViewDelegate { } private func dispatchRowsVisibilityChangesTo(_ delegate: ScrollStackControllerDelegate) { - delegate.scrollStackDidScroll(self, offset: contentOffset) - rows.enumerated().forEach { (idx, row) in let current = isRowVisible(index: idx) - if let previous = prevVisibilityState[row] { - switch (previous, current) { - case (.offscreen, .partial), // row will become invisible - (.hidden, .partial), - (.hidden, .entire): - delegate.scrollStackRowDidBecomeVisible(self, row: row, index: idx, state: current) - - case (.partial, .offscreen), // row will become visible - (.partial, .hidden), - (.entire, .hidden): - delegate.scrollStackRowDidBecomeHidden(self, row: row, index: idx, state: current) - - default: - break - } + let previous = prevVisibilityState[row] + + switch (previous, current) { + case (.offscreen, .partial), // row will become visible + (nil, .entire), + (nil, .partial), + (.partial, .entire), + (.hidden, .partial), + (.hidden, .entire): + delegate.scrollStackRowDidBecomeVisible(self, row: row, index: idx, state: current) + + case (.partial, .offscreen), // row will become invisible + (.entire, .partial), + (.partial, .hidden), + (.entire, .hidden): + delegate.scrollStackRowDidBecomeHidden(self, row: row, index: idx, state: current) + + default: + break } // store previous state @@ -1062,7 +1080,8 @@ open class ScrollStack: UIScrollView, UIScrollViewDelegate { guard let stackDelegate = stackDelegate else { return } - + stackDelegate.scrollStackDidScroll(self, offset: contentOffset) + dispatchRowsVisibilityChangesTo(stackDelegate) } diff --git a/Sources/ScrollStackController/ScrollStackRow.swift b/Sources/ScrollStackController/ScrollStackRow.swift index 03be664..b334712 100644 --- a/Sources/ScrollStackController/ScrollStackRow.swift +++ b/Sources/ScrollStackController/ScrollStackRow.swift @@ -239,10 +239,11 @@ open class ScrollStackRow: UIView, UIGestureRecognizerDelegate { setNeedsUpdateConstraints() } - open override func layoutSubviews() { - super.layoutSubviews() + open override func updateConstraints() { // called the event to update the height of the row. askForCutomizedSizeOfContentView(animated: false) + + super.updateConstraints() } private func applyParentStackAttributes() { From 6213415049df2a0089126f683ae5c6fe7d17214d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Fontana?= Date: Tue, 7 May 2024 17:30:55 +0200 Subject: [PATCH 3/8] doc: fix comment --- Sources/ScrollStackController/ScrollStack.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/ScrollStackController/ScrollStack.swift b/Sources/ScrollStackController/ScrollStack.swift index 737ba3d..ec58435 100644 --- a/Sources/ScrollStackController/ScrollStack.swift +++ b/Sources/ScrollStackController/ScrollStack.swift @@ -892,7 +892,7 @@ open class ScrollStack: UIScrollView, UIScrollViewDelegate { /// Schedule a single `dispatchRowsVisibilityChangesTo(_:)` call. /// - /// In this way, when rows are created inside a for-loop, the delegate is called only once after the `ScrollStack` has been fully layed out. + /// In this way, when rows are created inside a for-loop, the delegate is called only once after the `ScrollStack` has been fully laid out. DispatchQueue.main.async(execute: rowVisibilityChangesDispatchWorkItem!) } From 71363b2a79845e081fcffba35dc3554d520bc432 Mon Sep 17 00:00:00 2001 From: Daniele Margutti Date: Mon, 20 May 2024 16:48:46 +0200 Subject: [PATCH 4/8] Set 1.7.0 as release for podspec --- ScrollStackController.podspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ScrollStackController.podspec b/ScrollStackController.podspec index 00eb216..9333c52 100644 --- a/ScrollStackController.podspec +++ b/ScrollStackController.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "ScrollStackController" - s.version = "1.6.0" + s.version = "1.7.0" s.summary = "Create complex scrollable layout using UIViewController and simplify your code" s.homepage = "https://github.com/malcommac/ScrollStackController" s.license = { :type => "MIT", :file => "LICENSE" } From 7de2809ac9ce256db7b849b7f000c080db632fe5 Mon Sep 17 00:00:00 2001 From: Marco Del Giudice Date: Thu, 30 May 2024 18:42:45 +0200 Subject: [PATCH 5/8] =?UTF-8?q?Add=20visibility=20percentage=20to=20partia?= =?UTF-8?q?l=20Rowv=C3=ACVisibility=20case?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Sources/ScrollStackController/ScrollStack.swift | 8 +++++++- .../Support/ScrollStack+Protocols.swift | 4 ++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/Sources/ScrollStackController/ScrollStack.swift b/Sources/ScrollStackController/ScrollStack.swift index ec58435..2d2caa0 100644 --- a/Sources/ScrollStackController/ScrollStack.swift +++ b/Sources/ScrollStackController/ScrollStack.swift @@ -794,7 +794,13 @@ open class ScrollStack: UIScrollView, UIScrollViewDelegate { return .offscreen } - return (bounds.contains(rowFrame) ? .entire : .partial) + if bounds.contains(rowFrame) { + return .entire + } else { + let intersection = bounds.intersection(rowFrame) + let intersectionPercentage = ((intersection.width * intersection.height) / (rowFrame.width * rowFrame.height)) * 100 + return .partial(percentage: intersectionPercentage) + } } /// Remove passed row from stack view. diff --git a/Sources/ScrollStackController/Support/ScrollStack+Protocols.swift b/Sources/ScrollStackController/Support/ScrollStack+Protocols.swift index 204ac98..2aceebd 100644 --- a/Sources/ScrollStackController/Support/ScrollStack+Protocols.swift +++ b/Sources/ScrollStackController/Support/ScrollStack+Protocols.swift @@ -177,9 +177,9 @@ public extension ScrollStack { /// - `hidden`: row is invisible and hidden. /// - `offscreen`: row is not hidden but currently offscreen due to scroll position. /// - `removed`: row is removed manually. - enum RowVisibility { + enum RowVisibility: Equatable { case hidden - case partial + case partial(percentage: Double) case entire case offscreen case removed From be6fe363d0a67045336f65a6d53a6395460f71a1 Mon Sep 17 00:00:00 2001 From: Daniele Margutti Date: Fri, 31 May 2024 13:04:50 +0200 Subject: [PATCH 6/8] Added 1.7.1 to podspec along with Swift 5.10 --- ScrollStackController.podspec | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ScrollStackController.podspec b/ScrollStackController.podspec index 9333c52..14b0645 100644 --- a/ScrollStackController.podspec +++ b/ScrollStackController.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "ScrollStackController" - s.version = "1.7.0" + s.version = "1.7.1" s.summary = "Create complex scrollable layout using UIViewController and simplify your code" s.homepage = "https://github.com/malcommac/ScrollStackController" s.license = { :type => "MIT", :file => "LICENSE" } @@ -10,5 +10,5 @@ Pod::Spec.new do |s| s.source = { :git => "https://github.com/malcommac/ScrollStackController.git", :tag => s.version.to_s } s.frameworks = "Foundation", "UIKit" s.source_files = 'Sources/**/*.swift' - s.swift_versions = ['5.0', '5.1', '5.3', '5.4', '5.5', '5.7', '5.8', '5.9'] + s.swift_versions = ['5.0', '5.1', '5.3', '5.4', '5.5', '5.7', '5.8', '5.9', '5.10'] end From 0c355030ef55bed99fb88c95a824ddc184aa83b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Fontana?= Date: Wed, 13 Nov 2024 09:59:24 +0100 Subject: [PATCH 7/8] feat: add support for `scrollViewDidEndScrollingAnimation(_:)` delegate method --- Sources/ScrollStackController/ScrollStack.swift | 8 ++++++++ .../Support/ScrollStack+Protocols.swift | 5 +++++ 2 files changed, 13 insertions(+) diff --git a/Sources/ScrollStackController/ScrollStack.swift b/Sources/ScrollStackController/ScrollStack.swift index 992f3a3..c46307c 100644 --- a/Sources/ScrollStackController/ScrollStack.swift +++ b/Sources/ScrollStackController/ScrollStack.swift @@ -1066,6 +1066,14 @@ open class ScrollStack: UIScrollView, UIScrollViewDelegate { dispatchRowsVisibilityChangesTo(stackDelegate) } + public func scrollViewDidEndScrollingAnimation(_ scrollView: UIScrollView) { + guard let stackDelegate = stackDelegate else { + return + } + + stackDelegate.scrollStackDidEndScrollingAnimation(self) + } + open override func layoutSubviews() { super.layoutSubviews() diff --git a/Sources/ScrollStackController/Support/ScrollStack+Protocols.swift b/Sources/ScrollStackController/Support/ScrollStack+Protocols.swift index 204ac98..896c2c0 100644 --- a/Sources/ScrollStackController/Support/ScrollStack+Protocols.swift +++ b/Sources/ScrollStackController/Support/ScrollStack+Protocols.swift @@ -68,6 +68,11 @@ public protocol ScrollStackControllerDelegate: AnyObject { /// - Parameter offset: current scroll offset. func scrollStackDidScroll(_ stackView: ScrollStack, offset: CGPoint) + /// Tells the delegate when a scrolling animation in the scroll view concludes. + /// + /// - Parameter stackView: The ScrollStack object that’s performing the scrolling animation. + func scrollStackDidEndScrollingAnimation(_ stackView: ScrollStack) + /// Row did become partially or entirely visible. /// /// - Parameter row: target row. From b47668b5a1c475118d4c18745b73432417d9544a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Fontana?= Date: Wed, 13 Nov 2024 15:33:42 +0100 Subject: [PATCH 8/8] chore: update README --- README.md | 50 +++++++++++++++++++++++++++----------------------- 1 file changed, 27 insertions(+), 23 deletions(-) diff --git a/README.md b/README.md index 4d31e64..7de0307 100644 --- a/README.md +++ b/README.md @@ -634,33 +634,37 @@ Example: ```swift class ViewController: ScrollStackController, ScrollStackControllerDelegate { - - func viewDidLoad() { - super.viewDidLoad() - - self.scrollStack.stackDelegate = self - } - - func scrollStackDidScroll(_ stackView: ScrollStack, offset: CGPoint) { - // stack did scroll - } - - func scrollStackRowDidBecomeVisible(_ stackView: ScrollStack, row: ScrollStackRow, index: Int, state: ScrollStack.RowVisibility) { - // Row did become partially or entirely visible. - } + + func viewDidLoad() { + super.viewDidLoad() + + self.scrollStack.stackDelegate = self + } + + func scrollStackDidScroll(_ stackView: ScrollStack, offset: CGPoint) { + // Stack did scroll + } - func scrollStackRowDidBecomeHidden(_ stackView: ScrollStack, row: ScrollStackRow, index: Int, state: ScrollStack.RowVisibility) { - // Row did become partially or entirely invisible. - } + + func scrollStackDidEndScrollingAnimation(_ stackView: ScrollStack) { + // Scrolling animation has ended + } - func scrollStackDidUpdateLayout(_ stackView: ScrollStack) { - // This function is called when layout is updated (added, removed, hide or show one or more rows). - } + func scrollStackRowDidBecomeVisible(_ stackView: ScrollStack, row: ScrollStackRow, index: Int, state: ScrollStack.RowVisibility) { + // Row did become partially or entirely visible. + } - func scrollStackContentSizeDidChange(_ stackView: ScrollStack, from oldValue: CGSize, to newValue: CGSize) { - // This function is called when content size of the stack did change (remove/add, hide/show rows). - } + func scrollStackRowDidBecomeHidden(_ stackView: ScrollStack, row: ScrollStackRow, index: Int, state: ScrollStack.RowVisibility) { + // Row did become partially or entirely invisible. + } + func scrollStackDidUpdateLayout(_ stackView: ScrollStack) { + // This function is called when layout is updated (added, removed, hide or show one or more rows). + } + + func scrollStackContentSizeDidChange(_ stackView: ScrollStack, from oldValue: CGSize, to newValue: CGSize) { + // This function is called when content size of the stack did change (remove/add, hide/show rows). + } } ```