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

Обработка касаний для вложенных прокруток UIScrollViews в одном направлении

У меня есть два вложенных UIScrollViews, которые прокручиваются в вертикальном направлении. Мне нужно внешнее scrollview, чтобы прокрутить до него максимальный диапазон, прежде чем разрешить прокрутку внутреннего прокрутки. Внутренняя прокрутка не должна прокручиваться, пока внешнее scrollview не достигнет максимального диапазона. Вот иллюстрация: Nested Scrollviews Diagram

На левой диаграмме вертикальное перетаскивание внутри Scrollview B должно перемещаться Scrollview A, а Scrollview B не должно быть прокручиваемым (но оно все равно должно иметь возможность получать штрихи/метки). Когда Scrollview A достигает максимального диапазона (когда Scrollview B попадает в верхнюю часть экрана), тогда Scrollview B должен прокручиваться. Это должно работать в одном непрерывном движении.

Я попытался переключить Scrollview B scrollEnabled из Scrollview A scrollViewDidScroll: метод делегата, но это не кажется жизнеспособным решением, потому что оно не работает в одном непрерывном движении (например: пользователю нужно освободить и снова коснуться после того, как Scrollview B достигнет верхней части экрана).

Какой лучший способ реализовать это, который работает в одном непрерывном движении?

4b9b3361

Ответ 1

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

У меня есть 3 свойства, которые управляют прокруткой:

@property (nonatomic, assign) CGFloat currentPanY;
@property (nonatomic, assign) BOOL    scrollA;
@property (nonatomic, assign) BOOL    scrollB;

2-ступенчатая прокрутка:

Отключить прокрутку для B и включить прокрутку для A.
Это позволяет прокручивать A.

Когда A достигнет своего максимального положения, отключите прокрутку для A и включите прокрутку для B:

-(void)scrollViewDidScroll: (UIScrollView *)scrollView {
    if (scrollView.contentOffset.y >= self.maxScrollUpOffset) {
        [scrollView setContentOffset:CGPointMake(0, self.maxScrollUpOffset) animated:NO];        
        self.scrollviewA.scrollEnabled = NO;
        self.scrollviewB.scrollEnabled = YES;
        self.scrollB = YES;
    }
}

Это дает следующий эффект:
Когда A прокручивается вверх, она перестанет прокручиваться, когда достигнут максимальный размер. Однако B не начнет прокрутку, так как распознающий знак жесткого диска A не пересылает свои действия распознавателю жесты на букву B. Таким образом, нужно поднять палец и начать вторую прокрутку. Затем, B будет прокручиваться. Это дает 2-ступенчатую прокрутку.

Непрерывная прокрутка:

Для непрерывной прокрутки B должен прокручиваться, пока палец, который начал прокрутку A, продолжает двигаться вверх. Чтобы обнаружить это, я добавил еще один распознаватель жесты таза A и активировал его одновременное обнаружение жестов со встроенными распознавателями жестов A и B:

 - (BOOL)gestureRecognizer:(UIPanGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UISwipeGestureRecognizer *)otherGestureRecognizer {
    return YES;
}

В действии этого дополнительного распознавателя распознавания жеста, я вычисляю расстояние, которое пальцем движется вверх после того, как достигнут предел прокрутки A. На этом расстоянии B затем прокручивается программно:

- (void)panGestureRecognizerAction:(UIPanGestureRecognizer *)recognizer {
    if (recognizer.state != UIGestureRecognizerStateChanged) {
        self.currentPanY = 0;
        self.scrollB = NO;
        self.scrollA = NO;
    } else {
        CGPoint currentTranslation = [recognizer translationInView:self.scrollviewA];
        CGFloat currentYup = currentTranslation.y;

        if (self.scrollA || self.scrollB) {
            if (self.currentPanY == 0) {
                self.currentPanY = currentYup;
            }

            CGFloat additionalYup = self.currentPanY - currentYup;
            if (self.scrollA) {
                CGFloat offset = self.scrollviewA.scrollUpOffset + additionalYup;
                if (offset >= 0) {
                    self.scrollviewA.contentOffset = CGPointMake(0, offset);
                } else {
                    self.scrollviewA.contentOffset = CGPointZero;
                }
            } else if (self.scrollB){
                self.scrollviewB.contentOffset = CGPointMake(0, additionalYup);
            }
        }
    }
}  

По-прежнему существует недостаток:
Если вы начнете прокрутку, поднимите палец и позвольте scrollView замедляться, он будет вести себя как двухступенчатая прокрутка, так как дополнительный распознаватель жесты не распознает любой жест панорамы.

Ответ 2

В моем случае я решил создать подкласс UIScrollView для внешнего ScrollView.

class MYOuterScrollView: UIScrollView, UIGestureRecognizerDelegate
{
    override func gestureRecognizerShouldBegin(gestureRecognizer: UIGestureRecognizer) -> Bool
    {
        return true
    }

    func gestureRecognizer(gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWithGestureRecognizer otherGestureRecognizer: UIGestureRecognizer) -> Bool
    {
         return true
    }
}

Ответ 3

Ваше требование, чтобы это работало в одном непрерывном движении, вызывает ответ: вам нужно использовать только один UIScrollView, а не два.

Если у вас есть только один scrollview, вы можете выполнить свою магию, переопределив метод scrollview layoutSubviews и перерисуя его содержимое, чтобы выполнить эффект параллакса на основе текущего значения contentOffset. Убедитесь, что contentSize всегда отражает полную высоту (вы можете даже обновить contentSize внутри layoutSubviews, если это необходимо).

Для архитектуры возьмите свою существующую диаграмму и просто замените Scrollview B на вид B.

Ответ 4

распознаватель жестов для просмотра прокрутки A должен перейти к распознавателю жестов для просмотра прокрутки B, чтобы иметь непрерывное движение, которое, я уверен, невозможно. Почему бы не объединить содержимое двух списков прокрутки, а затем у вас будет одно непрерывное движение. Этот код будет комбинировать содержимое scrollView A и B только с A.

UIScrollView* scrollViewA = ...;
UIScrollView* scrollViewB = ...;
NSArray* subviews = scrollViewB.subviews;
for (int i = 0; i < subviews.count; i++)
{
    UIView* subview = [subviews objectAtIndex:i];
    CGRect frame = subview.frame;
    frame.origin.y += scrollViewA.contentSize.height;
    subview.frame = frame;
    [scrollViewA addSubview:subview];
}
CGSize size = scrollViewA.contentSize;
size.height += scrollViewB.contentSize.height;
scrollViewA.contentSize = size;

Ответ 5

Прокрутка с одинаковым направлением

Прокрутка в том же направлении происходит, когда a UIScrollView, который является подзором UIScrollView, прокручивается в одном направлении. Это показано на левом изображении.

Следуйте этой ссылке, если хотите подробно ознакомиться https://developer.apple.com/library/ios/documentation/WindowsViews/Conceptual/UIScrollView_pg/NestedScrollViews/NestedScrollViews.html

enter image description here

Просто установите некоторые атрибуты, упомянутые в приведенном выше изображении. он работает.