UIScrollView custom paging size

后端 未结 11 1490
走了就别回头了
走了就别回头了 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:02

    This seemed to work a lot better for me:

    UIScrollView Custom Paging

    Here they are adding the scrollview (keeping it's paging niceness) as a subview to an ExtendedTouchView or subclass of UIVIew and overwriting the hit test method

    - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
    if ([self pointInside:point withEvent:event]) {
        if ([[self subviews] count] > 0) {
            //force return of first child, if exists
            return [[self subviews] objectAtIndex:0];
        } else {
            return self;
        }
    }
    return nil;
    }
    

    This did exactly whatI wanted with minimal code and headache.

    0 讨论(0)
  • 2020-12-07 12:03

    The easiest way is to add this code

    scrollView.clipsToBounds = false
    scrollView.removeGestureRecognizer(scrollView.panGestureRecognizer)
    view.addGestureRecognizer(scrollView.panGestureRecognizer)
    
    0 讨论(0)
  • 2020-12-07 12:06

    You should disable paging and add a UIPanGestureRecognizer to your scroll view and handle the paging yourself.

    - (void)viewDidLoad {
    
        [super viewDidLoad];
        CGRect viewRect = self.view.bounds; // View controller's view bounds
        theScrollView = [[UIScrollView alloc] initWithFrame:viewRect]; 
    
        theScrollView.scrollsToTop      = NO;
        theScrollView.pagingEnabled         = NO;
        theScrollView.delaysContentTouches  = NO;
        theScrollView.delegate = self;
    
        [self.view addSubview:theScrollView];
    
        UIPanGestureRecognizer * peter = [[[UIPanGestureRecognizer alloc] initWithTarget:self  
                                                                                  action:@selector(handlePan:)]
                                           autorelease]; 
        [theScrollView addGestureRecognizer:peter]; 
    
    }
    
    -(void)handlePan:(UIPanGestureRecognizer*)recognizer{
    
        switch (recognizer.state) {
        case UIGestureRecognizerStateBegan:{
            // panStart and startPoint are instance vars for the viewContainer 
            panStart = theScrollView.contentOffset;
            startPoint = [recognizer locationInView:theScrollView]; 
            
            
            break;
        }
        case UIGestureRecognizerStateChanged:{
                        
            CGPoint newPoint = [recognizer locationInView:theScrollView];
            CGFloat delta = startPoint.x - newPoint.x;
            if ( abs(delta) > 2)
                theScrollView.contentOffset = CGPointMake( theScrollView.contentOffset.x + delta, 0); 
            
            CGFloat moveDelta = panStart.x - theScrollView.contentOffset.x;                               
            
    
            // current witdh should hold the currently displayed page/view in theScrollView
            if ( abs(moveDelta) > (currentWidth * 0.40)){
                panStart = theScrollView.contentOffset;
                startPoint = newPoint;
                
                //NSLog(@"delta is bigger"); 
                if ( moveDelta < 0 )
                    [self incrementPageNumber]; // you should implement this method and present the next view
                else 
                    [self decrementPageNumber]; // you should implement this method and present the previous view
       
                recognizer.enabled = NO; // disable further event until view change finish
                     
            }
            
            break; 
        }
            
        case UIGestureRecognizerStateEnded:
        case UIGestureRecognizerStateCancelled:
    
            recognizer.enabled = YES; 
            [self showDocumentPage:currentPage]; 
            
            break;
            
            
        default:
            break;
        }
    }
    
    0 讨论(0)
  • 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<CGPoint>) {
        ///         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<CGPoint>)
    }
    
    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<CGPoint>) {
            targetContentOffset.pointee.x = getTargetContentOffset(scrollView: scrollView, velocity: velocity)
        }
    }
    

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

    0 讨论(0)
  • 2020-12-07 12:12

    Swift 4.1, iOS11+:

    func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
        targetContentOffset.pointee = CGPoint(
            x: round(targetContentOffset.pointee.x / pageWidth) * pageWidth,
            y: targetContentOffset.pointee.y
        )
    }
    
    0 讨论(0)
  • 2020-12-07 12:17

    I had the same problem short ago. My aproach was to add a second UIScrollView to the scrollview. So you can switch to the page. On that page it seems than if the page is bigger than the screen. I hope it works also in your situation. ;-)

    Sandro Meier

    0 讨论(0)
提交回复
热议问题