UIScrollView горизонтальный пейджинг, как вкладки Mobile Safari - PullRequest
88 голосов
/ 03 августа 2009

Mobile Safari позволяет переключать страницы, вводя своего рода горизонтальное представление подкачки UIScrollView с элементом управления страницей внизу.

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

Пример, предоставленный Apple: PageControl показывает, как использовать UIScrollView для горизонтальной подкачки, но все представления занимают всю ширину экрана.

Как получить UIScrollView для отображения некоторого содержимого следующего представления, как это делает мобильный Safari?

Ответы [ 9 ]

260 голосов
/ 03 августа 2009

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

Тогда, поскольку вы хотите видеть другие страницы, конечно, установите clipsToBounds в NO, как указано в mhjoy. Теперь уловка состоит в том, чтобы прокрутить его, когда пользователь начинает перетаскивание за пределы диапазона кадра UIScrollView. Мое решение (когда я должен был сделать это совсем недавно) было следующим:

Создайте UIView подкласс (то есть ClipView), который будет содержать UIScrollView и его подпредставления. По сути, он должен иметь структуру того, что, как вы предполагаете, UIScrollView будет иметь при нормальных обстоятельствах. Поместите UIScrollView в центр ClipView. Убедитесь, что ClipView s clipsToBounds установлен на YES, если его ширина меньше ширины родительского представления. Кроме того, ClipView требуется ссылка на UIScrollView.

Последний шаг - переопределить - (UIView *)hitTest:withEvent: внутри ClipView.

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
  return [self pointInside:point withEvent:event] ? scrollView : nil;
}

Это в основном расширяет область касания UIScrollView до рамки вида его родителя, именно то, что вам нужно.

Другим вариантом может быть создание подкласса UIScrollView и переопределение его метода - (BOOL)pointInside:(CGPoint) point withEvent:(UIEvent *) event, однако вам все равно потребуется представление контейнера для выполнения отсечения, и может быть трудно определить, когда возвращать YES, основываясь только на UIScrollView кадр.

ПРИМЕЧАНИЕ: Вам также следует взглянуть на hitTest: withEvent: модификация Джури Пакасте, если у вас есть проблемы с взаимодействием с пользователем subview.

69 голосов
/ 03 сентября 2009

Приведенное выше решение ClipView сработало для меня, но мне пришлось сделать другую реализацию -[UIView hitTest:withEvent:]. Версия Эда Марти не позволяла взаимодействовать с пользователем при работе с вертикальными прокрутками, которые есть у меня внутри горизонтального.

У меня работала следующая версия:

-(UIView*)hitTest:(CGPoint)point withEvent:(UIEvent*)event
{
    UIView* child = nil;
    if ((child = [super hitTest:point withEvent:event]) == self)
        return self.scrollView;     
    return child;
}
8 голосов
/ 09 декабря 2014

Установите размер кадра scrollView, так как размер ваших страниц будет:

[self.view addSubview:scrollView];
[self.view addGestureRecognizer:mainScrollView.panGestureRecognizer];

Теперь вы можете перемещаться по self.view, и содержимое в scrollView будет прокручиваться.
Также используйте scrollView.clipsToBounds = NO; для предотвращения отсечения содержимого.

5 голосов
/ 19 ноября 2013

Я закончил с пользовательским UIScrollView, так как это был самый быстрый и простой способ, который мне показался. Однако я не видел точного кода, так что решил поделиться. Мои потребности были в UIScrollView с небольшим содержимым, и поэтому сам UIScrollView был маленьким для достижения эффекта подкачки. Как говорится в сообщении, вы не можете пролистать. Но теперь вы можете.

Создайте класс CustomScrollView и подкласс UIScrollView. Тогда все, что вам нужно сделать, это добавить это в файл .m:

- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event {
  return (point.y >= 0 && point.y <= self.frame.size.height);
}

Это позволяет вам прокручивать из стороны в сторону (по горизонтали). Отрегулируйте границы соответствующим образом, чтобы установить область касания для прокрутки / прокрутки. Наслаждайтесь!

4 голосов
/ 17 августа 2012

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

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
    if ([self pointInside:point withEvent:event]) {
        for (id view in [self subviews]) {
            if ([view isKindOfClass:[UIScrollView class]]) {
                return view;
            }
        }
    }
    return nil;
}
3 голосов
/ 25 июля 2013

Включение событий касания для дочерних представлений вида с прокруткой при поддержке техники этого вопроса SO. Использует ссылку на представление прокрутки (self.scrollView) для удобства чтения.

- (UIView*)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
    UIView *hitView = nil;
    NSArray *childViews = [self.scrollView subviews];
    for (NSUInteger i = 0; i < childViews.count; i++) {
        CGRect childFrame = [[childViews objectAtIndex:i] frame];
        CGRect scrollFrame = self.scrollView.frame;
        CGPoint contentOffset = self.scrollView.contentOffset;
        if (childFrame.origin.x + scrollFrame.origin.x < point.x + contentOffset.x &&
            point.x + contentOffset.x < childFrame.origin.x + scrollFrame.origin.x + childFrame.size.width &&
            childFrame.origin.y + scrollFrame.origin.y < point.y + contentOffset.y &&
            point.y + contentOffset.y < childFrame.origin.y + scrollFrame.origin.y + childFrame.size.height
        ){
            hitView = [childViews objectAtIndex:i];
            return hitView;
        }
    }
    hitView = [super hitTest:point withEvent:event];
    if (hitView == self)
        return self.scrollView;
    return hitView;
}

Добавьте это к вашему дочернему виду, чтобы захватить событие касания:

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event

(Это вариант решения пользователя 1856273. Очистил для удобства чтения и включил исправление ошибки Бартсерка. Я думал о редактировании ответа пользователя 1856273, но это было слишком большое изменение, чтобы его сделать.)

3 голосов
/ 29 октября 2012

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

В итоге я эмулировал поведение прокрутки, добавив нижеприведенный метод к делегату (или UICollectionViewLayout).

- (CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset withScrollingVelocity:(CGPoint)velocity
{    
  if (velocity.x > 0) {
    proposedContentOffset.x = ceilf(self.collectionView.contentOffset.x / pageSize) * pageSize;
  }
  else {
    proposedContentOffset.x = floorf(self.collectionView.contentOffset.x / pageSize) * pageSize;
  }

  return proposedContentOffset;
}

Это полностью исключает делегирование действия смахивания, что также было бонусом. UIScrollViewDelegate имеет аналогичный метод, называемый scrollViewWillEndDragging:withVelocity:targetContentOffset:, который можно использовать для отображения страниц UITableViews и UIScrollViews.

3 голосов
/ 03 марта 2012

У меня есть другая потенциально полезная модификация для реализации ClipView hitView. Мне не понравилось предоставлять ссылку на UIScrollView на ClipView. Моя реализация ниже позволяет вам повторно использовать класс ClipView для расширения области проверки попадания чего-либо, и не нужно предоставлять ему ссылку.

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
    if (([self pointInside:point withEvent:event]) && (self.subviews.count >= 1))
    {
        // An extended-hit view should only have one sub-view, or make sure the
        // first subview is the one you want to expand the hit test for.
        return [self.subviews objectAtIndex:0];
    }

    return nil;
}
2 голосов
/ 15 марта 2013

моя версия Нажатие кнопки лежа на свитке - работа =)

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
    UIView* child = nil;
    for (int i=0; i<[[[[self subviews] objectAtIndex:0] subviews] count];i++) {

        CGRect myframe =[[[[[self subviews] objectAtIndex:0] subviews]objectAtIndex:i] frame];
        CGRect myframeS =[[[self subviews] objectAtIndex:0] frame];
        CGPoint myscroll =[[[self subviews] objectAtIndex:0] contentOffset];
        if (myframe.origin.x < point.x && point.x < myframe.origin.x+myframe.size.width &&
            myframe.origin.y+myframeS.origin.y < point.y+myscroll.y && point.y+myscroll.y < myframe.origin.y+myframeS.origin.y +myframe.size.height){
            child = [[[[self subviews] objectAtIndex:0] subviews]objectAtIndex:i];
            return child;
        }


    }

    child = [super hitTest:point withEvent:event];
    if (child == self)
        return [[self subviews] objectAtIndex:0];
    return child;
    }

но только [[self subviews] objectAtIndex: 0] должно быть прокруткой

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