Skip to content

Commit eb0d673

Browse files
committed
Merge branch 'release/0.4.8'
2 parents a4040e6 + 3b8064b commit eb0d673

7 files changed

Lines changed: 118 additions & 18 deletions

File tree

Artwork/artwork.sketch

0 Bytes
Binary file not shown.

Artwork/logo.png

411 KB
Loading

Pageboy.podspec

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ Pod::Spec.new do |s|
44
s.platform = :ios, "9.0"
55
s.requires_arc = true
66

7-
s.version = "0.4.7"
7+
s.version = "0.4.8"
88
s.summary = "A simple, highly informative page view controller."
99
s.description = <<-DESC
1010
Pageboy is a page view controller that provides simplified data source management, enhanced delegation and other useful features.

Sources/Pageboy/Info.plist

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
<key>CFBundlePackageType</key>
1616
<string>FMWK</string>
1717
<key>CFBundleShortVersionString</key>
18-
<string>0.4.7</string>
18+
<string>0.4.8</string>
1919
<key>CFBundleVersion</key>
2020
<string>AUTO_GENERATED</string>
2121
<key>NSPrincipalClass</key>

Sources/Pageboy/PageboyScrollDetection.swift

Lines changed: 75 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -69,10 +69,18 @@ extension PageboyViewController: UIPageViewControllerDelegate, UIScrollViewDeleg
6969
contentOffset = scrollView.contentOffset.y
7070
}
7171

72-
let scrollIndexDiff = CGFloat(max(1, abs((self.expectedTransitionIndex ?? currentIndex + 1) - currentIndex)))
73-
let scrollOffset = contentOffset - pageSize
74-
let pageOffset = (CGFloat(currentIndex) * pageSize) + (scrollOffset * scrollIndexDiff)
75-
var pagePosition = pageOffset / pageSize
72+
guard let scrollIndexDiff = self.pageScrollIndexDiff(forCurrentIndex: currentIndex,
73+
expectedIndex: self.expectedTransitionIndex,
74+
currentContentOffset: contentOffset,
75+
pageSize: pageSize) else {
76+
return
77+
}
78+
79+
guard var pagePosition = self.pagePosition(forContentOffset: contentOffset,
80+
pageSize: pageSize,
81+
indexDiff: scrollIndexDiff) else {
82+
return
83+
}
7684

7785
// do not continue if a page change is detected
7886
guard !self.detectCurrentPageIndexIfNeeded(pagePosition: pagePosition,
@@ -117,13 +125,11 @@ extension PageboyViewController: UIPageViewControllerDelegate, UIScrollViewDeleg
117125
///
118126
/// - Parameter pagePosition: the relative page position.
119127
private func detectInfiniteOverscrollIfNeeded(pagePosition: inout CGFloat) {
120-
let maxPagePosition = CGFloat((self.viewControllers?.count ?? 1) - 1)
121-
let overscrolling = pagePosition < 0.0 || pagePosition > maxPagePosition
122-
123-
guard self.isInfiniteScrollEnabled && overscrolling else {
128+
guard self.isInfinitelyScrolling(forPosition: pagePosition) else {
124129
return
125130
}
126131

132+
let maxPagePosition = CGFloat((self.viewControllers?.count ?? 1) - 1)
127133
var integral: Double = 0.0
128134
var progress = CGFloat(modf(fabs(Double(pagePosition)), &integral))
129135
var maxInfinitePosition: CGFloat!
@@ -142,6 +148,20 @@ extension PageboyViewController: UIPageViewControllerDelegate, UIScrollViewDeleg
142148
pagePosition = infinitePagePosition
143149
}
144150

151+
/// Whether a position is infinitely scrolling between end ranges
152+
///
153+
/// - Parameter pagePosition: The position.
154+
/// - Returns: Whether the position is infinitely scrolling.
155+
private func isInfinitelyScrolling(forPosition pagePosition: CGFloat) -> Bool {
156+
let maxPagePosition = CGFloat((self.viewControllers?.count ?? 1) - 1)
157+
let overscrolling = pagePosition < 0.0 || pagePosition > maxPagePosition
158+
159+
guard self.isInfiniteScrollEnabled && overscrolling else {
160+
return false
161+
}
162+
return true
163+
}
164+
145165
/// Detects whether a page boundary has been passed.
146166
/// As pageViewController:didFinishAnimating is not reliable.
147167
///
@@ -184,6 +204,53 @@ extension PageboyViewController: UIPageViewControllerDelegate, UIScrollViewDeleg
184204
}
185205
self.currentIndex = index
186206
}
207+
208+
/// Calculate the expected index diff for a page scroll.
209+
///
210+
/// - Parameters:
211+
/// - index: The current index.
212+
/// - expectedIndex: The target page index.
213+
/// - currentContentOffset: The current content offset.
214+
/// - pageSize: The size of each page.
215+
/// - Returns: The expected index diff.
216+
private func pageScrollIndexDiff(forCurrentIndex index: Int?,
217+
expectedIndex: Int?,
218+
currentContentOffset: CGFloat,
219+
pageSize: CGFloat) -> CGFloat? {
220+
guard let index = index else {
221+
return nil
222+
}
223+
224+
let expectedIndex = expectedIndex ?? index
225+
let expectedDiff = CGFloat(max(1, abs(expectedIndex - index)))
226+
let expectedPosition = self.pagePosition(forContentOffset: currentContentOffset,
227+
pageSize: pageSize,
228+
indexDiff: expectedDiff) ?? CGFloat(index)
229+
230+
guard self.isInfinitelyScrolling(forPosition: expectedPosition) == false else {
231+
return 1
232+
}
233+
return expectedDiff
234+
}
235+
236+
/// Calculate the relative page position.
237+
///
238+
/// - Parameters:
239+
/// - contentOffset: The current contentOffset.
240+
/// - pageSize: The current page size.
241+
/// - indexDiff: The expected difference between current / target page indexes.
242+
/// - Returns: The relative page position.
243+
private func pagePosition(forContentOffset contentOffset: CGFloat,
244+
pageSize: CGFloat,
245+
indexDiff: CGFloat) -> CGFloat? {
246+
guard let currentIndex = self.currentIndex else {
247+
return nil
248+
}
249+
250+
let scrollOffset = contentOffset - pageSize
251+
let pageOffset = (CGFloat(currentIndex) * pageSize) + (scrollOffset * indexDiff)
252+
return pageOffset / pageSize
253+
}
187254
}
188255

189256

Sources/Pageboy/PageboyViewController.swift

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,11 @@ open class PageboyViewController: UIViewController {
167167
/// Whether the page view controller should infinitely scroll at the end of page ranges.
168168
///
169169
/// Default is FALSE.
170-
public var isInfiniteScrollEnabled: Bool = false
170+
public var isInfiniteScrollEnabled: Bool = false {
171+
didSet {
172+
self.reloadCurrentPageSoftly()
173+
}
174+
}
171175

172176
/// The view controllers that are displayed in the page view controller.
173177
public internal(set) var viewControllers: [UIViewController]?
@@ -235,6 +239,15 @@ open class PageboyViewController: UIViewController {
235239
self.reloadPages(reloadViewControllers: true)
236240
}
237241

242+
/// Reload the currently active page into the page view controller if possible.
243+
/// Does not reload from dataSource.
244+
private func reloadCurrentPageSoftly() {
245+
guard let currentIndex = self.currentIndex else { return }
246+
guard let currentViewController = self.viewControllers?[currentIndex] else { return }
247+
248+
self.pageViewController.setViewControllers([currentViewController], direction: .forward, animated: false, completion: nil)
249+
}
250+
238251
//
239252
// MARK: Transitioning
240253
//
@@ -361,3 +374,17 @@ internal extension PageboyViewController {
361374
}
362375
}
363376
}
377+
378+
// MARK: - NavigationDirection: CustomStringConvertible
379+
extension PageboyViewController.NavigationDirection: CustomStringConvertible {
380+
public var description: String {
381+
switch self {
382+
case .forward:
383+
return "Forward"
384+
case .reverse:
385+
return "Reverse"
386+
default:
387+
return "Neutral"
388+
}
389+
}
390+
}

Sources/PageboyTests/PageboyTransitionTests.swift

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -114,9 +114,7 @@ class PageboyTransitionTests: PageboyTests {
114114
self.pageboyViewController.dataSource = self.dataSource
115115

116116
// simulate scroll
117-
let boundsWidth = self.pageboyViewController.view.frame.size.width
118-
self.pageboyViewController.pageViewController.scrollView?.setContentOffset(CGPoint(x: boundsWidth + (boundsWidth / 2.0), y: 0.0),
119-
animated: false)
117+
self.simulateScroll(toPosition: 0.5)
120118

121119
XCTAssert(String(format:"%.1f", self.delegate.lastRecordedPagePosition?.x ?? 0.0) == "0.5" &&
122120
self.pageboyViewController.currentIndex == 0,
@@ -145,9 +143,7 @@ class PageboyTransitionTests: PageboyTests {
145143
self.pageboyViewController.dataSource = self.dataSource
146144

147145
// simulate scroll
148-
let boundsWidth = self.pageboyViewController.view.frame.size.width
149-
self.pageboyViewController.pageViewController.scrollView?.setContentOffset(CGPoint(x: boundsWidth + (boundsWidth / 2.0), y: 0.0),
150-
animated: false)
146+
self.simulateScroll(toPosition: 0.5)
151147

152148
XCTAssert(self.delegate.lastRecordedDirection == .forward && self.pageboyViewController.currentIndex == 0,
153149
"Not reporting partial user interacted transition direction values correctly.")
@@ -197,5 +193,15 @@ class PageboyTransitionTests: PageboyTests {
197193
"Animated flags for an non animated scrollToPage are incorrect.")
198194
}
199195
}
200-
196+
197+
// MARK: Utils
198+
199+
func simulateScroll(toPosition position: CGFloat) {
200+
let targetIndex = Int(position.rounded())
201+
202+
let boundsWidth = self.pageboyViewController.view.frame.size.width
203+
self.pageboyViewController.expectedTransitionIndex = targetIndex
204+
self.pageboyViewController.pageViewController.scrollView?.setContentOffset(CGPoint(x: boundsWidth + (boundsWidth * position), y: 0.0),
205+
animated: false)
206+
}
201207
}

0 commit comments

Comments
 (0)