Пользовательский пейджинг UIScrollView
Мой вопрос связан с пользовательской формой пейджинга, которую я пытаюсь сделать с помощью скроллера, и это проще визуализировать, если вы сначала рассмотрите тип прокрутки, реализованный в игровом автомате.
Итак, мой UIScrollView имеет ширину 100 пикселей. Предположим, что он содержит 3 внутренних вида, каждый с шириной 30 пикселей, так что они разделены шириной 3 пикселя. Тип пейджинга, который я хотел бы достичь, таков, что каждая страница является одним из моих представлений (30 пикселей), а не всей шириной прокрутки.
Я знаю, что обычно, если представление занимает всю ширину прокрутки, а пейджинг включен, тогда все работает. Однако в моем пользовательском пейджинге я также хочу, чтобы окружающие представления в представлении прокрутки также были видимыми.
Как мне это сделать?
Ответы
Ответ 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
}