[ios] UIPanGestureRecognizer-세로 또는 가로 만

나는이 가지고있는보기가 UIPanGestureRecognizer수직으로 뷰를 드래그합니다. 인식기 콜백에서는 y 좌표 만 업데이트하여 이동합니다. 이 뷰의 슈퍼 뷰 UIPanGestureRecognizer에는 뷰를 가로로 드래그하여 x 좌표 만 업데이트하는 뷰가 있습니다.

문제는 첫 번째 UIPanGestureRecognizer로 뷰를 세로로 이동하는 이벤트를 수행하므로 슈퍼 뷰 제스처를 사용할 수 없다는 것입니다.

나는 시도했다

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer
 shouldRecognizeSimultaneouslyWithGestureRecognizer:
                            (UIGestureRecognizer *)otherGestureRecognizer;

둘 다 작동하지만, 나는 그것을 원하지 않습니다. 움직임이 명확하게 수평 인 경우에만 수평을 감지하고 싶습니다. UIPanGestureRecognizer방향 속성이 있으면 좋을 것 입니다.

이 동작을 어떻게 달성 할 수 있습니까? 문서가 매우 혼란 스럽기 때문에 누군가가 여기에서 더 잘 설명 할 수 있습니다.



답변

세로 팬 제스처 인식기를 위해이 작업을 수행하면 나에게 효과적입니다.

- (BOOL)gestureRecognizerShouldBegin:(UIPanGestureRecognizer *)panGestureRecognizer {
    CGPoint velocity = [panGestureRecognizer velocityInView:someView];
    return fabs(velocity.y) > fabs(velocity.x);
}

그리고 스위프트의 경우 :

func gestureRecognizerShouldBegin(_ gestureRecognizer: UIPanGestureRecognizer) -> Bool {
    let velocity = gestureRecognizer.velocity(in: someView)
    return abs(velocity.x) > abs(velocity.y)
}


답변

제공된 @LocoMike와 같은 하위 클래스를 사용하여 솔루션을 만들었지 만 @Hejazi가 제공 한 초기 속도를 통해보다 효과적인 탐지 메커니즘을 사용했습니다. 나는 또한 Swift를 사용하고 있지만 원하는 경우 Obj-C로 쉽게 번역 할 수 있어야합니다.

다른 솔루션에 비해 장점 :

  • 다른 서브 클래 싱 솔루션보다 간단하고 간결합니다. 관리 할 추가 상태가 없습니다.
  • 시작 동작을 보내기 전에 방향 감지가 발생하므로 잘못된 방향으로 스 와이프하면 팬 제스처 선택기가 메시지를받지 않습니다.
  • 초기 방향이 결정된 후에는 방향 논리가 더 이상 참조되지 않습니다. 초기 방향이 올바른 경우 인식기를 활성화하는 것이 일반적으로 바람직한 동작이지만, 사용자의 손가락이 방향을 따라 완벽하게 움직이지 않으면 시작된 제스처를 취소하지 않습니다.

코드는 다음과 같습니다.

import UIKit.UIGestureRecognizerSubclass

enum PanDirection {
    case vertical
    case horizontal
}

class PanDirectionGestureRecognizer: UIPanGestureRecognizer {

    let direction: PanDirection

    init(direction: PanDirection, target: AnyObject, action: Selector) {
        self.direction = direction
        super.init(target: target, action: action)
    }

    override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent) {
        super.touchesMoved(touches, with: event)

        if state == .began {
            let vel = velocity(in: view)
            switch direction {
            case .horizontal where fabs(vel.y) > fabs(vel.x):
                state = .cancelled
            case .vertical where fabs(vel.x) > fabs(vel.y):
                state = .cancelled
            default:
                break
            }
        }
    }
}

사용 예 :

let panGestureRecognizer = PanDirectionGestureRecognizer(direction: .horizontal, target: self, action: #selector(handlePanGesture(_:)))
panGestureRecognizer.cancelsTouchesInView = false
self.view.addGestureRecognizer(panGestureRecognizer)

func handlePanGesture(_ pan: UIPanGestureRecognizer) {
    let percent = max(pan.translation(in: view).x, 0) / view.frame.width

    switch pan.state {
    case .began:
    ...
}


답변

UIPanGestureRecognizer의 서브 클래스를 생성하는 것으로 파악했습니다.

DirectionPanGestureRecognizer :

#import <Foundation/Foundation.h>
#import <UIKit/UIGestureRecognizerSubclass.h>

typedef enum {
    DirectionPangestureRecognizerVertical,
    DirectionPanGestureRecognizerHorizontal
} DirectionPangestureRecognizerDirection;

@interface DirectionPanGestureRecognizer : UIPanGestureRecognizer {
    BOOL _drag;
    int _moveX;
    int _moveY;
    DirectionPangestureRecognizerDirection _direction;
}

@property (nonatomic, assign) DirectionPangestureRecognizerDirection direction;

@end

DirectionPanGestureRecognizer.m :

#import "DirectionPanGestureRecognizer.h"

int const static kDirectionPanThreshold = 5;

@implementation DirectionPanGestureRecognizer

@synthesize direction = _direction;

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
    [super touchesMoved:touches withEvent:event];
    if (self.state == UIGestureRecognizerStateFailed) return;
    CGPoint nowPoint = [[touches anyObject] locationInView:self.view];
    CGPoint prevPoint = [[touches anyObject] previousLocationInView:self.view];
    _moveX += prevPoint.x - nowPoint.x;
    _moveY += prevPoint.y - nowPoint.y;
    if (!_drag) {
        if (abs(_moveX) > kDirectionPanThreshold) {
            if (_direction == DirectionPangestureRecognizerVertical) {
                self.state = UIGestureRecognizerStateFailed;
            }else {
                _drag = YES;
            }
        }else if (abs(_moveY) > kDirectionPanThreshold) {
            if (_direction == DirectionPanGestureRecognizerHorizontal) {
                self.state = UIGestureRecognizerStateFailed;
            }else {
                _drag = YES;
            }
        }
    }
}

- (void)reset {
    [super reset];
    _drag = NO;
    _moveX = 0;
    _moveY = 0;
}

@end

사용자가 선택한 비헤이비어에서 드래그를 시작하면 제스처가 트리거됩니다. 방향 속성을 올바른 값으로 설정하면 모든 설정이 완료됩니다.


답변

UIPanGestureRecognizer를 사용하여 유효한 영역을 가로로 제한하려고했습니다.

- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
{
    if ([gestureRecognizer isKindOfClass:[UIPanGestureRecognizer class]]) {

        UIPanGestureRecognizer *panGesture = (UIPanGestureRecognizer *)gestureRecognizer;
        CGPoint velocity = [panGesture velocityInView:panGesture.view];

        double radian = atan(velocity.y/velocity.x);
        double degree = radian * 180 / M_PI;

        double thresholdAngle = 20.0;
        if (fabs(degree) > thresholdAngle) {
            return NO;
        }
    }
    return YES;
}

그런 다음 thresholdAngle 도 내에서 가로 로만 스 와이프 하면이 팬 제스처가 트리거 될 수 있습니다.


답변

Swift 3.0 답변 : 수직 제스처 만 처리합니다.

    override func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
    if let pan = gestureRecognizer as? UIPanGestureRecognizer {
        let velocity = pan.velocity(in: self)
        return fabs(velocity.y) > fabs(velocity.x)
    }
    return true

}


답변

다음 솔루션은 내 문제를 해결했습니다.

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
    if ([gestureRecognizer.view isEqual:self.view] && [otherGestureRecognizer.view isEqual:self.tableView]) {
        return NO;
    }
    return YES;
}

이것은 실제로 팬이 기본보기 또는 tableView에서 진행되고 있는지 확인하는 것입니다.


답변

게으른에 대한 리의 답변 스위프트 3 버전

import UIKit
import UIKit.UIGestureRecognizerSubclass

enum PanDirection {
    case vertical
    case horizontal
}

class UIPanDirectionGestureRecognizer: UIPanGestureRecognizer {

    let direction : PanDirection

    init(direction: PanDirection, target: AnyObject, action: Selector) {
        self.direction = direction
        super.init(target: target, action: action)
    }

    override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent) {
        super.touchesMoved(touches, with: event)

        if state == .began {

            let vel = velocity(in: self.view!)
            switch direction {
            case .horizontal where fabs(vel.y) > fabs(vel.x):
                state = .cancelled
            case .vertical where fabs(vel.x) > fabs(vel.y):
                state = .cancelled
            default:
                break
            }
        }
    }
}