Можно ли объединить UIPinchGestureRecognizer и UIPanGestureRecognizer? - PullRequest
8 голосов
/ 08 февраля 2011

Я немного пытаюсь выяснить, возможно ли создать единый комбинированный распознаватель жестов, который объединяет UIPinchGestureRecognizer с UIPanGestureRecognizer.

Я использую панорамирование для трансляции вида и пинч для масштабирования вида. Я делаю последовательную матричную конкатенацию, чтобы получить результирующую окончательную матрицу преобразования, которая применяется к представлению. Эта матрица имеет как масштаб, так и перевод. Использование отдельных распознавателей жестов приводит к нервному движению / масштабированию. Не то, что я хочу. Таким образом, я хочу обработать объединение масштаба и перевода один раз за один жест. Может кто-нибудь, пожалуйста, пролить свет на то, как это сделать?

1 Ответ

17 голосов
/ 27 марта 2011

6/14/14: обновлен пример кода для iOS 7+ с ARC.

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

Загрузить проект Xcode

Примечание: iOS 7 ведет себя странно с UIView в IB, к которым применены жесты Pan / Pinch / Rotate. IOS 8 исправляет это, но мойОбходной путь - добавить все представления в коде, как в этом примере кода.

Демонстрационное видео

Demo UIPinchGesture Video

  1. Добавить ихдля просмотра и соответствия протоколу UIGestureRecognizerDelegate

    @interface ViewController () <UIGestureRecognizerDelegate>
    
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad
    {
        [super viewDidLoad];
    
        UIView *blueView = [[UIView alloc] initWithFrame:CGRectMake(100, 100, 150, 150)];
        blueView.backgroundColor = [UIColor blueColor];
        [self.view addSubview:blueView];
        [self addMovementGesturesToView:blueView];
    
        // UIImageView's and UILabel's don't have userInteractionEnabled by default!
        UIImageView *imageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"BombDodge.png"]]; // Any image in Xcode project
        imageView.center = CGPointMake(100, 250);
        [imageView sizeToFit];
        [self.view addSubview:imageView];
        [self addMovementGesturesToView:imageView];
    
        // Note: Changing the font size would be crisper than zooming a font!
        UILabel *label = [[UILabel alloc] init];
        label.text = @"Hello Gestures!";
        label.font = [UIFont systemFontOfSize:30];
        label.textColor = [UIColor blackColor];
        [label sizeToFit];
        label.center = CGPointMake(100, 400);
        [self.view addSubview:label];
        [self addMovementGesturesToView:label];
    }
    
    - (void)addMovementGesturesToView:(UIView *)view {
        view.userInteractionEnabled = YES;  // Enable user interaction
    
        UIPanGestureRecognizer *panGesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handlePanGesture:)];
        panGesture.delegate = self;
        [view addGestureRecognizer:panGesture];
    
        UIPinchGestureRecognizer *pinchGesture = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:@selector(handlePinchGesture:)];
        pinchGesture.delegate = self;
        [view addGestureRecognizer:pinchGesture];
    }
    
  2. Реализация методов жестов

    - (void)handlePanGesture:(UIPanGestureRecognizer *)panGesture {
        CGPoint translation = [panGesture translationInView:panGesture.view.superview];
    
        if (UIGestureRecognizerStateBegan == panGesture.state ||UIGestureRecognizerStateChanged == panGesture.state) {
            panGesture.view.center = CGPointMake(panGesture.view.center.x + translation.x,
                                                 panGesture.view.center.y + translation.y);
            // Reset translation, so we can get translation delta's (i.e. change in translation)
            [panGesture setTranslation:CGPointZero inView:self.view];
        }
        // Don't need any logic for ended/failed/canceled states
    }
    
    - (void)handlePinchGesture:(UIPinchGestureRecognizer *)pinchGesture {
    
        if (UIGestureRecognizerStateBegan == pinchGesture.state ||
            UIGestureRecognizerStateChanged == pinchGesture.state) {
    
            // Use the x or y scale, they should be the same for typical zooming (non-skewing)
            float currentScale = [[pinchGesture.view.layer valueForKeyPath:@"transform.scale.x"] floatValue];
    
            // Variables to adjust the max/min values of zoom
            float minScale = 1.0;
            float maxScale = 2.0;
            float zoomSpeed = .5;
    
            float deltaScale = pinchGesture.scale;
    
            // You need to translate the zoom to 0 (origin) so that you
            // can multiply a speed factor and then translate back to "zoomSpace" around 1
            deltaScale = ((deltaScale - 1) * zoomSpeed) + 1;
    
            // Limit to min/max size (i.e maxScale = 2, current scale = 2, 2/2 = 1.0)
            //  A deltaScale is ~0.99 for decreasing or ~1.01 for increasing
            //  A deltaScale of 1.0 will maintain the zoom size
            deltaScale = MIN(deltaScale, maxScale / currentScale);
            deltaScale = MAX(deltaScale, minScale / currentScale);
    
            CGAffineTransform zoomTransform = CGAffineTransformScale(pinchGesture.view.transform, deltaScale, deltaScale);
            pinchGesture.view.transform = zoomTransform;
    
            // Reset to 1 for scale delta's
            //  Note: not 0, or we won't see a size: 0 * width = 0
            pinchGesture.scale = 1;
        }
    }
    
    - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
        return YES; // Works for most use cases of pinch + zoom + pan
    }
    

Ресурсы

...