UIScrollView пользовательский размер подкачки - PullRequest
40 голосов
/ 25 июля 2011
Пейджинг

в UIScrollView - это отличная функция, мне нужно здесь установить меньшее расстояние для пейджинга, например, я хочу, чтобы у моего UIScrollView был размер страницы меньше размера рамки UIScrollView.Спасибо

Ответы [ 11 ]

64 голосов
/ 08 ноября 2013

Существует метод делегата UIScrollView, который вы можете использовать. Установите свой класс в качестве делегата представления прокрутки, а затем реализуйте следующее:

- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset
{
    CGFloat kMaxIndex = 23;
    CGFloat targetX = scrollView.contentOffset.x + velocity.x * 60.0;
    CGFloat targetIndex = round(targetX / (kCellWidth + kCellSpacing));
    if (targetIndex < 0)
        targetIndex = 0;
    if (targetIndex > kMaxIndex)
        targetIndex = kMaxIndex;
    targetContentOffset->x = targetIndex * (kCellWidth + kCellSpacing);
}

Параметр скорости необходим для того, чтобы прокрутка ощущалась естественной и не заканчивалась внезапно, когда касание заканчивается, когда палец все еще движется. Ширина ячейки и расстояние между ячейками - это ширина страницы и расстояние между страницами в вашем представлении. В этом случае я использую UICollectionView.

22 голосов
/ 04 октября 2012
  1. Измените размер scrollView на нужный размер страницы
  2. Установите scroll.clipsToBounds = NO
  3. Создайте подкласс UIView (например, HackClipView) и переопределите hitTest: withEvent: method

    -(UIView *) hitTest:(CGPoint) point withEvent:(UIEvent *)event
    {     
        UIView* child = [super hitTest:point withEvent:event]; 
        if (child == self && self.subviews.count > 0)  
        {
            return self.subviews[0];
        }
        return child;
    }
    
  4. Установите HackClipView.clipsToBounds = YES

  5. Поместите свой scrollView в этот HackClipView (с полным желаемым размером прокрутки)

См. этот ответ для получения более подробной информации

Обновление: Как указано в ответе lucius, теперь вы можете реализовать протокол UIScollViewDelegate и использовать - (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset метод.Поскольку targetContentOffset является указателем.Использование этого метода не гарантирует того же результата при просмотре страниц прокрутки, что пользователь может прокручивать сразу несколько страниц.Но установка descelerationRate на fast будет почти даст вам тот же результат

11 голосов
/ 08 февраля 2012

Вы должны отключить пейджинг и добавить UIPanGestureRecognizer в представление прокрутки и обрабатывать пейджинг самостоятельно.

- (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;
}

}

3 голосов
/ 18 сентября 2018

Swift 4.1 решение, которое упрощает повторное использование:

/// 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
    }
}

Просто следуйте протоколу ScrollViewCustomPageSize в вашем делегате UIScrollView / UITableView / UICollectionView, и все готово, например ::

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)
    }
}

Для необычной прокрутки я также рекомендую установить collectionView.decelerationRate = UIScrollViewDecelerationRateFast

1 голос
/ 20 мая 2013

У меня была такая же проблема, поэтому я создал пользовательский UIScrollView. Теперь он доступен на Github, потому что, когда я искал, я не нашел подобных решений. Наслаждайтесь! https://github.com/MartinMetselaar/MMCPSScrollView

MMCPSScrollView* scrollView = [[MMCPSScrollView alloc] initWithFrame:self.view.bounds];
[scrollView setType:MMCPSScrollVertical];
[scrollView setPageHeight:250];
[scrollView setPageSize:2];
[self.view addSubview:scrollView];

Если у вас есть дополнительные вопросы об этом компоненте, просто спросите.

1 голос
/ 25 июля 2011

Установите метод contentOffset in -(void)scrollViewDidScroll:(UIScrollView *)scrollView.

Также см. UIScrollViewDelegate ссылки

0 голосов
/ 23 января 2019

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 голосов
/ 24 января 2018

Самый простой способ - добавить этот код

scrollView.clipsToBounds = false
scrollView.removeGestureRecognizer(scrollView.panGestureRecognizer)
view.addGestureRecognizer(scrollView.panGestureRecognizer)
0 голосов
/ 25 октября 2013

Добавление распознавателей жестов или других подпредставлений и т. Д. Глупо.Просто установите делегата для просмотра прокрутки следующим образом:

// This is for a vertical scrolling scroll view. 
// Let's say you want it to snap to every 160 pixels :    

- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate
{     
     int y = scrollView.contentOffset.y;
     int yOff = y % 160;
     if(yOff < 80)
          y -= yOff;
     else
          y += 160 - yOff;

     [scrollView setContentOffset:CGPointMake(scrollView.contentOffset.x, y) animated:YES];
}

// This is for a horizontal scrolling scroll view.
// Let's say you want the same, to snap to every 160 pixels :

- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate
{
     int x = scrollView.contentOffset.x;
     int xOff = x % 160;
     if(xOff < 80)
          x -= xOff;
     else
          x += 160 - xOff;

     [scrollView setContentOffset:CGPointMake(x, scrollView.contentOffset.y) animated:YES];
}
0 голосов
/ 26 марта 2013

Мне показалось, что это работает намного лучше:

UIScrollView Custom Paging

Здесь они добавляют представление прокрутки (сохраняя при этом правильность подкачки) в качестве подпредставления к ExtendedTouchView или подклассу UIVIew и перезаписывают метод проверки попадания

- (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;
}

Это было именно то, что я хотел, с минимальным кодом и головной болью.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...