mirror of
https://github.com/divkit/divkit.git
synced 2026-06-06 20:07:59 +00:00
Fixed a bug when scrolling from page to another page.
7ecf33a967461348eea94543f1068150f14e4e66
This commit is contained in:
@@ -18,7 +18,7 @@ extension ScrollableContentPager: ScrollDelegate {
|
||||
: targetContentOffset.pointee.y
|
||||
if let resultOffset = targetPageOffset(
|
||||
forProposedOffset: proposedOffset,
|
||||
isHorizontal: isHorizontal
|
||||
velocity: velocity.projection(isHorizontal)
|
||||
) {
|
||||
if isHorizontal {
|
||||
targetContentOffset.pointee.x = resultOffset
|
||||
|
||||
@@ -20,69 +20,53 @@ public final class ScrollableContentPager: NSObject {
|
||||
currentPageIndex = nil
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public func setInitialOffset(_ offset: CGFloat) {
|
||||
guard isPagingEnabled else { return }
|
||||
|
||||
if currentPageIndex == nil, lastContentOffset == nil, indexedPageOrigins.count > 0 {
|
||||
let initialPageIndex = pageIndex(forOffset: offset)
|
||||
let pageOrigin = indexedPageOrigins[initialPageIndex].origin
|
||||
let initialContentOffset = isHorizontal ? CGPoint(x: pageOrigin, y: 0) : CGPoint(x: 0, y: pageOrigin)
|
||||
|
||||
currentPageIndex = initialPageIndex
|
||||
lastContentOffset = initialContentOffset
|
||||
}
|
||||
|
||||
currentPageIndex = pageIndex(forOffset: offset)
|
||||
}
|
||||
|
||||
public private(set) var currentPageIndex: Int?
|
||||
public private(set) var lastContentOffset: CGPoint?
|
||||
|
||||
public func targetPageOffset(forProposedOffset offset: CGFloat, isHorizontal: Bool) -> CGFloat? {
|
||||
guard currentPageIndex != nil,
|
||||
var newLastContentOffset = lastContentOffset else {
|
||||
return nil
|
||||
}
|
||||
|
||||
public func targetPageOffset(forProposedOffset offset: CGFloat, velocity: CGFloat) -> CGFloat? {
|
||||
guard let startPageIndex = currentPageIndex else { return nil }
|
||||
|
||||
var resultPageIndex = pageIndex(forOffset: offset)
|
||||
|
||||
var forcedIndexOffset = 0
|
||||
if newLastContentOffset.forceIndexOffset(isHorizontal: isHorizontal).isApproximatelyNotEqualTo(offset) {
|
||||
forcedIndexOffset = newLastContentOffset.forceIndexOffset(isHorizontal: isHorizontal) < offset ? 1 : 0
|
||||
if resultPageIndex == startPageIndex, velocity.isApproximatelyNotEqualTo(0) {
|
||||
let forcedIndexOffset = (velocity > 0 ? 1 : -1)
|
||||
resultPageIndex += forcedIndexOffset
|
||||
}
|
||||
resultPageIndex += forcedIndexOffset
|
||||
|
||||
resultPageIndex = clamp(
|
||||
resultPageIndex,
|
||||
min: 0,
|
||||
max: indexedPageOrigins.count - 1
|
||||
)
|
||||
|
||||
let targetPageOffset = indexedPageOrigins[resultPageIndex].origin
|
||||
if isHorizontal {
|
||||
newLastContentOffset.x = targetPageOffset
|
||||
} else {
|
||||
newLastContentOffset.y = targetPageOffset
|
||||
}
|
||||
lastContentOffset = newLastContentOffset
|
||||
|
||||
currentPageIndex = resultPageIndex
|
||||
return targetPageOffset
|
||||
return indexedPageOrigins[resultPageIndex].origin
|
||||
}
|
||||
|
||||
|
||||
private func pageIndex(forOffset offset: CGFloat) -> Int {
|
||||
guard indexedPageOrigins.first(where: {
|
||||
Int(round(intermediatePageIndex(forOffset: offset)))
|
||||
}
|
||||
|
||||
private func intermediatePageIndex(forOffset offset: CGFloat) -> Float {
|
||||
guard let rightBound = indexedPageOrigins.first(where: {
|
||||
$0.origin.isApproximatelyGreaterOrEqualThan(offset)
|
||||
}) != nil else { return indexedPageOrigins.count - 1 }
|
||||
|
||||
}) else { return Float(indexedPageOrigins.count - 1) }
|
||||
|
||||
guard let leftBound = indexedPageOrigins.reversed().first(where: {
|
||||
$0.origin.isApproximatelyLessOrEqualThan(offset)
|
||||
}) else { return 0 }
|
||||
|
||||
return leftBound.index
|
||||
|
||||
guard rightBound.index != leftBound.index,
|
||||
!rightBound.origin.isApproximatelyEqualTo(leftBound.origin) else {
|
||||
return Float(leftBound.index)
|
||||
}
|
||||
|
||||
let relativePart = (offset - leftBound.origin) / (rightBound.origin - leftBound.origin)
|
||||
return Float(leftBound.index) + Float(relativePart)
|
||||
}
|
||||
}
|
||||
|
||||
extension CGPoint {
|
||||
fileprivate func forceIndexOffset(isHorizontal: Bool) -> CGFloat {
|
||||
isHorizontal ? self.x : self.y
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,13 +3,12 @@ import XCTest
|
||||
import LayoutKit
|
||||
|
||||
final class ScrollableContentPagerTests: XCTestCase {
|
||||
private var pager = ScrollableContentPager()
|
||||
private let pager = ScrollableContentPager()
|
||||
private var isHorizontal = true
|
||||
|
||||
override func invokeTest() {
|
||||
for isHorizontal in [false, true] {
|
||||
self.isHorizontal = isHorizontal
|
||||
pager = ScrollableContentPager()
|
||||
super.invokeTest()
|
||||
}
|
||||
}
|
||||
@@ -21,15 +20,15 @@ final class ScrollableContentPagerTests: XCTestCase {
|
||||
func test_WhenMovingForward_SnapsToNextPage() {
|
||||
pager.setInitialOffset(20)
|
||||
|
||||
let resultOffset = pager.targetPageOffset(forProposedOffset: 170, isHorizontal: isHorizontal)!
|
||||
let resultOffset = pager.targetPageOffset(forProposedOffset: 170, velocity: 0.1)!
|
||||
|
||||
XCTAssertEqual(resultOffset, 250)
|
||||
XCTAssertEqual(resultOffset, 100)
|
||||
}
|
||||
|
||||
func test_WhenMovingForwardAndResultPageIsSame_ForcesToNextPage() {
|
||||
pager.setInitialOffset(20)
|
||||
|
||||
let resultOffset = pager.targetPageOffset(forProposedOffset: 49, isHorizontal: isHorizontal)!
|
||||
let resultOffset = pager.targetPageOffset(forProposedOffset: 49, velocity: 0.1)!
|
||||
|
||||
XCTAssertEqual(resultOffset, 100)
|
||||
}
|
||||
@@ -37,7 +36,7 @@ final class ScrollableContentPagerTests: XCTestCase {
|
||||
func test_WhenMovingBackward_SnapsToPreviousPage() {
|
||||
pager.setInitialOffset(260)
|
||||
|
||||
let resultOffset = pager.targetPageOffset(forProposedOffset: 49, isHorizontal: isHorizontal)!
|
||||
let resultOffset = pager.targetPageOffset(forProposedOffset: 49, velocity: -0.1)!
|
||||
|
||||
XCTAssertEqual(resultOffset, 0) // over one page
|
||||
}
|
||||
@@ -45,7 +44,7 @@ final class ScrollableContentPagerTests: XCTestCase {
|
||||
func test_WhenMovingBackwardAndResultPageIsSame_ForcesToPreviousPage() {
|
||||
pager.setInitialOffset(110)
|
||||
|
||||
let resultOffset = pager.targetPageOffset(forProposedOffset: 51, isHorizontal: isHorizontal)!
|
||||
let resultOffset = pager.targetPageOffset(forProposedOffset: 51, velocity: -0.1)!
|
||||
|
||||
XCTAssertEqual(resultOffset, 0)
|
||||
}
|
||||
@@ -53,7 +52,7 @@ final class ScrollableContentPagerTests: XCTestCase {
|
||||
func test_WhenBouncingLeft_SnapsToFirstPage() {
|
||||
pager.setInitialOffset(110)
|
||||
|
||||
let resultOffset = pager.targetPageOffset(forProposedOffset: -10, isHorizontal: isHorizontal)!
|
||||
let resultOffset = pager.targetPageOffset(forProposedOffset: -10, velocity: -0.1)!
|
||||
|
||||
XCTAssertEqual(resultOffset, 0)
|
||||
}
|
||||
@@ -61,7 +60,7 @@ final class ScrollableContentPagerTests: XCTestCase {
|
||||
func test_WhenBouncingRight_SnapsToLastPage() {
|
||||
pager.setInitialOffset(300)
|
||||
|
||||
let resultOffset = pager.targetPageOffset(forProposedOffset: 400, isHorizontal: isHorizontal)!
|
||||
let resultOffset = pager.targetPageOffset(forProposedOffset: 400, velocity: 0.1)!
|
||||
|
||||
XCTAssertEqual(resultOffset, 300)
|
||||
}
|
||||
@@ -70,13 +69,13 @@ final class ScrollableContentPagerTests: XCTestCase {
|
||||
pager.setPageOrigins([100, 250], withPagingEnabled: false, isHorizontal: isHorizontal)
|
||||
pager.setInitialOffset(100)
|
||||
|
||||
let resultOffset = pager.targetPageOffset(forProposedOffset: 150, isHorizontal: isHorizontal)
|
||||
let resultOffset = pager.targetPageOffset(forProposedOffset: 150, velocity: -1)
|
||||
|
||||
XCTAssertNil(resultOffset)
|
||||
}
|
||||
|
||||
func test_WithNoInitialOffset_TargetPageOffsetFails() {
|
||||
let resultOffset = pager.targetPageOffset(forProposedOffset: 150, isHorizontal: isHorizontal)
|
||||
let resultOffset = pager.targetPageOffset(forProposedOffset: 150, velocity: 1)
|
||||
|
||||
XCTAssertNil(resultOffset)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user