Непрерывная вертикальная прокрутка между UICollectionView, вложенным в UIScrollView
Хотя я знаю, что вложенные scrollViews не идеальны, наши дизайнеры предоставили мне эту настройку, поэтому я прилагаю все усилия, чтобы заставить ее работать. Пусть начнется!
Просмотр иерархии
- UIView
- UIScrollView (только для вертикальной прокрутки)
- UIImageView
- UICollectionView # 1 (только для горизонтальной прокрутки)
- UIImageView (отличается от предыдущего UIImageView)
- UICollectionView # 2 (только для вертикальной прокрутки)
Важное примечание
Все мои представления определяются с помощью программной автоматической компоновки. Каждое последующее представление в иерархии подзаголовков UIScrollView имеет зависимость y-координаты от представления, которое было до него.
Проблема
Для простоты немного изменим номенклатуру:
-
_outerScrollView
будет ссылаться на UIScrollView
-
_innerScrollView
будет ссылаться на UICollectionView #2
Я бы хотел, чтобы мой _outerScrollView
отправил свое событие касания к _innerScrollView
, достигнув нижней части его contentSize. Я бы хотел, чтобы произошло обратное, когда я прокручиваю назад.
В настоящее время у меня есть следующий код:
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
{
CGFloat bottomEdge = [scrollView contentOffset].y + CGRectGetHeight(scrollView.frame);
if (bottomEdge >= [_outerScrollView contentSize].height) {
_outerScrollView.scrollEnabled = NO;
_innerScrollView.scrollEnabled = YES;
} else {
_outerScrollView.scrollEnabled = YES;
_innerScrollView.scrollEnabled = NO;
}
}
где начальные условия (до прокрутки) установлены на:
outerScrollView.scrollEnabled = YES;
innerScrollView.scrollEnabled = NO;
Что происходит?
При касании вида outerScrollView
прокручивается до нижнего края, а затем имеет эффект резинкой из-за _outerScrollView.bounces = YES;
Если я снова коснусь вида, прокрутка innerScrollView прокручивается до тех пор, пока он не достигнет своего нижнего края. На обратном пути тот же эффект резинкой имеет место в обратном порядке. То, что я хочу сделать, - это движение жидкости между двумя подзонами.
Очевидно, это связано с условиями scrollEnabled
, которые установлены в условном выражении в фрагменте кода. Я пытаюсь выяснить, как направить скорость/скорость одного scrollView на следующий scrollView при ударе по краю.
Любая помощь в этом вопросе будет с благодарностью.
Другие заметки
- Это не сработало для меня: https://github.com/ole/OLEContainerScrollView
- Я рассматриваю возможность размещения всего в иерархии UIScrollView (кроме UICollectionView # 2) внутри UICollectionView # 2
supplementaryView
. Не уверен, что это сработает.
Ответы
Ответ 1
Выяснилось!
Сначала:
_scrollView.pagingEnabled = YES;
Во-вторых:
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
if (scrollView == _scrollView || scrollView == _offersCollectionView) {
CGFloat offersCollectionViewPosition = _offersCollectionView.contentOffset.y;
CGFloat scrollViewBottomEdge = [scrollView contentOffset].y + CGRectGetHeight(scrollView.frame);
if (scrollViewBottomEdge >= [_scrollView contentSize].height) {
_scrollView.scrollEnabled = NO;
_offersCollectionView.scrollEnabled = YES;
} else if (offersCollectionViewPosition <= 0.0f && [_offersCollectionView isScrollEnabled]) {
[_scrollView scrollRectToVisible:[_scrollView frame] animated:YES];
_scrollView.scrollEnabled = YES;
_offersCollectionView.scrollEnabled = NO;
}
}
}
Где:
-
_scrollView
- это _outerScrollView
-
_offersCollectionView
- это _innerScrollView
(который был UICollectionView # 2 в моем исходном сообщении).
Вот что происходит сейчас:
- Когда я прокручиваю (так что представление перемещается вниз),
offersCollectionView
берет на себя весь вид, перемещая другие подэкраны из представления.
- Если я прокручу вниз (так что взгляды вверх), остальные части обзора возвращаются в фокус с эффектом отскока scrollView.
Ответ 2
Принятый ответ не работал для меня. Вот что сделал:
Определите подкласс UIScrollView
:
class CollaborativeScrollView: UIScrollView, UIGestureRecognizerDelegate {
var lastContentOffset: CGPoint = CGPoint(x: 0, y: 0)
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
return otherGestureRecognizer.view is CollaborativeScrollView
}
}
Поскольку невозможно перенаправить касания в другое представление, единственный способ гарантировать, что внешнее представление прокрутки может продолжать прокрутку после остановки внутреннего, - это если он получал касания все время. Однако, чтобы предотвратить перемещение внешнего, в то время как внутренний, мы должны заблокировать его, не устанавливая его isScrollEnabled
в false
, в противном случае он перестанет получать прикосновения и не сможет определить, где находится внутренний. остановился, когда мы хотим прокрутить внутреннюю верхнюю или нижнюю часть.
Это делается путем назначения UIScrollViewDelegate
для scrollviews и реализации scrollViewDidScroll(_:)
как показано:
class YourViewController: UIViewController, UIScrollViewDelegate {
private var mLockOuterScrollView = false
@IBOutlet var mOuterScrollView: CollaborativeScrollView!
@IBOutlet var mInnerScrollView: CollaborativeScrollView!
enum Direction {
case none, left, right, up, down
}
func viewDidLoad() {
mOuterScrollView.delegate = self
mInnerScrollView.delegate = self
mInnerScrollView.bounces = false
}
func scrollViewDidScroll(_ scrollView: UIScrollView) {
guard scrollView is CollaborativeScrollView else {return}
let csv = scrollView as! CollaborativeScrollView
//determine direction of scrolling
var directionTemp: Direction?
if csv.lastContentOffset.y > csv.contentOffset.y {
directionTemp = .up
} else if csv.lastContentOffset.y < csv.contentOffset.y {
directionTemp = .down
}
guard let direction = directionTemp else {return}
//lock outer scrollview if necessary
if csv === mInnerScrollView {
let isAlreadyAllTheWayDown = (mInnerScrollView.contentOffset.y + mInnerScrollView.frame.size.height) == mInnerScrollView.contentSize.height
let isAlreadyAllTheWayUp = mInnerScrollView.contentOffset.y == 0
if (direction == .down && isAlreadyAllTheWayDown) || (direction == .up && isAlreadyAllTheWayUp) {
mLockOuterScrollView = false
} else {
mLockOuterScrollView = true
}
} else if mLockOuterScrollView {
mOuterScrollView.contentOffset = mOuterScrollView.lastContentOffset
}
csv.lastContentOffset = csv.contentOffset
}
}
И это оно. Это предотвратит прокрутку внешнего прокрутки, когда вы начнете прокручивать внутренний, и заставит его снова подхватиться, когда внутренний прокручивается до самого конца.