UIScrollView custom paging size

后端 未结 11 1494
走了就别回头了
走了就别回头了 2020-12-07 11:49

paging in UIScrollView is a great feature, what I need here is to set the paging to a smaller distance, for example I want my UIScrollView to page less size that the UIScrol

11条回答
  •  無奈伤痛
    2020-12-07 12:12

    Swift 4.1 solution that simplifies reusing:

    /// Protocol that simplifies custom page size configuration for UIScrollView.
    /// Sadly, can not be done better due to protocol extensions limitations - https://stackoverflow.com/questions/39487168/non-objc-method-does-not-satisfy-optional-requirement-of-objc-protocol
    /// - note: Set `.decelerationRate` to `UIScrollViewDecelerationRateFast` for a fancy scrolling animation.
    protocol ScrollViewCustomHorizontalPageSize: UIScrollViewDelegate {
        /// Custom page size
        var pageSize: CGFloat { get }
    
        /// Helper method to get current page fraction
        func getCurrentPage(scrollView: UIScrollView) -> CGFloat
    
        /// Helper method to get targetContentOffset. Usage:
        ///
        ///     func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer) {
        ///         targetContentOffset.pointee.x = getTargetContentOffset(scrollView: scrollView, velocity: velocity)
        ///     }
        func getTargetContentOffset(scrollView: UIScrollView, velocity: CGPoint) -> CGFloat
    
        /// Must be implemented. See `getTargetContentOffset` for more info.
        func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer)
    }
    
    extension ScrollViewCustomHorizontalPageSize {
        func getCurrentPage(scrollView: UIScrollView) -> CGFloat {
            return (scrollView.contentOffset.x + scrollView.contentInset.left) / pageSize
        }
    
        func getTargetContentOffset(scrollView: UIScrollView, velocity: CGPoint) -> CGFloat {
            let targetX: CGFloat = scrollView.contentOffset.x + velocity.x * 60.0
    
            var targetIndex = (targetX + scrollView.contentInset.left) / pageSize
            let maxOffsetX = scrollView.contentSize.width - scrollView.bounds.width + scrollView.contentInset.right
            let maxIndex = (maxOffsetX + scrollView.contentInset.left) / pageSize
            if velocity.x > 0 {
                targetIndex = ceil(targetIndex)
            } else if velocity.x < 0 {
                targetIndex = floor(targetIndex)
            } else {
                let (maxFloorIndex, lastInterval) = modf(maxIndex)
                if targetIndex > maxFloorIndex {
                    if targetIndex >= lastInterval / 2 + maxFloorIndex {
                        targetIndex = maxIndex
                    } else {
                        targetIndex = maxFloorIndex
                    }
                } else {
                    targetIndex = round(targetIndex)
                }
            }
    
            if targetIndex < 0 {
                targetIndex = 0
            }
    
            var offsetX = targetIndex * pageSize - scrollView.contentInset.left
            offsetX = min(offsetX, maxOffsetX)
    
            return offsetX
        }
    }
    

    Just conform to ScrollViewCustomPageSize protocol in your UIScrollView/UITableView/UICollectionView delegate and you are done, e.g.:

    extension MyCollectionViewController: ScrollViewCustomPageSize {
        var pageSize: CGFloat {
            return 200
        }
    
        func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer) {
            targetContentOffset.pointee.x = getTargetContentOffset(scrollView: scrollView, velocity: velocity)
        }
    }
    

    For a fancy scrolling I also recommend to set collectionView.decelerationRate = UIScrollViewDecelerationRateFast

提交回复
热议问题