Подтвердить что ты не робот

Размер пользовательского подкачки UIScrollView

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

4b9b3361

Ответ 1

Существует метод делегата 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 = 0.0;
    if (velocity.x > 0) {
        targetIndex = ceil(targetX / (kCellWidth + kCellSpacing));
    } else if (velocity.x == 0) {
        targetIndex = round(targetX / (kCellWidth + kCellSpacing));
    } else if (velocity.x < 0) {
        targetIndex = floor(targetX / (kCellWidth + kCellSpacing));
    }
    if (targetIndex < 0)
        targetIndex = 0;
    if (targetIndex > kMaxIndex)
        targetIndex = kMaxIndex;
    targetContentOffset->x = targetIndex * (kCellWidth + kCellSpacing);
    //scrollView.decelerationRate = UIScrollViewDecelerationRateFast;//uncomment this for faster paging
}

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

Ответ 2

  • Измените размер scrollView на желаемый размер страницы.
  • Установите scroll.clipsToBounds = NO
  • Создайте подкласс 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;
    }
    
  • Установите HackClipView.clipsToBounds = YES

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

Подробнее см. этот ответ

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

Ответ 3

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

- (void)viewDidLoad {

    [super viewDidLoad];
    CGRect viewRect = self.view.bounds; // View controller 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;
}

}

Ответ 4

Решение 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 протоколу ScrollViewCustomPageSize в вашем 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

Ответ 6

У меня была такая же проблема, поэтому я создал пользовательский 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];

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

Ответ 7

У меня была одна и та же проблема. Мой aproach должен был добавить второй UIScrollView в scrollview. Таким образом, вы можете перейти на страницу. На этой странице кажется, что если страница больше экрана. Надеюсь, это сработает и в вашей ситуации.; -)

Сандро Мейер

Ответ 8

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

Пользовательский пейджинг UIScrollView

Здесь они добавляют scrollview (сохраняя его пейджинговую привлекательность) в качестве подзаголовка в 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;
}

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

Ответ 9

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

// This is for a vertical scrolling scroll view. 
// Let 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 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];
}

Ответ 10

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

Ответ 11

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

scrollView.clipsToBounds = false
scrollView.removeGestureRecognizer(scrollView.panGestureRecognizer)
view.addGestureRecognizer(scrollView.panGestureRecognizer)