Как я могу заставить объект, UIScrollView
в котором включены листание и прокрутка, перемещаться только вертикально или горизонтально в данный момент?
Насколько я понимаю, это directionalLockEnabled
свойство должно обеспечивать это, но диагональное смахивание по-прежнему заставляет представление прокручиваться по диагонали вместо ограничения движения по одной оси.
Изменить: чтобы было понятнее, я хотел бы разрешить пользователю прокручивать по горизонтали ИЛИ по вертикали, но не то и другое одновременно.
Ответы:
Должен сказать, вы в очень сложной ситуации.
Обратите внимание, что вам нужно использовать UIScrollView with
pagingEnabled=YES
для переключения между страницами, но вам нужноpagingEnabled=NO
прокручивать по вертикали.Есть 2 возможные стратегии. Я не знаю, какой из них будет работать / проще реализовать, поэтому попробуйте оба.
Во-первых: вложенные UIScrollViews. Честно говоря, я еще не видел человека, у которого это работало бы. Однако лично я недостаточно старался, и моя практика показывает, что когда вы действительно стараетесь, вы можете заставить UIScrollView делать все, что захотите .
Таким образом, стратегия состоит в том, чтобы позволить внешнему виду прокрутки обрабатывать только горизонтальную прокрутку, а внутренним видам прокрутки - только вертикальной прокрутке. Для этого вы должны знать, как UIScrollView работает внутри. Он переопределяет
hitTest
метод и всегда возвращает себя, так что все события касания попадают в UIScrollView. Затем внутриtouchesBegan
иtouchesMoved
т.д. он проверяет, заинтересовано ли событие в событии, и либо обрабатывает, либо передает его внутренним компонентам.Чтобы решить, обрабатывать или пересылать касание, UIScrollView запускает таймер при первом касании:
Если вы не переместили палец значительно в течение 150 мс, он передает событие во внутреннее представление.
Если вы значительно переместили палец в течение 150 мс, он начинает прокрутку (и никогда не передает событие во внутреннее представление).
Обратите внимание: когда вы касаетесь таблицы (которая является подклассом режима прокрутки) и сразу начинаете прокрутку, строка, к которой вы прикоснулись, никогда не выделяется.
Если вы не переместили палец значительно в течение 150 мс, и UIScrollView начал передавать события во внутреннее представление, но затем вы переместили палец достаточно далеко, чтобы началась прокрутка, UIScrollView вызывает
touchesCancelled
внутреннее представление и начинает прокрутку.Обратите внимание: когда вы касаетесь таблицы, немного удерживаете палец и затем начинаете прокрутку, строка, к которой вы прикоснулись, выделяется первой, но затем снимается.
Эта последовательность событий может быть изменена конфигурацией UIScrollView:
delaysContentTouches
НЕТ, то таймер не используется - события сразу переходят во внутренний элемент управления (но затем отменяются, если вы перемещаете палец достаточно далеко)cancelsTouches
НЕТ, то после отправки событий в элемент управления прокрутка никогда не будет.Обратите внимание , что UIScrollView , который получает все
touchesBegin
,touchesMoved
,touchesEnded
иtouchesCanceled
события из CocoaTouch (потому что егоhitTest
говорит , что это делать). Затем он направляет их к внутреннему взгляду, если хочет, пока хочет.Теперь, когда вы знаете все о UIScrollView, вы можете изменить его поведение. Могу поспорить, вы хотите отдать предпочтение вертикальной прокрутке, чтобы, как только пользователь коснулся представления и начал двигать пальцем (даже немного), представление начало прокручиваться в вертикальном направлении; но когда пользователь перемещает палец в горизонтальном направлении достаточно далеко, вы хотите отменить вертикальную прокрутку и начать горизонтальную прокрутку.
Вы хотите создать подкласс своего внешнего UIScrollView (скажем, вы называете свой класс RemorsefulScrollView), чтобы вместо поведения по умолчанию он немедленно перенаправлял все события во внутреннее представление и прокручивался только при обнаружении значительного горизонтального движения.
Как заставить RemorsefulScrollView вести себя таким образом?
Похоже, отключение вертикальной прокрутки и установка значения
delaysContentTouches
NO должны заставить работать вложенные UIScrollViews. К сожалению, это не так; UIScrollView, похоже, выполняет некоторую дополнительную фильтрацию для быстрых движений (которые нельзя отключить), так что даже если UIScrollView можно прокручивать только по горизонтали, он всегда будет поглощать (и игнорировать) достаточно быстрые вертикальные движения.Эффект настолько серьезен, что вертикальную прокрутку внутри вложенного представления прокрутки невозможно использовать. (Похоже, у вас есть именно такая настройка, поэтому попробуйте: удерживайте палец в течение 150 мс, а затем перемещайте его в вертикальном направлении - тогда вложенный UIScrollView работает так, как ожидалось!)
Это означает, что вы не можете использовать код UIScrollView для обработки событий; вам нужно переопределить все четыре метода обработки касания в RemorsefulScrollView и сначала выполнить свою собственную обработку, перенаправляя событие только в
super
(UIScrollView), если вы решили использовать горизонтальную прокрутку.Однако вы должны пройти
touchesBegan
к UIScrollView, потому что вы хотите, чтобы помнить основание координат для будущей горизонтальной прокрутки (если позже вы решите , что это горизонтальная прокрутка). Вы не сможете отправитьtouchesBegan
в UIScrollView позже, потому что вы не можете сохранитьtouches
аргумент: он содержит объекты, которые будут изменены до следующегоtouchesMoved
события, и вы не можете воспроизвести старое состояние.Таким образом, вы должны
touchesBegan
немедленно перейти к UIScrollView, но вы будете скрыватьtouchesMoved
от него любые дальнейшие события, пока не решите прокрутить по горизонтали. НетtouchesMoved
означает отсутствие прокрутки, поэтому начальнаяtouchesBegan
буква не повредит. Но установитеdelaysContentTouches
значение NO, чтобы никакие дополнительные таймеры неожиданности не мешали.(Offtopic - в отличие от вас, UIScrollView может правильно сохранять касания и может воспроизводить и пересылать исходное
touchesBegan
событие позже. Он имеет несправедливое преимущество использования неопубликованных API-интерфейсов, поэтому может клонировать сенсорные объекты до того, как они будут видоизменены.)Учитывая, что вы всегда вперед
touchesBegan
, вам также нужно впередtouchesCancelled
иtouchesEnded
. Вы должны включитьtouchesEnded
вtouchesCancelled
, однако, поскольку UIScrollView будет интерпретироватьtouchesBegan
,touchesEnded
последовательность как сенсорную мышь, и направите его на внутренний взгляд. Вы уже сами пересылаете нужные события, поэтому вы никогда не хотите, чтобы UIScrollView что-либо пересылал.В основном это псевдокод того, что вам нужно сделать. Для простоты я никогда не разрешаю горизонтальную прокрутку после того, как произошло событие мультитач.
// RemorsefulScrollView.h @interface RemorsefulScrollView : UIScrollView { CGPoint _originalPoint; BOOL _isHorizontalScroll, _isMultitouch; UIView *_currentChild; } @end // RemorsefulScrollView.m // the numbers from an example in Apple docs, may need to tune them #define kThresholdX 12.0f #define kThresholdY 4.0f @implementation RemorsefulScrollView - (id)initWithFrame:(CGRect)frame { if (self = [super initWithFrame:frame]) { self.delaysContentTouches = NO; } return self; } - (id)initWithCoder:(NSCoder *)coder { if (self = [super initWithCoder:coder]) { self.delaysContentTouches = NO; } return self; } - (UIView *)honestHitTest:(CGPoint)point withEvent:(UIEvent *)event { UIView *result = nil; for (UIView *child in self.subviews) if ([child pointInside:point withEvent:event]) if ((result = [child hitTest:point withEvent:event]) != nil) break; return result; } - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { [super touchesBegan:touches withEvent:event]; // always forward touchesBegan -- there's no way to forward it later if (_isHorizontalScroll) return; // UIScrollView is in charge now if ([touches count] == [[event touchesForView:self] count]) { // initial touch _originalPoint = [[touches anyObject] locationInView:self]; _currentChild = [self honestHitTest:_originalPoint withEvent:event]; _isMultitouch = NO; } _isMultitouch |= ([[event touchesForView:self] count] > 1); [_currentChild touchesBegan:touches withEvent:event]; } - (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event { if (!_isHorizontalScroll && !_isMultitouch) { CGPoint point = [[touches anyObject] locationInView:self]; if (fabsf(_originalPoint.x - point.x) > kThresholdX && fabsf(_originalPoint.y - point.y) < kThresholdY) { _isHorizontalScroll = YES; [_currentChild touchesCancelled:[event touchesForView:self] withEvent:event] } } if (_isHorizontalScroll) [super touchesMoved:touches withEvent:event]; // UIScrollView only kicks in on horizontal scroll else [_currentChild touchesMoved:touches withEvent:event]; } - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event { if (_isHorizontalScroll) [super touchesEnded:touches withEvent:event]; else { [super touchesCancelled:touches withEvent:event]; [_currentChild touchesEnded:touches withEvent:event]; } } - (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event { [super touchesCancelled:touches withEvent:event]; if (!_isHorizontalScroll) [_currentChild touchesCancelled:touches withEvent:event]; } @end
Я не пытался запустить или даже скомпилировать это (и набрал весь класс в текстовом редакторе), но вы можете начать с вышеизложенного и, надеюсь, заставить его работать.
Единственная скрытая уловка, которую я вижу, заключается в том, что если вы добавляете какие-либо дочерние представления, не относящиеся к UIScrollView, в RemorsefulScrollView, события касания, которые вы пересылаете дочернему элементу, могут возвращаться к вам через цепочку респондентов, если ребенок не всегда обрабатывает касания, как это делает UIScrollView. Пуленепробиваемая реализация RemorsefulScrollView защитит от
touchesXxx
повторного входа.Вторая стратегия: если по какой-то причине вложенные UIScrollView не работают или оказываются слишком сложными для правильного выполнения, вы можете попробовать обойтись только одним UIScrollView, переключая его
pagingEnabled
свойство на лету из вашегоscrollViewDidScroll
метода делегата.Чтобы предотвратить диагональную прокрутку, вы должны сначала попытаться запомнить contentOffset in
scrollViewWillBeginDragging
, а также проверить и сбросить contentOffset внутри,scrollViewDidScroll
если вы обнаружите диагональное движение. Еще одна стратегия, которую следует попробовать, - сбросить contentSize, чтобы разрешить прокрутку только в одном направлении, после того как вы решите, в каком направлении движется палец пользователя. (UIScrollView кажется довольно снисходительным к возням с contentSize и contentOffset из его методов делегата.)Если это не работает или приводит к неаккуратным визуальным эффектам, вам необходимо переопределить
touchesBegan
иtouchesMoved
т. Д., А не пересылать события диагонального движения в UIScrollView. (Однако в этом случае пользовательский опыт будет неоптимальным, потому что вам придется игнорировать диагональные движения вместо того, чтобы направлять их в одном направлении. Если вы действительно любите приключения, вы можете написать свой собственный UITouch-аналог, что-то вроде RevengeTouch. Цель -C - это старый добрый C, и в мире нет ничего более утиного типа, чем C; пока никто не проверяет реальный класс объектов, что, как я полагаю, никто не делает, вы можете сделать любой класс похожим на любой другой класс. Это открывает возможность синтезировать любые прикосновения, какие захотите, с любыми координатами.)Стратегия резервного копирования: есть TTScrollView, разумная реализация UIScrollView в библиотеке Three20 . К сожалению, пользователю это кажется очень неестественным и неуместным. Но если каждая попытка использования UIScrollView терпит неудачу, вы можете вернуться к просмотру прокрутки с настраиваемым кодом. Я действительно не рекомендую этого делать, если это вообще возможно; использование UIScrollView гарантирует, что вы получите естественный внешний вид, независимо от того, как он будет развиваться в будущих версиях iPhone OS.
Хорошо, это небольшое эссе получилось слишком длинным. Я просто все еще увлекаюсь играми UIScrollView после работы над ScrollingMadness несколько дней назад.
PS Если у вас что-то из этого работает и вы захотите поделиться, пришлите мне соответствующий код по электронной почте на адрес andreyvit@gmail.com, я с радостью добавлю его в свой набор трюков ScrollingMadness .
PPS Добавление этого небольшого эссе в README ScrollingMadness.
источник
Это работает для меня каждый раз ...
scrollView.contentSize = CGSizeMake(scrollView.frame.size.width * NumberOfPages, 1);
источник
Для таких ленивых, как я:
Установить
scrollview.directionalLockEnabled
наYES
- (void) scrollViewWillBeginDragging: (UIScrollView *) scrollView { self.oldContentOffset = scrollView.contentOffset; } - (void) scrollViewDidScroll: (UIScrollView *) scrollView { if (scrollView.contentOffset.x != self.oldContentOffset.x) { scrollView.pagingEnabled = YES; scrollView.contentOffset = CGPointMake(scrollView.contentOffset.x, self.oldContentOffset.y); } else { scrollView.pagingEnabled = NO; } } - (void) scrollViewDidEndDecelerating: (UIScrollView *) scrollView { self.oldContentOffset = scrollView.contentOffset; }
источник
Я использовал следующий метод для решения этой проблемы, надеюсь, он вам поможет.
Сначала определите следующие переменные в файле заголовка вашего контроллера.
CGPoint startPos; int scrollDirection;
startPos сохранит значение contentOffset, когда ваш делегат получит сообщение scrollViewWillBeginDragging . Итак, в этом методе мы делаем следующее;
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView{ startPos = scrollView.contentOffset; scrollDirection=0; }
затем мы используем эти значения для определения предполагаемого пользователем направления прокрутки в сообщении scrollViewDidScroll .
- (void)scrollViewDidScroll:(UIScrollView *)scrollView{ if (scrollDirection==0){//we need to determine direction //use the difference between positions to determine the direction. if (abs(startPos.x-scrollView.contentOffset.x)<abs(startPos.y-scrollView.contentOffset.y)){ NSLog(@"Vertical Scrolling"); scrollDirection=1; } else { NSLog(@"Horitonzal Scrolling"); scrollDirection=2; } } //Update scroll position of the scrollview according to detected direction. if (scrollDirection==1) { [scrollView setContentOffset:CGPointMake(startPos.x,scrollView.contentOffset.y) animated:NO]; } else if (scrollDirection==2){ [scrollView setContentOffset:CGPointMake(scrollView.contentOffset.x,startPos.y) animated:NO]; } }
наконец, мы должны остановить все операции обновления, когда пользователь завершает перетаскивание;
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate{ if (decelerate) { scrollDirection=3; } }
источник
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate{ if (decelerate) { scrollDirection=3; } }
было изменено на- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate{ if (!decelerate) { scrollDirection=0; } }
и- (void)scrollViewDidEndDecelerating{ scrollDirection=0; }
Я мог бы здесь копать, но сегодня я наткнулся на этот пост, когда пытался решить ту же проблему. Вот мое решение, похоже, отлично работает.
Я хочу отобразить полный экран содержимого, но разрешить пользователю прокручивать до одного из 4 других экранов, вверх вниз влево и вправо. У меня есть один UIScrollView размером 320x480 и contentSize 960x1440. Смещение содержимого начинается с 320 480. В scrollViewDidEndDecelerating: я перерисовываю 5 представлений (центральные представления и 4 вокруг него) и сбрасываю смещение содержимого на 320 480.
Вот суть моего решения - метод scrollViewDidScroll :.
- (void)scrollViewDidScroll:(UIScrollView *)scrollView { if (!scrollingVertically && ! scrollingHorizontally) { int xDiff = abs(scrollView.contentOffset.x - 320); int yDiff = abs(scrollView.contentOffset.y - 480); if (xDiff > yDiff) { scrollingHorizontally = YES; } else if (xDiff < yDiff) { scrollingVertically = YES; } } if (scrollingHorizontally) { scrollView.contentOffset = CGPointMake(scrollView.contentOffset.x, 480); } else if (scrollingVertically) { scrollView.contentOffset = CGPointMake(320, scrollView.contentOffset.y); } }
источник
Ребята, это так же просто, как сделать высоту подпредставления такой же, как высоту прокрутки и высоту размера содержимого. Тогда вертикальная прокрутка отключена.
источник
Вот моя реализация страничной страницы,
UIScrollView
которая разрешает только ортогональные прокрутки. Обязательно установитеpagingEnabled
наYES
.PagedOrthoScrollView.h
@interface PagedOrthoScrollView : UIScrollView @end
PagedOrthoScrollView.m
#import "PagedOrthoScrollView.h" typedef enum { PagedOrthoScrollViewLockDirectionNone, PagedOrthoScrollViewLockDirectionVertical, PagedOrthoScrollViewLockDirectionHorizontal } PagedOrthoScrollViewLockDirection; @interface PagedOrthoScrollView () @property(nonatomic, assign) PagedOrthoScrollViewLockDirection dirLock; @property(nonatomic, assign) CGFloat valueLock; @end @implementation PagedOrthoScrollView @synthesize dirLock; @synthesize valueLock; - (id)initWithFrame:(CGRect)frame { self = [super initWithFrame:frame]; if (self == nil) { return self; } self.dirLock = PagedOrthoScrollViewLockDirectionNone; self.valueLock = 0; return self; } - (void)setBounds:(CGRect)bounds { int mx, my; if (self.dirLock == PagedOrthoScrollViewLockDirectionNone) { // is on even page coordinates, set dir lock and lock value to closest page mx = abs((int)CGRectGetMinX(bounds) % (int)CGRectGetWidth(self.bounds)); if (mx != 0) { self.dirLock = PagedOrthoScrollViewLockDirectionHorizontal; self.valueLock = (round(CGRectGetMinY(bounds) / CGRectGetHeight(self.bounds)) * CGRectGetHeight(self.bounds)); } else { self.dirLock = PagedOrthoScrollViewLockDirectionVertical; self.valueLock = (round(CGRectGetMinX(bounds) / CGRectGetWidth(self.bounds)) * CGRectGetWidth(self.bounds)); } // show only possible scroll indicator self.showsVerticalScrollIndicator = dirLock == PagedOrthoScrollViewLockDirectionVertical; self.showsHorizontalScrollIndicator = dirLock == PagedOrthoScrollViewLockDirectionHorizontal; } if (self.dirLock == PagedOrthoScrollViewLockDirectionHorizontal) { bounds.origin.y = self.valueLock; } else { bounds.origin.x = self.valueLock; } mx = abs((int)CGRectGetMinX(bounds) % (int)CGRectGetWidth(self.bounds)); my = abs((int)CGRectGetMinY(bounds) % (int)CGRectGetHeight(self.bounds)); if (mx == 0 && my == 0) { // is on even page coordinates, reset lock self.dirLock = PagedOrthoScrollViewLockDirectionNone; } [super setBounds:bounds]; } @end
источник
Мне также приходилось решать эту проблему, и хотя ответ Андрея Таранцова определенно содержит много полезной информации для понимания того
UIScrollViews
, как работает, я считаю, что решение немного сложнее. Мое решение, которое не включает подклассы или переадресацию касаний, выглядит следующим образом:UIPanGestureRecognizers
, снова по одному для каждого направления.UIScrollViews
свойствами panGestureRecognizer и фиктивным элементом,UIPanGestureRecognizer
соответствующим направлению, перпендикулярному желаемому направлению прокрутки UIScrollView, с помощьюUIGestureRecognizer's
-requireGestureRecognizerToFail:
селектора.-gestureRecognizerShouldBegin:
обратных вызовах для ваших фиктивных UIPanGestureRecognizers вычислите начальное направление панорамирования с помощью селектора -translationInView: для жеста (выUIPanGestureRecognizers
не будете вызывать этот обратный вызов делегата, пока ваше прикосновение не переведет достаточно, чтобы зарегистрироваться как панорама). Разрешить или запретить запуск распознавателя жестов в зависимости от рассчитанного направления, которое, в свою очередь, должно определять, разрешено ли запускать перпендикулярный распознаватель жестов панорамирования UIScrollView.-gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer:
, не позволяют вашему манекену ,UIPanGestureRecognizers
чтобы работать вместе с прокруткой (если у вас есть некоторое дополнительное поведение , которое вы хотите добавить).источник
Что я сделал, так это создал страницу UIScrollView с рамкой размером с экран просмотра и установил размер содержимого равным ширине, необходимой для всего моего содержимого, и высоте 1 (спасибо Tonetel). Затем я создал меню UIScrollView pages с фреймами, установленными для каждой страницы, размером экрана просмотра, и установил размер содержимого каждой из них равным ширине экрана и необходимой высоте для каждой. Затем я просто добавил каждую страницу меню к просмотру страниц. Убедитесь, что разбиение на страницы включено в просмотре прокрутки, но отключено в представлениях меню (по умолчанию должно быть отключено), и все должно быть в порядке.
Вид прокрутки страницы теперь прокручивается по горизонтали, но не по вертикали из-за высоты содержимого. Каждая страница прокручивается по вертикали, но не по горизонтали из-за ограничений размера содержимого.
Вот код, если это объяснение оставляло желать лучшего:
UIScrollView *newPagingScrollView = [[UIScrollView alloc] initWithFrame:self.view.bounds]; [newPagingScrollView setPagingEnabled:YES]; [newPagingScrollView setShowsVerticalScrollIndicator:NO]; [newPagingScrollView setShowsHorizontalScrollIndicator:NO]; [newPagingScrollView setDelegate:self]; [newPagingScrollView setContentSize:CGSizeMake(self.view.bounds.size.width * NumberOfDetailPages, 1)]; [self.view addSubview:newPagingScrollView]; float pageX = 0; for (int i = 0; i < NumberOfDetailPages; i++) { CGRect pageFrame = (CGRect) { .origin = CGPointMake(pageX, pagingScrollView.bounds.origin.y), .size = pagingScrollView.bounds.size }; UIScrollView *newPage = [self createNewPageFromIndex:i ToPageFrame:pageFrame]; // newPage.contentSize custom set in here [pagingScrollView addSubview:newPage]; pageX += pageFrame.size.width; }
источник
Спасибо, Tonetel. Я немного изменил ваш подход, но это было именно то, что мне нужно, чтобы предотвратить горизонтальную прокрутку.
self.scrollView.contentSize = CGSizeMake(self.scrollView.contentSize.width, 1);
источник
Это улучшенное решение icompot, которое было для меня довольно нестабильным. Этот отлично работает, и его легко реализовать:
- (void) scrollViewWillBeginDragging: (UIScrollView *) scrollView { self.oldContentOffset = scrollView.contentOffset; } - (void) scrollViewDidScroll: (UIScrollView *) scrollView { float XOffset = fabsf(self.oldContentOffset.x - scrollView.contentOffset.x ); float YOffset = fabsf(self.oldContentOffset.y - scrollView.contentOffset.y ); if (scrollView.contentOffset.x != self.oldContentOffset.x && (XOffset >= YOffset) ) { scrollView.pagingEnabled = YES; scrollView.contentOffset = CGPointMake(scrollView.contentOffset.x, self.oldContentOffset.y); } else { scrollView.pagingEnabled = NO; scrollView.contentOffset = CGPointMake( self.oldContentOffset.x, scrollView.contentOffset.y); } } - (void) scrollViewDidEndDecelerating: (UIScrollView *) scrollView { self.oldContentOffset = scrollView.contentOffset; }
источник
На будущее я основывался на ответе Андрея Танасова и придумал следующее решение, которое отлично работает.
Сначала определите переменную для хранения contentOffset и установите для нее координаты 0,0
var positionScroll = CGPointMake(0, 0)
Теперь реализуйте в делегате scrollView следующее:
override func scrollViewWillBeginDragging(scrollView: UIScrollView) { // Note that we are getting a reference to self.scrollView and not the scrollView referenced passed by the method // For some reason, not doing this fails to get the logic to work self.positionScroll = (self.scrollView?.contentOffset)! } override func scrollViewDidScroll(scrollView: UIScrollView) { // Check that contentOffset is not nil // We do this because the scrollViewDidScroll method is called on viewDidLoad if self.scrollView?.contentOffset != nil { // The user wants to scroll on the X axis if self.scrollView?.contentOffset.x > self.positionScroll.x || self.scrollView?.contentOffset.x < self.positionScroll.x { // Reset the Y position of the scrollView to what it was BEFORE scrolling started self.scrollView?.contentOffset = CGPointMake((self.scrollView?.contentOffset.x)!, self.positionScroll.y); self.scrollView?.pagingEnabled = true } // The user wants to scroll on the Y axis else { // Reset the X position of the scrollView to what it was BEFORE scrolling started self.scrollView?.contentOffset = CGPointMake(self.positionScroll.x, (self.scrollView?.contentOffset.y)!); self.collectionView?.pagingEnabled = false } } }
Надеюсь это поможет.
источник
Из документов:
"значение по умолчанию - НЕТ, что означает, что прокрутка разрешена как в горизонтальном, так и в вертикальном направлениях. Если значение ДА и пользователь начинает перетаскивание в одном общем направлении (горизонтальном или вертикальном), вид прокрутки отключает прокрутку в другом направлении. "
Я думаю, что важная часть: «если ... пользователь начинает перетаскивать в одном общем направлении». Так что, если они начнут перетаскивать по диагонали, это не сработает. Не то чтобы эти документы всегда надежны, когда дело доходит до интерпретации, но похоже, что это соответствует тому, что вы видите.
Это действительно кажется разумным. Я должен спросить - почему вы хотите ограничиться только горизонтальным или только вертикальным в любое время? Возможно, UIScrollView не для вас?
источник
Мое представление состоит из 10 горизонтальных представлений, а некоторые из этих представлений состоят из нескольких вертикальных представлений, поэтому вы получите что-то вроде этого:
1.0 2.0 3.1 4.1 2.1 3.1 2.2 2.3
При включенном разбиении по горизонтальной и вертикальной оси
Представление также имеет следующие атрибуты:
[self setDelaysContentTouches:NO]; [self setMultipleTouchEnabled:NO]; [self setDirectionalLockEnabled:YES];
Теперь, чтобы предотвратить диагональную прокрутку, сделайте следующее:
-(void)scrollViewDidScroll:(UIScrollView *)scrollView { int pageWidth = 768; int pageHeight = 1024; int subPage =round(self.contentOffset.y / pageHeight); if ((int)self.contentOffset.x % pageWidth != 0 && (int)self.contentOffset.y % pageHeight != 0) { [self setContentOffset:CGPointMake(self.contentOffset.x, subPage * pageHeight]; } }
Для меня работает как шарм!
источник
Нужно сбросить
_isHorizontalScroll
на НЕТ вtouchesEnded
иtouchesCancelled
.источник
Если у вас большой скроллинг ... и вы хотите ограничить диагональную прокрутку http://chandanshetty01.blogspot.in/2012/07/restricting-diagonal-scrolling-in.html
источник
Если вы не пробовали сделать это в бета-версии 3.0, я рекомендую вам попробовать. В настоящее время я использую серию табличных представлений внутри представления прокрутки, и он работает так, как ожидалось. (Во всяком случае, прокрутка есть. Обнаружил этот вопрос, когда искал решение совершенно другой проблемы ...)
источник
Для тех, кто ищет в Swift во втором решении @AndreyTarantsov, его код выглядит следующим образом:
var positionScroll:CGFloat = 0 func scrollViewWillBeginDragging(scrollView: UIScrollView) { positionScroll = self.theScroll.contentOffset.x } func scrollViewDidScroll(scrollView: UIScrollView) { if self.theScroll.contentOffset.x > self.positionScroll || self.theScroll.contentOffset.x < self.positionScroll{ self.theScroll.pagingEnabled = true }else{ self.theScroll.pagingEnabled = false } }
здесь мы сохраняем текущую позицию непосредственно перед перетаскиванием scrollview, а затем проверяем, увеличился или уменьшился x по сравнению с текущей позицией x. Если да, то установите для pagingEnabled значение true, если нет (y увеличилось / уменьшилось), установите для pagingEnabled значение false.
Надеюсь, это пригодится новичкам!
источник
Мне удалось решить эту проблему, реализовав scrollViewDidBeginDragging (_ :) и посмотрев на скорость базового UIPanGestureRecognizer.
NB: с этим решением каждая панорама, которая была бы диагональной, будет игнорироваться scrollView.
override func scrollViewWillBeginDragging(_ scrollView: UIScrollView) { super.scrollViewWillBeginDragging(scrollView) let velocity = scrollView.panGestureRecognizer.velocity(in: scrollView) if velocity.x != 0 && velocity.y != 0 { scrollView.panGestureRecognizer.isEnabled = false scrollView.panGestureRecognizer.isEnabled = true } }
источник
Если вы используете конструктор интерфейсов для разработки своего интерфейса - это можно сделать довольно легко.
В построителе интерфейса просто щелкните UIScrollView и выберите параметр (в инспекторе), который ограничивает его только односторонней прокруткой.
источник