Прокрутка двумя пальцами с помощью UIScrollView

У меня есть приложение, в котором мой основной вид принимает как touchesBegan, так и touchesMoved, и поэтому принимает касания одним пальцем и перетаскивает. Я хочу реализовать UIScrollView, и он у меня работает, но он отменяет перетаскивание, и поэтому мой contentView никогда их не получает. Я хотел бы реализовать UIScrollview, где перетаскивание двумя пальцами указывает на прокрутку, а событие перетаскивания одним пальцем передается в мое представление содержимого, поэтому оно работает нормально. Мне нужно создать собственный подкласс UIScrollView?

Вот мой код из моего appDelegate, где я реализую UIScrollView.

@implementation MusicGridAppDelegate

@synthesize window;
@synthesize viewController;
@synthesize scrollView;


- (void)applicationDidFinishLaunching:(UIApplication *)application {    

    // Override point for customization after app launch    
    //[application setStatusBarHidden:YES animated:NO];
    //[window addSubview:viewController.view];

    scrollView.contentSize = CGSizeMake(720, 480);
    scrollView.showsHorizontalScrollIndicator = YES;
    scrollView.showsVerticalScrollIndicator = YES;
    scrollView.delegate = self;
    [scrollView addSubview:viewController.view];
    [window makeKeyAndVisible];
}


- (void)dealloc {
    [viewController release];
    [scrollView release];
    [window release];
    [super dealloc];
}

person Craig    schedule 24.04.2009    source источник


Ответы (14)


Вам нужно создать подкласс UIScrollView (конечно!). Тогда вам необходимо:

  • сделать события одним пальцем, чтобы перейти к просмотру содержимого (легко), и

  • заставить события с двумя пальцами прокручивать представление прокрутки (может быть легко, может быть сложно, может быть невозможно).

Предложение Патрика в целом правильное: пусть ваш подкласс UIScrollView знает о вашем представлении контента, затем обработчики событий касания проверяют количество пальцев и соответствующим образом пересылают событие. Просто убедитесь, что (1) события, которые вы отправляете в представление содержимого, не возвращаются в UIScrollView через цепочку респондентов (т.е. убедитесь, что обрабатываете их все), (2) соблюдайте обычный поток событий касания (например, touchesBegan, чем некоторое количество {touchesBegan, touchesMoved, touchesEnded}, завершенных touchesEnded или touchesCancelled), особенно при работе с UIScrollView. №2 может быть непростым.

Если вы решите, что событие предназначено для UIScrollView, другой трюк состоит в том, чтобы заставить UIScrollView поверить, что ваш жест двумя пальцами на самом деле является жестом одним пальцем (поскольку UIScrollView не может прокручиваться двумя пальцами). Попробуйте передать в super только данные для одного пальца (путем фильтрации аргумента (NSSet *)touches - обратите внимание, что он содержит только измененные касания - и полностью игнорируя события для неправильного пальца).

Если это не сработает, у вас проблемы. Теоретически вы можете попытаться создать искусственные штрихи для передачи в UIScrollView, создав класс, похожий на UITouch. Базовый код C не проверяет типы, поэтому, возможно, приведение (YourTouch *) к (UITouch *) сработает, и вы сможете обманом заставить UIScrollView обрабатывать касания, которых на самом деле не было.

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

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

person Andrey Tarantsov    schedule 10.05.2009
comment
Андрей, спасибо за объяснение. Я фактически заставил его работать, передав подмножество касаний в super ... Я создал NSSet всего из одного события касания и передал его. Однако должен ли я передавать в этом вызове одно и то же событие UIEvent? Или мне нужно что-то изменить в мероприятии перед его передачей? Я получаю неожиданное поведение при прокрутке назад, и я думаю, что это из-за UIevent, которое я передаю в super, - это исходное событие, а NSSet касаний изменен, чтобы иметь только одно касание. - person Craig; 12.06.2009
comment
Крейд, я не верю, что вы можете изменить UIEvent, поэтому, если вы правы в отношении причины, у вас проблемы. :) - person Andrey Tarantsov; 14.06.2009

В SDK 3.2 обработка касания для UIScrollView обрабатывается с помощью распознавателей жестов.

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

for (UIGestureRecognizer *gestureRecognizer in scrollView.gestureRecognizers) {     
    if ([gestureRecognizer  isKindOfClass:[UIPanGestureRecognizer class]]) {
        UIPanGestureRecognizer *panGR = (UIPanGestureRecognizer *) gestureRecognizer;
        panGR.minimumNumberOfTouches = 2;               
    }
}
person Kenshi    schedule 04.07.2010
comment
Я пробовал это, но прокрутка по какой-то причине обнаруживает любые множественные касания как отдельные одиночные касания (т. Е. Прокрутка двумя пальцами определяется как два одиночных пальца), поэтому жест никогда не вызывается. Любые идеи? - person Dyldo42; 09.08.2010
comment
Я заставил это решение работать, установив также: panGR.maximumNumberOfTouches = 2; Кажется, что он установлен на единицу. Это, безусловно, самый простой способ заставить работать двойную прокрутку пальцем. - person Adam; 07.01.2011
comment
Это отлично сработало для меня. Намного чище, чем создание подкласса UIScrollView, чтобы внести это простое изменение. - person zachzurn; 20.07.2011
comment
Фантастическая штука - очень быстрое и простое решение этой проблемы. - person Luke; 01.11.2011
comment
Я попробовал все это и получил следующее: игнорирование вызова [UIPanGestureRecognizer setTranslation: inView:], поскольку распознаватель жестов не активен, мультитач включен - person h1vpdata; 02.08.2012
comment
абсолютно офигенный ответ ...! - person Prashant Nikam; 11.04.2014

Для iOS 5+ установка этого свойства имеет тот же эффект, что и ответ Майка Лоуренса:

self.scrollView.panGestureRecognizer.minimumNumberOfTouches = 2;

PanGestureRecognizer игнорирует перетаскивание одним пальцем, поэтому событие перетаскивания одним пальцем передается в представление содержимого.

person Guto Araujo    schedule 04.08.2013
comment
Согласен, это сразу устранило мою проблему. Спасибо, Гуто! - person David Peat; 05.06.2016
comment
Верно. Кто сейчас пользуется iOS 4? - person Nicolas Miari; 31.10.2016

В iOS 3.2+ теперь можно довольно легко выполнять прокрутку двумя пальцами. Просто добавьте распознаватель жестов панорамирования в представление прокрутки и установите для параметра maximumNumberOfTouches значение 1. Он будет требовать все прокрутки одним пальцем, но позволит прокрутки пальцами 2+ проходить вверх по цепочке во встроенный распознаватель жестов прокрутки (и, таким образом, разрешить нормальную прокрутку).

UIPanGestureRecognizer *panGestureRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(recognizePan:)];
panGestureRecognizer.maximumNumberOfTouches = 1;
[scrollView addGestureRecognizer:panGestureRecognizer];
[panGestureRecognizer release];
person Mike Laurence    schedule 25.09.2010
comment
Хорошо работает. Хотя пока вид все еще прокручивается от предыдущего жеста двумя пальцами, размещение одного пальца на экране продолжает перетаскивать вид. Есть ли способ предотвратить это? - person sharvey; 07.11.2010
comment
@sharvey, вы можете свести к минимуму это, удалив подпрыгивание. Я все еще ищу другие пути ... - person Dan Rosenstark; 09.03.2011
comment
@Yar Вы когда-нибудь находили другой способ предотвратить это? - person Sophie Alpert; 11.06.2011
comment
Мне кажется, это лучший вариант из всех ответов на этой странице. - person AndyDunn; 04.08.2012

Эти ответы - беспорядок, поскольку вы можете найти правильный ответ, только прочитав все остальные ответы и комментарии (самый близкий ответ получил вопрос задом наперед). Принятый ответ слишком расплывчат, чтобы быть полезным, и предлагает другой метод.

Синтезируя, это работает

    // makes it so that only two finger scrolls go
    for (id gestureRecognizer in self.gestureRecognizers) {     
        if ([gestureRecognizer  isKindOfClass:[UIPanGestureRecognizer class]])
        {
            UIPanGestureRecognizer *panGR = gestureRecognizer;
            panGR.minimumNumberOfTouches = 2;              
            panGR.maximumNumberOfTouches = 2;
        }
    }   

Для прокрутки потребуется два пальца. Я сделал это в подклассе, но если нет, просто замените self.gestureRecognizers на myScrollView.gestureRecognizers, и все готово.

Единственное, что я добавил, - это использовать id, чтобы избежать некрасивого приведения :)

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

person Dan Rosenstark    schedule 09.03.2011
comment
У меня это не работает, просто UIScrollView вообще не прокручивается. - person Rik Smith-Unna; 24.06.2012
comment
@RichardSmith вернитесь к простейшему возможному примеру и протестируйте его. Это определенно работает для прокрутки двумя пальцами. - person Dan Rosenstark; 25.06.2012

нам удалось реализовать аналогичную функциональность в нашем приложении для рисования на iPhone, создав подкласс UIScrollView и фильтруя события в зависимости от количества касаний простым и грубым способом:

//OCRScroller.h
@interface OCRUIScrollView: UIScrollView
{
    double pass2scroller;
}
@end

//OCRScroller.mm
@implementation OCRUIScrollView
- (id)initWithFrame:(CGRect)aRect {
    pass2scroller = 0;
    UIScrollView* newv = [super initWithFrame:aRect];
    return newv;
}
- (void)setupPassOnEvent:(UIEvent *)event {
    int touch_cnt = [[event allTouches] count];
    if(touch_cnt<=1){
        pass2scroller = 0;
    }else{
        double timems = double(CACurrentMediaTime()*1000);
        pass2scroller = timems+200;
    }
}

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    [self setupPassOnEvent:event];
    [super touchesBegan:touches withEvent:event];
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
    [self setupPassOnEvent:event];
    [super touchesMoved:touches withEvent:event];   
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
    pass2scroller = 0;
    [super touchesEnded:touches withEvent:event];
}


- (BOOL)touchesShouldBegin:(NSSet *)touches withEvent:(UIEvent *)event inContentView:(UIView *)view
{
    return YES;
}

- (BOOL)touchesShouldCancelInContentView:(UIView *)view
{
    double timems = double(CACurrentMediaTime()*1000);
    if (pass2scroller == 0 || timems> pass2scroller){
        return NO;
    }
    return YES;
}
@end

ScrollView настроен следующим образом:

scroll_view = [[OCRUIScrollView alloc] initWithFrame:rect];
scroll_view.contentSize = img_size;
scroll_view.contentOffset = CGPointMake(0,0);
scroll_view.canCancelContentTouches = YES;
scroll_view.delaysContentTouches = NO;
scroll_view.scrollEnabled = YES;
scroll_view.bounces = NO;
scroll_view.bouncesZoom = YES;
scroll_view.maximumZoomScale = 10.0f;
scroll_view.minimumZoomScale = 0.1f;
scroll_view.delegate = self;
self.view = scroll_view;

простое касание ничего не делает (вы можете справиться с этим так, как вам нужно), касание двумя пальцами прокручивает / увеличивает масштаб, как и ожидалось. GestureRecognizer не используется, поэтому работает с iOS 3.1

person IPv6    schedule 10.03.2011

У меня есть дальнейшее улучшение кода выше. Проблема заключалась в том, что даже после того, как мы установили setCanCancelContentTouches:NO У нас есть проблема, что жест масштабирования прерывает отображение содержимого. Это не отменит касание контента, но позволит увеличить масштаб. Чтобы предотвратить это, я блокирую масштабирование, устанавливая минимальное масштабирование и максимальное масштабирование на одни и те же значения каждый раз, когда срабатывает таймер.

A quite strange behavior is that when a one finger event gets canceled by a two finger gesture within the allowed time period, the timer will be delayed. It gets fired after the touchCanceled Event gets called. So we have the problem, that we try to lock the zooming although the event is already canceled and therefore disable zooming for the next event. To handle this behavior the timer callback method checks against if touchesCanceled was called before. @implementation JWTwoFingerScrollView

#pragma mark -
#pragma mark Event Passing


- (id)initWithCoder:(NSCoder *)coder {
    self = [super initWithCoder:coder];
    if (self) {
        for (UIGestureRecognizer* r in self.gestureRecognizers) {
            if ([r isKindOfClass:[UIPanGestureRecognizer class]]) {
                [((UIPanGestureRecognizer*)r) setMaximumNumberOfTouches:2];
                [((UIPanGestureRecognizer*)r) setMinimumNumberOfTouches:2];
                zoomScale[0] = -1.0;
                zoomScale[1] = -1.0;
            }
            timerWasDelayed = NO;
        }
    }
    return self;
}
-(void)lockZoomScale {    
    zoomScale[0] = self.minimumZoomScale;
    zoomScale[1] = self.maximumZoomScale;
    [self setMinimumZoomScale:self.zoomScale];
    [self setMaximumZoomScale:self.zoomScale];
        NSLog(@"locked %.2f %.2f",self.minimumZoomScale,self.maximumZoomScale);
}
-(void)unlockZoomScale {
    if (zoomScale[0] != -1 && zoomScale[1] != -1) {
        [self setMinimumZoomScale:zoomScale[0]];
        [self setMaximumZoomScale:zoomScale[1]];
        zoomScale[0] = -1.0;
        zoomScale[1] = -1.0;
        NSLog(@"unlocked %.2f %.2f",self.minimumZoomScale,self.maximumZoomScale);
    }
}

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    NSLog(@"began %i",[event allTouches].count);
    [self setCanCancelContentTouches:YES];
     if ([event allTouches].count == 1){
         touchesBeganTimer = [NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:@selector(firstTouchTimerFired:) userInfo:nil repeats:NO];
         [touchesBeganTimer retain];
         [touchFilter touchesBegan:touches withEvent:event];
     }
 }

//if one finger touch gets canceled by two finger touch, this timer gets delayed
// so we can! use this method to disable zooming, because it doesnt get called when two finger touch events are wanted; otherwise we would disable zooming while zooming
-(void)firstTouchTimerFired:(NSTimer*)timer {
    NSLog(@"fired");
    [self setCanCancelContentTouches:NO];
    //if already locked: unlock
    //this happens because two finger gesture delays timer until touch event finishes.. then we dont want to lock!
    if (timerWasDelayed) {
        [self unlockZoomScale];
    }
    else {
        [self lockZoomScale];
    }
    timerWasDelayed = NO;
 }

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
//    NSLog(@"moved %i",[event allTouches].count);
    [touchFilter touchesMoved:touches withEvent:event];
}

 - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
    NSLog(@"ended %i",[event allTouches].count);
    [touchFilter touchesEnded:touches withEvent:event];
    [self unlockZoomScale];
 }

 //[self setCanCancelContentTouches:NO];
 -(void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {
    NSLog(@"canceled %i",[event allTouches].count);
    [touchFilter touchesCancelled:touches withEvent:event];
    [self unlockZoomScale];
     timerWasDelayed = YES;
 }

@end

person Jan    schedule 28.12.2011

Плохие новости: iPhone SDK 3.0 и выше, больше не передавайте прикосновения к методам подкласса -touchesBegan: и -touchesEnded: ** _ 3 _ **. Вы можете использовать методы touchesShouldBegin и touchesShouldCancelInContentView, которые не совпадают.

Если вы действительно хотите получить эти штрихи, сделайте один хак, который позволит это.

В вашем подклассе UIScrollView переопределите метод hitTest следующим образом:

- (UIView *)hitTest:(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;
}

Это передаст вам подкласс this touch, однако вы не сможете отменить прикосновения к суперклассу UIScrollView.

person SEQOY Development Team    schedule 03.11.2009

Что я делаю, так это то, что мой контроллер представления настроил представление прокрутки:

[scrollView setCanCancelContentTouches:NO];
[scrollView setDelaysContentTouches:NO];

И в моем детском представлении у меня есть таймер, потому что прикосновения двумя пальцами обычно начинаются с одного пальца, за которым быстро следует два пальца:

- (void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    // Hand tool or two or more touches means a pan or zoom gesture.
    if ((selectedTool == kHandToolIndex) || (event.allTouches.count > 1)) {
        [[self parentScrollView] setCanCancelContentTouches:YES];
        [firstTouchTimer invalidate];
        firstTouchTimer = nil;
        return;
    }

    // Use a timer to delay first touch because two-finger touches usually start with one touch followed by a second touch.
    [[self parentScrollView] setCanCancelContentTouches:NO];
    anchorPoint = [[touches anyObject] locationInView:self];
    firstTouchTimer = [NSTimer scheduledTimerWithTimeInterval:kFirstTouchTimeInterval target:self selector:@selector(firstTouchTimerFired:) userInfo:nil repeats:NO];
    firstTouchTimeStamp = event.timestamp;
}

Если второе событие touchesBegan: поступает с более чем одним пальцем, просмотр прокрутки может отменить касания. Поэтому, если пользователь панорамирует двумя пальцами, это представление получит сообщение touchesCanceled:.

person lucius    schedule 12.01.2010

Кажется, это лучший ресурс для ответа на этот вопрос в Интернете. Еще одно близкое решение можно найти здесь.

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

Следующий процесс обеспечит:

  • UIScrollView, содержащий ваше собственное представление

  • Масштабирование и панорамирование двумя пальцами (через UIPinchGestureRecognizer)

  • Обработка событий вашего представления для всех остальных касаний

Во-первых, предположим, что у вас есть контроллер представления и его представление. В IB сделайте представление подчиненным представлением scrollView и настройте правила изменения размера представления, чтобы его размер не изменялся. В атрибутах scrollview включите все, что написано «bounce», и выключите off «delaysContentTouches». Также вы должны установить минимальное и максимальное масштабирование, отличное от значения по умолчанию 1.0, поскольку, как говорится в документации Apple, это требуется для работы масштабирования.

Создайте собственный подкласс UIScrollView и сделайте этот scrollview этим настраиваемым подклассом. Добавьте выход к вашему контроллеру представления для просмотра прокрутки и подключите их. Теперь вы полностью настроены.

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

#pragma mark -
#pragma mark Event Passing

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    [self.nextResponder touchesBegan:touches withEvent:event];
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
    [self.nextResponder touchesMoved:touches withEvent:event];
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
    [self.nextResponder touchesEnded:touches withEvent:event];
}
- (BOOL)touchesShouldCancelInContentView:(UIView *)view {
    return NO;
}

Добавьте этот код в свой контроллер представления:

- (void)setupGestures {
    UIPinchGestureRecognizer *pinchGesture = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:@selector(handlePinchGesture:)];
    [self.view addGestureRecognizer:pinchGesture];
    [pinchGesture release];
}

- (IBAction)handlePinchGesture:(UIPinchGestureRecognizer *)sender {
if ( sender.state == UIGestureRecognizerStateBegan ) {
    //Hold values
    previousLocation = [sender locationInView:self.view];
    previousOffset = self.scrollView.contentOffset;
    previousScale = self.scrollView.zoomScale;
} else if ( sender.state == UIGestureRecognizerStateChanged ) {
    //Zoom
    [self.scrollView setZoomScale:previousScale*sender.scale animated:NO];

    //Move
    location = [sender locationInView:self.view];
    CGPoint offset = CGPointMake(previousOffset.x+(previousLocation.x-location.x), previousOffset.y+(previousLocation.y-location.y));
    [self.scrollView setContentOffset:offset animated:NO];  
} else {
    if ( previousScale*sender.scale < 1.15 && previousScale*sender.scale > .85 )
        [self.scrollView setZoomScale:1.0 animated:YES];
}

}

Обратите внимание, что в этом методе есть ссылки на ряд свойств, которые вы должны определить в файлах классов вашего контроллера представления:

  • CGFloat previousScale;
  • CGPoint previousOffset;
  • CGPoint previousLocation;
  • CGPoint location;

Хорошо, вот и все!

К сожалению, мне не удалось заставить scrollView отображать свои скроллеры во время жеста. Я испробовал все эти стратегии:

//Scroll indicators
self.scrollView.showsVerticalScrollIndicator = YES;
self.scrollView.showsVerticalScrollIndicator = YES;
[self.scrollView flashScrollIndicators];
[self.scrollView setNeedsDisplay];

Одна вещь, которая мне действительно понравилась, - если вы посмотрите на последнюю строку, вы заметите, что она захватывает любое окончательное масштабирование, составляющее около 100%, и просто округляет его до этого. Вы можете отрегулировать свой уровень толерантности; Я видел это в поведении масштабирования Pages и подумал, что это было бы неплохо.

person SG1    schedule 25.05.2010
comment
Что такое агрессивное подклассирование? - person Dan Rosenstark; 09.03.2011

Я поместил это в метод viewDidLoad, и это завершает просмотр прокрутки, обрабатывающий поведение двух касаний панорамирования, и другой обработчик жестов панорамирования, обрабатывающий поведение панорамирования одним касанием ->

scrollView.panGestureRecognizer.minimumNumberOfTouches = 2

let panGR = UIPanGestureRecognizer(target: self, action: #selector(ViewController.handlePan(_:)))
panGR.minimumNumberOfTouches = 1
panGR.maximumNumberOfTouches = 1

scrollView.gestureRecognizers?.append(panGR)

а в методе handlePan, который является функцией, прикрепленной к ViewController, есть просто оператор печати, чтобы убедиться, что метод вводится ->

@IBAction func handlePan(_ sender: UIPanGestureRecognizer) {
    print("Entered handlePan numberOfTuoches: \(sender.numberOfTouches)")
}

HTH

person Adam Freeman    schedule 02.10.2017

Посмотрите мой решение:

#import “JWTwoFingerScrollView.h”

@implementation JWTwoFingerScrollView

- (id)initWithCoder:(NSCoder *)coder {
    self = [super initWithCoder:coder];
    if (self) {
        for (UIGestureRecognizer* r in self.gestureRecognizers) {
            NSLog(@“%@”,[r class]);
            if ([r isKindOfClass:[UIPanGestureRecognizer class]]) {
                [((UIPanGestureRecognizer*)r) setMaximumNumberOfTouches:2];
                [((UIPanGestureRecognizer*)r) setMinimumNumberOfTouches:2];
            }
        }
    }
    return self;
}

-(void)firstTouchTimerFired:(NSTimer*)timer {
    [self setCanCancelContentTouches:NO];
}

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    [self setCanCancelContentTouches:YES];
    if ([event allTouches].count == 1){
        touchesBeganTimer = [NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:@selector(firstTouchTimerFired:) userInfo: nil repeats:NO];
        [touchesBeganTimer retain];
        [touchFilter touchesBegan:touches withEvent:event];
    }
}

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
    [touchFilter touchesMoved:touches withEvent:event];
}

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
    NSLog(@“ended %i”,[event allTouches].count);
    [touchFilter touchesEnded:touches withEvent:event];
}

-(void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {
    NSLog(@“canceled %i”,[event allTouches].count);
    [touchFilter touchesCancelled:touches withEvent:event];
}

@end

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

person Jan    schedule 15.12.2011

Да, вам нужно создать подкласс UIScrollView и переопределить его методы -touchesBegan: и -touchesEnded:, чтобы передать штрихи «вверх». Это, вероятно, также будет включать подкласс, имеющий переменную-член UIView, чтобы он знал, что означает передать штрихи.

person anisoptera    schedule 24.04.2009
comment
Вы можете пояснить, что вы имеете в виду, Патрик? У меня есть подкласс UIScrollView, но я не знаю, как реализовать прокрутку самостоятельно (переместить фрейм прокрутки?), И я не уверен, как передавать события в мой UIView (контент). Прямо сейчас его методы Responder никогда не вызываются. - person Craig; 25.04.2009
comment
У вас будет переменная-член UIView в подклассе UIScrollView, которую вы назначаете при создании подкласса. Затем реализуйте -touchesBegan, -touchesMoved и -touchesEnded и попросите их просто передать событие (путем вызова [uiViewMemberVariable touchesBegan: ...]), если они не знают, что это для них. - person anisoptera; 27.04.2009
comment
О, и если вы знаете, что это для ScrollView, тогда просто сделайте [super touchBegan: ...] в нем. - person anisoptera; 27.04.2009
comment
touchesmoved и друзья не работают в uiscrollview начиная с версии 3.0. Вы можете получить частичную операцию, но это не рекомендуется - person twerdster; 23.11.2010

Ответ Кенши в Swift 4

for gestureRecognizer: UIGestureRecognizer in self.gestureRecognizers! {
    if (gestureRecognizer is UIPanGestureRecognizer) {
        let panGR = gestureRecognizer as? UIPanGestureRecognizer
        panGR?.minimumNumberOfTouches = 2
    }
}
person Ty Lertwichaiworawit    schedule 22.12.2017