From 5a86fa4aaa3d39944096a52018a12465e2914529 Mon Sep 17 00:00:00 2001 From: Gil Birman Date: Thu, 3 Aug 2023 11:03:18 -0500 Subject: [PATCH 1/3] CONV-1435: Add scroll indicator insets to customScrollViewInsets - Before: customScrollViewInsets only returns insets to adjust contentInset - After: customScrollViewInsets function signature matches calculateScrollViewInsets so you can specify scroll-indicator insets - Why? In the chat demo, the scroll bar was disappearing below the bottom inset of the list. It's likely that if you are modifying contentInset you will have to modify scroll indicator insets as well --- .../Demo Screens/ChatDemoViewController.swift | 15 ++++++++------- .../KeyboardTestingViewController.swift | 6 +++++- ListableUI/Sources/ListView/ListView.swift | 11 +++++++++-- 3 files changed, 22 insertions(+), 10 deletions(-) diff --git a/Demo/Sources/Demos/Demo Screens/ChatDemoViewController.swift b/Demo/Sources/Demos/Demo Screens/ChatDemoViewController.swift index fb1b45d83..51e2de3e3 100644 --- a/Demo/Sources/Demos/Demo Screens/ChatDemoViewController.swift +++ b/Demo/Sources/Demos/Demo Screens/ChatDemoViewController.swift @@ -27,15 +27,16 @@ final class ChatDemoViewController : UIViewController { listView.frame = view.bounds listView.customScrollViewInsets = { [weak self] in - guard let self = self else { return .zero } + guard let self = self else { return (.zero, .zero, .zero) } let inset = max(self.footerHeight, self.keyboardHeight - self.view.safeAreaInsets.bottom) print("new bottom inset:", inset) - return UIEdgeInsets( - top: 0, - left: 0, - bottom: inset, - right: 0 - ) + let insets = UIEdgeInsets( + top: 0, + left: 0, + bottom: inset, + right: 0 + ) + return (insets, .zero, insets) } listView.onKeyboardFrameWillChange = { [weak self] keyboardCurrentFrameProvider, keyboardAnimation in guard let self = self else { return } diff --git a/Demo/Sources/Demos/Demo Screens/KeyboardTestingViewController.swift b/Demo/Sources/Demos/Demo Screens/KeyboardTestingViewController.swift index 29141c5be..9922d5d0a 100644 --- a/Demo/Sources/Demos/Demo Screens/KeyboardTestingViewController.swift +++ b/Demo/Sources/Demos/Demo Screens/KeyboardTestingViewController.swift @@ -27,7 +27,11 @@ final class KeyboardTestingViewController : UIViewController } self.listView.customScrollViewInsets = { [weak self] in - self?.insets ?? .zero + if let insets = self?.insets { + return (insets, insets, insets) + } else { + return (.zero, .zero, .zero) + } } self.listView.onKeyboardFrameWillChange = { [weak self] keyboardCurrentFrameProvider, animation in diff --git a/ListableUI/Sources/ListView/ListView.swift b/ListableUI/Sources/ListView/ListView.swift index 81ef23c03..4be7957e7 100644 --- a/ListableUI/Sources/ListView/ListView.swift +++ b/ListableUI/Sources/ListView/ListView.swift @@ -329,7 +329,11 @@ public final class ListView : UIView /// This callback determines the scroll view's insets only when /// `behavior.keyboardAdjustmentMode` is `.custom` - public var customScrollViewInsets: () -> UIEdgeInsets = { .zero } + public var customScrollViewInsets: () -> ( + content: UIEdgeInsets, + horizontalScroll: UIEdgeInsets, + verticalScroll: UIEdgeInsets + ) = { (.zero, .zero, .zero) } /// Call this to trigger an insets update. /// When the `keyboardAdjustmentMode` is `.custom`, you should set @@ -338,7 +342,10 @@ public final class ListView : UIView public func updateScrollViewInsets() { if case .custom = self.behavior.keyboardAdjustmentMode { - self.collectionView.contentInset = self.customScrollViewInsets() + let insets = self.customScrollViewInsets() + self.collectionView.contentInset = insets.content + self.collectionView.horizontalScrollIndicatorInsets = insets.horizontalScroll + self.collectionView.verticalScrollIndicatorInsets = insets.verticalScroll return } From 8746cb70e5b449b8f4cfa014cb4246921b6c8bab Mon Sep 17 00:00:00 2001 From: Gil Birman Date: Thu, 3 Aug 2023 15:44:13 -0500 Subject: [PATCH 2/3] Refactor updateScrollViewInsets to check for difference before setting customScrollViewInsets result --- ListableUI/Sources/ListView/ListView.swift | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/ListableUI/Sources/ListView/ListView.swift b/ListableUI/Sources/ListView/ListView.swift index 4be7957e7..0b7b1b55c 100644 --- a/ListableUI/Sources/ListView/ListView.swift +++ b/ListableUI/Sources/ListView/ListView.swift @@ -341,18 +341,15 @@ public final class ListView : UIView /// whenever insets require an update. public func updateScrollViewInsets() { + let insets: (content: UIEdgeInsets, horizontalScroll: UIEdgeInsets, verticalScroll: UIEdgeInsets) if case .custom = self.behavior.keyboardAdjustmentMode { - let insets = self.customScrollViewInsets() - self.collectionView.contentInset = insets.content - self.collectionView.horizontalScrollIndicatorInsets = insets.horizontalScroll - self.collectionView.verticalScrollIndicatorInsets = insets.verticalScroll - return + insets = self.customScrollViewInsets() + } else { + insets = self.calculateScrollViewInsets( + with: self.keyboardObserver.currentFrame(in: self) + ) } - let insets = self.calculateScrollViewInsets( - with: self.keyboardObserver.currentFrame(in: self) - ) - if self.collectionView.contentInset != insets.content { self.collectionView.contentInset = insets.content } From 80acc3b841bad2b4060c4cf142e59fd9a12fec1f Mon Sep 17 00:00:00 2001 From: Gil Birman Date: Thu, 3 Aug 2023 15:53:03 -0500 Subject: [PATCH 3/3] refactor to use new type ScrollViewInsets instead of a tuple --- .../Demo Screens/ChatDemoViewController.swift | 4 +- .../KeyboardTestingViewController.swift | 4 +- ListableUI/Sources/ListView/ListView.swift | 39 ++++++++++++++----- 3 files changed, 34 insertions(+), 13 deletions(-) diff --git a/Demo/Sources/Demos/Demo Screens/ChatDemoViewController.swift b/Demo/Sources/Demos/Demo Screens/ChatDemoViewController.swift index 51e2de3e3..84d287e91 100644 --- a/Demo/Sources/Demos/Demo Screens/ChatDemoViewController.swift +++ b/Demo/Sources/Demos/Demo Screens/ChatDemoViewController.swift @@ -27,7 +27,7 @@ final class ChatDemoViewController : UIViewController { listView.frame = view.bounds listView.customScrollViewInsets = { [weak self] in - guard let self = self else { return (.zero, .zero, .zero) } + guard let self = self else { return .init() } let inset = max(self.footerHeight, self.keyboardHeight - self.view.safeAreaInsets.bottom) print("new bottom inset:", inset) let insets = UIEdgeInsets( @@ -36,7 +36,7 @@ final class ChatDemoViewController : UIViewController { bottom: inset, right: 0 ) - return (insets, .zero, insets) + return .init(content: insets, verticalScroll: insets) } listView.onKeyboardFrameWillChange = { [weak self] keyboardCurrentFrameProvider, keyboardAnimation in guard let self = self else { return } diff --git a/Demo/Sources/Demos/Demo Screens/KeyboardTestingViewController.swift b/Demo/Sources/Demos/Demo Screens/KeyboardTestingViewController.swift index 9922d5d0a..0eadd6f3e 100644 --- a/Demo/Sources/Demos/Demo Screens/KeyboardTestingViewController.swift +++ b/Demo/Sources/Demos/Demo Screens/KeyboardTestingViewController.swift @@ -28,9 +28,9 @@ final class KeyboardTestingViewController : UIViewController self.listView.customScrollViewInsets = { [weak self] in if let insets = self?.insets { - return (insets, insets, insets) + return .init(content: insets, verticalScroll: insets) } else { - return (.zero, .zero, .zero) + return .init() } } diff --git a/ListableUI/Sources/ListView/ListView.swift b/ListableUI/Sources/ListView/ListView.swift index 0b7b1b55c..e8b777c16 100644 --- a/ListableUI/Sources/ListView/ListView.swift +++ b/ListableUI/Sources/ListView/ListView.swift @@ -327,13 +327,35 @@ public final class ListView : UIView /// Called whenever a keyboard change is detected public var onKeyboardFrameWillChange: KeyboardFrameWillChangeCallback? + public struct ScrollViewInsets { + /// Insets for the content view + public let content: UIEdgeInsets + + /// Insets for the horizontal scroll bar + public let horizontalScroll: UIEdgeInsets + + /// Insets for the vertical scroll bar + public let verticalScroll: UIEdgeInsets + + /// All values are optional, and default to `.zero` + /// - Parameters: + /// - content: Insets for the content view + /// - horizontalScroll: Insets for the horizontal scroll bar + /// - verticalScroll: Insets for the vertical scroll bar + public init( + content: UIEdgeInsets = .zero, + horizontalScroll: UIEdgeInsets = .zero, + verticalScroll: UIEdgeInsets = .zero + ) { + self.content = content + self.horizontalScroll = horizontalScroll + self.verticalScroll = verticalScroll + } + } + /// This callback determines the scroll view's insets only when /// `behavior.keyboardAdjustmentMode` is `.custom` - public var customScrollViewInsets: () -> ( - content: UIEdgeInsets, - horizontalScroll: UIEdgeInsets, - verticalScroll: UIEdgeInsets - ) = { (.zero, .zero, .zero) } + public var customScrollViewInsets: () -> ScrollViewInsets = { .init() } /// Call this to trigger an insets update. /// When the `keyboardAdjustmentMode` is `.custom`, you should set @@ -341,7 +363,7 @@ public final class ListView : UIView /// whenever insets require an update. public func updateScrollViewInsets() { - let insets: (content: UIEdgeInsets, horizontalScroll: UIEdgeInsets, verticalScroll: UIEdgeInsets) + let insets: ScrollViewInsets if case .custom = self.behavior.keyboardAdjustmentMode { insets = self.customScrollViewInsets() } else { @@ -363,8 +385,7 @@ public final class ListView : UIView } } - func calculateScrollViewInsets(with keyboardFrame : KeyboardFrame?) -> (content: UIEdgeInsets, horizontalScroll: UIEdgeInsets, verticalScroll: UIEdgeInsets) - { + func calculateScrollViewInsets(with keyboardFrame : KeyboardFrame?) -> ScrollViewInsets { let keyboardBottomInset : CGFloat = { guard let keyboardFrame = keyboardFrame else { @@ -401,7 +422,7 @@ public final class ListView : UIView $0.bottom = keyboardBottomInset } - return ( + return .init( content: contentInsets, horizontalScroll: UIEdgeInsets( top: 0,