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

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

Мой вопрос связан с пользовательской формой пейджинга, которую я пытаюсь сделать с помощью скроллера, и это проще визуализировать, если вы сначала рассмотрите тип прокрутки, реализованный в игровом автомате.

Итак, мой UIScrollView имеет ширину 100 пикселей. Предположим, что он содержит 3 внутренних вида, каждый с шириной 30 пикселей, так что они разделены шириной 3 пикселя. Тип пейджинга, который я хотел бы достичь, таков, что каждая страница является одним из моих представлений (30 пикселей), а не всей шириной прокрутки.

Я знаю, что обычно, если представление занимает всю ширину прокрутки, а пейджинг включен, тогда все работает. Однако в моем пользовательском пейджинге я также хочу, чтобы окружающие представления в представлении прокрутки также были видимыми.

Как мне это сделать?

4b9b3361

Ответ 1

Я просто сделал это для другого проекта. Что вам нужно сделать, так это поместить UIScrollView в пользовательскую реализацию UIView. Я создал класс для этого имени ExtendedHitAreaViewController. ExtendedHitAreaView переопределяет функцию hitTest, чтобы вернуть свой первый дочерний объект, который будет вашим видом прокрутки.

В вашем представлении прокрутки должен быть требуемый размер страницы, т.е. 30px с clipToBounds = NO. Окно расширенной области хита должно быть полным размером области, которую вы хотите видеть, с клипамиToBounds = YES.

Добавьте представление прокрутки в виде подвью в расширенный вид области просмотра, а затем добавьте вид расширенной области попадания в представление viewcontroller.

@implementation ExtendedHitAreaViewContainer

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

Ответ 2

Так как iOS 5 есть этот метод делегата: - (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset.

Итак, вы можете сделать что-то вроде этого:

- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint
*)targetContentOffset {
    if (scrollView == self.scrollView) {
        CGFloat x = targetContentOffset->x;
        x = roundf(x / 30.0f) * 30.0f;
        targetContentOffset->x = x;
    } 
}

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

Ответ 3

У меня была та же проблема, и это отлично поработало для меня, протестировано на iOS 8.

- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView
                 withVelocity:(CGPoint)velocity
          targetContentOffset:(inout CGPoint *)targetContentOffset
{
    NSInteger index = lrint(targetContentOffset->x/_pageWidth);
    NSInteger currentPage = lrint(scrollView.contentOffset.x/_pageWidth);

    if(index == currentPage) {
        if(velocity.x > 0)
            index++;
        else if(velocity.x < 0)
            index--;
    }

    targetContentOffset->x = index * _pageWidth;
}

Мне нужно было проверить скорость и всегда перейти на следующую/предыдущую страницу, если скорость не была равна нулю, иначе это даст неанимированные прыжки при очень коротких и быстрых ошибках.

Обновление:. Это работает еще лучше:

- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView
                 withVelocity:(CGPoint)velocity
          targetContentOffset:(inout CGPoint *)targetContentOffset
{
    CGFloat index = targetContentOffset->x/channelScrollWidth;

    if(velocity.x > 0)
        index = ceil(index);
    else if(velocity.x < 0)
        index = floor(index);
    else
        index = round(index);

    targetContentOffset->x = index * channelScrollWidth;
}

Для горизонтального прокрутки используйте y вместо x для вертикального.

Ответ 4

После нескольких дней исследований и устранения неполадок я придумал что-то, что работает для меня!

Сначала вам нужно подклассифицировать представление, в котором находится прокрутка, и переопределить этот метод следующим образом:

    -(UIView*)hitTest:(CGPoint)point withEvent:(UIEvent*)event {
          UIView* child = nil;
          if ((child = [super hitTest:point withEvent:event]) == self)
                 return (UIView *)_calloutCell;
           return child;
    }

Затем все волшебство происходит в методах делегата scrollview

    -(void)scrollViewWillBeginDragging:(UIScrollView *)scrollView {
            //_lastOffset is declared in the header file
            //@property (nonatomic) CGPoint lastOffset;
            _lastOffset = scrollView.contentOffset;
     }

    - (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset {

            CGPoint currentOffset = scrollView.contentOffset;
            CGPoint newOffset = CGPointZero;

            if (_lastOffset.x < currentOffset.x) {
                // right to left
                newOffset.x = _lastOffset.x + 298;
            }
            else {
                 // left to right
                 newOffset.x = _lastOffset.x - 298;
            }

            [UIView beginAnimations:nil context:NULL];
            [UIView setAnimationDuration:0.5];
            [UIView setAnimationDelay:0.0f];

            targetContentOffset->x = newOffset.x;
            [UIView commitAnimations];

      }

Вам также необходимо установить скорость замедления прокрутки. Я сделал это в ViewDidLoad

    [self.scrollView setDecelerationRate:UIScrollViewDecelerationRateFast];

Ответ 5

У меня есть много разных видов внутри прокрутки с кнопками и распознавателями жестов.

@picciano ответ не работал (прокрутка работала хорошо, но кнопки и распознаватели не касались), поэтому я нашел это решение:

class ExtendedHitAreaView : UIScrollView {
    // Your insets
    var hitAreaEdgeInset = UIEdgeInsets(top: 0, left: -20, bottom: 0, right: -20) 

    override func pointInside(point: CGPoint, withEvent event: UIEvent?) -> Bool {
        let hitBounds = UIEdgeInsetsInsetRect(bounds, hitAreaEdgeInset)
        return CGRectContainsPoint(hitBounds, point)
    }
}

Ответ 6

Решение

alcides работает отлично. я просто включаю/выключаю прокрутку прокрутки, всякий раз, когда я ввожу scrollviewDidEndDragging и scrollViewWillEndDragging. если пользователь прокручивается несколько раз до того, как анимация подкачки завершена, ячейки немного выровнены.

поэтому я:

- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset {

    scrollView.scrollEnabled = NO

    CGPoint currentOffset = scrollView.contentOffset;
    CGPoint newOffset = CGPointZero;

    if (_lastOffset.x < currentOffset.x) {
        // right to left
        newOffset.x = _lastOffset.x + 298;
    }
    else {
        // left to right
        newOffset.x = _lastOffset.x - 298;
    }

    [UIView animateWithDuration:0.4 animations:^{
        targetContentOffset.x = newOffset.x
    }];
}

- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView {
    scrollView.scrollEnabled = YES
}