UIScrollView горизонтальный пейджинг, как вкладки Mobile Safari

StackOverflow https://stackoverflow.com/questions/1220354

Вопрос

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

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

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

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

Это было полезно?

Решение

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

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

Создайте подкласс UIView (т.е. ClipView ), который будет содержать UIScrollView и его подпредставления. По сути, он должен иметь кадр, который, как вы предполагаете, должен был бы иметь UIScrollView при обычных обстоятельствах. Поместите UIScrollView в центр ClipView . Убедитесь, что для ClipView 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) с помощью метода EventEvent: (UIEvent *) , однако вы все равно будете для выполнения отсечения требуется представление контейнера, и может быть сложно определить, когда возвращать YES , основываясь только на кадре UIScrollView .

ПРИМЕЧАНИЕ. Вам также следует ознакомиться с хитом Юрита Пакасте: withEvent :ification если у вас есть проблемы с взаимодействием с пользователем subview.

Другие советы

Приведенное выше решение 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;
}

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

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

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

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

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

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

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

Я сделал еще одну реализацию, которая может автоматически возвращать представление прокрутки. Поэтому не нужно иметь 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;
}

У меня есть другая потенциально полезная модификация для реализации ClipTest ClipView. Мне не понравилось предоставлять ссылку на 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;
}

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

В итоге я эмулировал поведение scrollview, добавив приведенный ниже метод к делегату (или 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.

Включите события касания для дочерних представлений вида с прокруткой, поддерживая при этом технику этого вопроса 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, но это было слишком большое изменение, чтобы его сделать.)

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

- (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] должно быть прокруткой

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top