UIPanGestureRecognizer - Yalnızca dikey veya yatay


146

Ben sahip bir görünüme sahip UIPanGestureRecognizerdikey görünüm sürüklemek. Tanıyıcı geri aramasında, yalnızca y koordinatını taşımak için güncellerim. Bu görünümün denetiminde UIPanGestureRecognizer, görünümü yalnızca x koordinatını güncelleyerek yatay olarak sürükleyecek bir görünüm bulunur.

Sorun, ilk UIPanGestureRecognizergörünümü dikey olarak taşımak için olay alıyor, bu yüzden denetim hareketini kullanamıyorum.

denedim

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

ve ikisi de işe yarayacak, ama bunu istemiyorum. Yatayın sadece hareket açıkça yatay olması durumunda algılanmasını istiyorum. Yani UIPanGestureRecognizerbir yön özelliği olsaydı harika olurdu .

Bu davranışı nasıl başarabilirim? Belgeleri çok kafa karıştırıcı buluyorum, belki birisi burada daha iyi açıklayabilir.


Çözümü anladıysanız, kendi sorunuzu cevaplamak ve cevabı kabul etmek uygundur.
jtbandes

@JoeBlow gerçekten mi? Öyleyse, belki de çeviri ve hareket hızını almak için hızlıca kaydırma hareketi kategorisi yaptınız?
Roman Truba

2
Ne dediğini anlamıyorum. Yatay kaydırmayı algılamak istiyorsanız , bu tamamen ve tamamen işletim sisteminde yerleşiktir . Tüm iş tamamen ve tamamen sizin için yapılır. Yapman gerek ... hiçbir şey! :) Sadece bu örnekte kodun iki satırını yapıştırın .. stackoverflow.com/a/20988648/294884 Sadece "sadece sağ" veya "her ikisi de" sol seçebileceğinizi unutmayın.
Fattie

Yanıtlar:


212

Sadece dikey kaydırma hareketi tanıyıcı için bunu yapın, benim için çalışır:

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

Ve Swift için:

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

3
bunu denedim, ancak çeviri genellikle == (0,0), bu yüzden kesin değil
zxcat

12
TranslationInView: yerine velocityInView: kullanıldığında (0,0) sorunu görülmez.
cbh2000

1
@ cbh2000 Bunun velocityInViewyerine kullanılacak cevabı güncelledim translationInView.
Hicazi

19
@JoeBlow Bir UISwipeGestureRecognizer, kaydırma hareketine yanıt olarak bir geçişi başlatmanın kolay bir yoludur, ancak ayrı bir harekettir. Birisi bir jestle geçişi canlandırmak için sürekli bir yaklaşım arıyorsa, bir UIPanGestureRecognizer gidilecek yoldur.
Levi McCallum

Bu akıllıca bir çözüm
Jakub Truhlář

79

Sağlanan cevap @LocoMike gibi alt sınıflama ile bir çözüm oluşturdum, ancak @Hejazi tarafından sağlanan başlangıç ​​hızı üzerinden daha etkili tespit mekanizmasını kullandım. Ayrıca Swift kullanıyorum, ancak istenirse Obj-C'ye çevirmek kolay olmalı.

Diğer çözümlere göre avantajları:

  • Diğer alt sınıf çözümlerinden daha basit ve daha özlü. Yönetilecek ek durum yok.
  • Yön algılama işlemi Başlamadan önce gönderilir, böylece kaydırma hareketi seçiciniz yanlış yön kaydırılırsa mesaj almaz.
  • İlk yön belirlendikten sonra, yön mantığına artık başvurulmaz. Bu, başlangıç ​​yönü doğruysa tanıyıcıyı etkinleştirmek için genellikle istenen davranışla sonuçlanır, ancak bir kullanıcının parmağı yön boyunca mükemmel bir şekilde hareket etmiyorsa, hareketi başladıktan sonra hareketi iptal etmez.

İşte kod:

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
            }
        }
    }
}

Kullanım örneği:

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:
    ...
}

4
Bu kesinlikle en iyi cevap. Apple'ın bu gibi bir işlevsellik eklememesi çok kötü UIPanGestureRecognizer.
NRitH

Bir kullanım örneği verebilir misiniz?
user82395214

Bu çok hoş! Teşekkürler! Hem yatay hem de dikey olarak istiflenirken mükemmel çalışır: let horizontalPanRecognizer = PanDirectionGestureRecognizer(direction: .horizontal, target: self, action: #selector(handleHorizontalPanGesture(recognizer:))) self.view?.addGestureRecognizer(horizontalPanRecognizer); let verticalPanRecognizer = PanDirectionGestureRecognizer(direction: .vertical, target: self, action: #selector(handleVerticalPanGesture(recognizer:))) self.view?.addGestureRecognizer(verticalPanRecognizer);
Han

Ah bu harika! Teşekkürler!
Baran Emre

51

Ben bir UIPanGestureRecognizer alt sınıfı oluşturmak anladım

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

Bu, yalnızca kullanıcı seçilen davranışta sürüklemeye başlarsa hareketi tetikler. Direction özelliğini doğru değere ayarlayın ve hepiniz ayarlandınız.


Bence 'sıfırlama' başlangıçta çağrılmıyor. Bir initWithTarget:action:yöntem eklendi ve reset denir ve hepsi iyi.
colinta

5
Geçerli uygulamada DirectionPanGestureRecognizer, siz ayarlamadığınız sürece hızlı sürüklemeleri göz ardı eder kDirectionPanThreshold = 20, bu durumda yanlış alarmlar verebilir. Sırasıyla yatay kasayı abs(_moveX) > abs(_moveY)yerine koymayı abs(_moveX) > kDirectionPanThresholdve değiştirmeyi öneririm .
Dennis Krut

2
Bu benim için de yararlı olduğunu eklemeliyim, ama pan jest tanıyıcı tetiklemek için eklemek zorunda ne _drag = YESekledi satırın altında if, başka bir parçası olduself.state = UIGestureRecognizerStateChanged;
bolnad

13

Geçerli alanı UIPanGestureRecognizer ile yatay olarak kısıtlamaya çalıştım.

- (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;
}

Daha sonra, yalnızca yatay olarak eşik açısı içinde kaydırmak bu kaydırma hareketini tetikleyebilir.


2
Mükemmel cevap. Bu gerçekten UIScrollView hareketlerini ve düzenli hareketleri karıştırırken bana yardımcı oldu. Ben örnek "enableThreshold" yerine "eşikAngle" demek anlamına geliyordu. Ve nadiren atan () kullanmalısınız çünkü bir NAN üretebilir. Bunun yerine atan2 () kullanın.
Brainware

9

Swift 3.0 cevabı: sadece dikey hareketi yapar

    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

}

6

Aşağıdaki çözüm sorunumu çözdü:

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

Bu aslında panın ana görünüme mi yoksa tableView'a mı gittiğini kontrol etmektir.


3
Neden -isEqual: iki görünümün aynı olup olmadığını karşılaştırmak için? Basit bir kimlik kontrolü yeterli olmalıdır. gestureRecognizer.view == self.view
openfrog

6

Lee'nin tembel cevap 3 Swift versiyonu

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
            }
        }
    }
}

4

Çektiğim Lee Goodrich bireyin cevabı ve ben özellikle tek yön tava gerektiği gibi genişletilmiş. Şöyle kullanın:let pan = PanDirectionGestureRecognizer(direction: .vertical(.up), target: self, action: #selector(handleCellPan(_:)))

Ayrıca, hangi kararların alındığını biraz daha netleştirmek için bazı yorumlar ekledim.

import UIKit.UIGestureRecognizerSubclass

enum PanVerticalDirection {
    case either
    case up
    case down
}

enum PanHorizontalDirection {
    case either
    case left
    case right
}

enum PanDirection {
    case vertical(PanVerticalDirection)
    case horizontal(PanHorizontalDirection)
}

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 {

            // expecting horizontal but moving vertical, cancel
            case .horizontal(_) where fabs(vel.y) > fabs(vel.x):
                state = .cancelled

            // expecting vertical but moving horizontal, cancel
            case .vertical(_) where fabs(vel.x) > fabs(vel.y):
                state = .cancelled

            // expecting horizontal and moving horizontal
            case .horizontal(let hDirection):
                switch hDirection {

                    // expecting left but moving right, cancel
                    case .left where vel.x > 0: state = .cancelled

                    // expecting right but moving left, cancel
                    case .right where vel.x < 0: state = .cancelled
                    default: break
                }

            // expecting vertical and moving vertical
            case .vertical(let vDirection):
                switch vDirection {
                    // expecting up but moving down, cancel
                    case .up where vel.y > 0: state = .cancelled

                    // expecting down but moving up, cancel
                    case .down where vel.y < 0: state = .cancelled
                    default: break
                }
            }
        }
    }
}

Hata yeri override func touchesMoved- Method does not override any method from its superclass.
AnBisw

@Annjawn "İçe aktarma UIKit.UIGestureRecognizerSubclass"
shawnynicole

Tamam. Bunun farkında değildim. İthalat UIKit'in otomatik olarak içe aktaracağını düşündüm. Bir deneyeceğim.
AnBisw

2

Üzerinde sürükleyerek yönünü bulabilirsiniz UIViewaracılığıyla UIPanGestureRecognizer. Lütfen kodu takip edin.

 - (void)viewDidLoad {
    [super viewDidLoad];
    flipFoward = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(doFlipForward:)];
    [flipFoward setMaximumNumberOfTouches:1];
    [flipFoward setMinimumNumberOfTouches:1];
    [flipFoward setDelegate:self];
    [self.view addGestureRecognizer:flipFoward];
    flipBack = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(doFlipBack:)];
    [flipBack setMaximumNumberOfTouches:1];
    [flipBack setMinimumNumberOfTouches:1];
    [flipBack setDelegate:self];
    [self.view addGestureRecognizer:flipBack];
}

#pragma mark -
#pragma mark RESPONDER

-(void)doFlipForward:(UIGestureRecognizer *)aGestureRecognizer{
    NSLog(@"doFlipForward");
    if([(UIPanGestureRecognizer*)aGestureRecognizer state] == UIGestureRecognizerStateBegan) {
        NSLog(@"UIGestureRecognizerStateBegan");
    }
    if([(UIPanGestureRecognizer*)aGestureRecognizer state] == UIGestureRecognizerStateChanged) {
        NSLog(@"UIGestureRecognizerStateChanged");
    }
    if([(UIPanGestureRecognizer*)aGestureRecognizer state] == UIGestureRecognizerStateEnded) {
        NSLog(@"UIGestureRecognizerStateEnded");
    }
}

-(void)doFlipBack:(UIGestureRecognizer *)aGestureRecognizer{
    NSLog(@"doFlipBack");
    if([(UIPanGestureRecognizer*)aGestureRecognizer state] == UIGestureRecognizerStateBegan) {
        NSLog(@"UIGestureRecognizerStateBegan1");
    }
    if([(UIPanGestureRecognizer*)aGestureRecognizer state] == UIGestureRecognizerStateChanged) {
        NSLog(@"UIGestureRecognizerStateChanged1");
    }
    if([(UIPanGestureRecognizer*)aGestureRecognizer state] == UIGestureRecognizerStateEnded) {
        NSLog(@"UIGestureRecognizerStateEnded1");
    }
}

#pragma mark -
#pragma mark DELEGATE

-(BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer{
    CGSize size = [self.view bounds].size;
    CGFloat touchX = [gestureRecognizer locationInView:self.view].x;
    if((gestureRecognizer == flipFoward) 
       && touchX >= (size.width - 88.0f))
    {
        return YES;
    }
    if((gestureRecognizer == flipBack)
       && touchX <= 88.0f)
    {
        return YES;
    }
    return NO;
}

Aslında bu iyi bir çözüm değil çünkü sadece soldan 88 puan kaydırılabiliyor.
Borut Tomazin

2

Hızlı 4.2

Çözüm, yatay kaydırma hareketiyle yalnızca dikey olarak yatay kaydırma hareketini desteklemek içindir.

let pan = UIPanGestureRecognizer(target: self, action: #selector(test1))
pan.cancelsTouchesInView = false
panView.addGestureRecognizer(pan)

Çözüm 1 :

@objc func panAction(pan: UIPanGestureRecognizer) {

        let velocity = pan.velocity(in: panView)
        guard abs(velocity.y) > abs(velocity.x) else {
            return
        }
}

Çözüm 2:

  [UISwipeGestureRecognizer.Direction.left, .right].forEach { direction in
        let swipe = UISwipeGestureRecognizer(target: self, action: #selector(swipeAction))
        swipe.direction = direction
        panView.addGestureRecognizer(swipe)
        pan.require(toFail: swipe)
    }

Ardından kaydırma hareketi kaydırma hareketini yutacaktır. Tabii ki, hiçbir şey yapmanıza gerek yok swipeAction.


1

İşte nasıl çözdüm:

Önce Eşzamanlı olarak PanGesture Tanıma'yı etkinleştirdim.

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

return YES;

Sonra Yatay ve Dikey Pan hareketlerini İzole Ediyorum (akümülatör NSMutableArray özelliğidir):

- (void)verticalPan :(UIPanGestureRecognizer *) sender {

CGPoint touch  = [sender translationInView:self];
NSValue *value = [NSValue valueWithCGPoint:touch];
[accumulator addObject:value];

int firstXObjectValue = (int)[[accumulator objectAtIndex:0] CGPointValue].x ;
int lastXObjectValue =  (int)[[accumulator lastObject] CGPointValue].x;

int firstYObjectValue = (int)[[accumulator objectAtIndex:0] CGPointValue].y;
int lastYObjectValue =  (int)[[accumulator lastObject] CGPointValue].y;

if (abs(lastYObjectValue - firstYObjectValue) < 4 && abs(lastXObjectValue - firstXObjectValue) > 4) {
    NSLog(@"Horizontal Pan");

    //do something here
}
else if (abs(lastYObjectValue - firstYObjectValue) > 4 && abs(lastXObjectValue - firstXObjectValue) < 4){
    NSLog(@"Vertical Pan");

    //do something here
}

if (accumulator.count > 3)
    [accumulator removeAllObjects];

Burada bir örnek verdim:

scrollview'da özel kaydırma ekle


1
let pangesture = UIPanGestureRecognizer(target: self, action: "dragview:")
yourview.addGestureRecognizer(pangesture)


func dragview(panGestureRecognizer:UIPanGestureRecognizer)
{
    let touchlocation = panGestureRecognizer.locationInView(parentview)
    yourview.center.y = touchlocation.y //x for horizontal 
}

1

Basit kullanabilirsiniz panGestureRecognizer. Kullanmaya pandirectionregognizerveya başka bir şeye gerek yok . Sadece translationInview Kodun altındaki y değerini kullan Sürükleme görünümünü yalnızca yukarı ve aşağı hareket ettirin

- (void)gesturePan_Handle:(UIPanGestureRecognizer *)gesture {
    if (gesture.state == UIGestureRecognizerStateChanged) {
        CGPoint translation = [gesture translationInView:gesture.view];
        recognizer.view.center = CGPointMake(recognizer.view.center.x, recognizer.view.center.y + translation.y);
        [gesture setTranslation:CGPointMake(0, 0) inView:gesture.view];
    }
}

Bu kod görünümü tarar. Yön kilidi yoktur.
zakishaheen

1
- (void)dragAction:(UIPanGestureRecognizer *)gesture{
      UILabel *label = (UILabel *)gesture.view;
      CGPoint translation = [gesture translationInView:label];
     label.center = CGPointMake(label.center.x + translation.x,
                             label.center.y + 0);
    [gesture setTranslation:CGPointZero inView:label];}

Yalnızca Yatay kaydırma gerektiren nesne için PanGestureRecognizer @ selector eylem yöntemini oluşturdum.

 UIPanGestureRecognizer *gesture = [[UIPanGestureRecognizer alloc]initWithTarget:self action:@selector(smileyDragged:)];
    [buttonObject addGestureRecognizer:gesture];

1

Hızlı yol

override func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
    if let panGestureRecognizer = gestureRecognizer as? UIPanGestureRecognizer {
        return isVerticalGesture(panGestureRecognizer)
    }
    return false
}

private func isVerticalGesture(_ recognizer: UIPanGestureRecognizer) -> Bool {
    let translation = recognizer.translation(in: superview!)
    if fabs(translation.y) > fabs(translation.x) {
        return true
    }
    return false
}

0

Orada tüm Swift kullanıcıları için, bu işi yapacak :)

import Foundation
import UIKit.UIGestureRecognizerSubclass


class DirectionPanGestureRecognizer: UIPanGestureRecognizer {

let kDirectionPanThreshold = CGFloat(5)
var drag = true
var moveX = CGFloat(0)
var moveY = CGFloat(0)

override init(target: AnyObject, action: Selector) {
    super.init(target: target, action: action)
}

override func touchesMoved(touches: NSSet, withEvent event: UIEvent) {
    super.touchesMoved(touches, withEvent: event)
    if state == .Failed {
        return
    }

    let nowPoint = touches.anyObject()?.locationInView(view)
    let prevPoint = touches.anyObject()?.previousLocationInView(view)
    moveX += prevPoint!.x - nowPoint!.x
    moveY += prevPoint!.y - nowPoint!.y
    if !drag {
        if abs(moveX) > kDirectionPanThreshold {
            state = .Failed
        } else {
            drag = true
        }

    }

}

 override func reset() {
    super.reset()
    moveX = 0
    moveY = 0
    drag = false
}




}

0

Lee Goodrich tarafından mükemmel bir cevap aldım ve Swift 3'e taşındım

import UIKit
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: 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

            }

        }
    }
}

0

Yaklaşımımı paylaşmak isterim çünkü diğer tüm yaklaşımlar UIGestureRecognizerDelegateya alt sınıfa dayalıdır UIPanGestureRecognizer.

Benim yaklaşımım çalışma zamanı ve dolandırıcılık üzerine kuruludur. Bu yaklaşımdan% 100 emin değilim, ama bunu kendiniz test edebilir ve geliştirebilirsiniz.

UIPanGestureRecognizerTek bir kod satırı ile herhangi birinin yönünü ayarlayın :

UITableView().panGestureRecognizer.direction = UIPanGestureRecognizer.Direction.vertical

kullanın pod 'UIPanGestureRecognizerDirection'veya kodu:

public extension UIPanGestureRecognizer {

    override open class func initialize() {
        super.initialize()
        guard self === UIPanGestureRecognizer.self else { return }
        func replace(_ method: Selector, with anotherMethod: Selector, for clаss: AnyClass) {
            let original = class_getInstanceMethod(clаss, method)
            let swizzled = class_getInstanceMethod(clаss, anotherMethod)
            switch class_addMethod(clаss, method, method_getImplementation(swizzled), method_getTypeEncoding(swizzled)) {
            case true:
                class_replaceMethod(clаss, anotherMethod, method_getImplementation(original), method_getTypeEncoding(original))
            case false:
                method_exchangeImplementations(original, swizzled)
            }
        }
        let selector1 = #selector(UIPanGestureRecognizer.touchesBegan(_:with:))
        let selector2 = #selector(UIPanGestureRecognizer.swizzling_touchesBegan(_:with:))
        replace(selector1, with: selector2, for: self)
        let selector3 = #selector(UIPanGestureRecognizer.touchesMoved(_:with:))
        let selector4 = #selector(UIPanGestureRecognizer.swizzling_touchesMoved(_:with:))
        replace(selector3, with: selector4, for: self)
    }

    @objc private func swizzling_touchesBegan(_ touches: Set<UITouch>, with event: UIEvent) {
        self.swizzling_touchesBegan(touches, with: event)
        guard direction != nil else { return }
        touchesBegan = true
    }

    @objc private func swizzling_touchesMoved(_ touches: Set<UITouch>, with event: UIEvent) {
        self.swizzling_touchesMoved(touches, with: event)
        guard let direction = direction, touchesBegan == true else { return }
        defer {
            touchesBegan = false
        }
        let forbiddenDirectionsCount = touches
            .flatMap({ ($0.location(in: $0.view) - $0.previousLocation(in: $0.view)).direction })
            .filter({ $0 != direction })
            .count
        if forbiddenDirectionsCount > 0 {
            state = .failed
        }
    }
}

public extension UIPanGestureRecognizer {

    public enum Direction: Int {

        case horizontal = 0
        case vertical
    }

    private struct UIPanGestureRecognizerRuntimeKeys {
        static var directions = "\(#file)+\(#line)"
        static var touchesBegan = "\(#file)+\(#line)"
    }

    public var direction: UIPanGestureRecognizer.Direction? {
        get {
            let object = objc_getAssociatedObject(self, &UIPanGestureRecognizerRuntimeKeys.directions)
            return object as? UIPanGestureRecognizer.Direction
        }
        set {
            let policy = objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC
            objc_setAssociatedObject(self, &UIPanGestureRecognizerRuntimeKeys.directions, newValue, policy)
        }
    }

    fileprivate var touchesBegan: Bool {
        get {
            let object = objc_getAssociatedObject(self, &UIPanGestureRecognizerRuntimeKeys.touchesBegan)
            return (object as? Bool) ?? false
        }
        set {
            let policy = objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC
            objc_setAssociatedObject(self, &UIPanGestureRecognizerRuntimeKeys.touchesBegan, newValue, policy)
        }
    }
}

fileprivate extension CGPoint {

    var direction: UIPanGestureRecognizer.Direction? {
        guard self != .zero else { return nil }
        switch fabs(x) > fabs(y) {
        case true:  return .horizontal
        case false: return .vertical
        }
    }

    static func -(lhs: CGPoint, rhs: CGPoint) -> CGPoint {
        return CGPoint(x: lhs.x - rhs.x, y: lhs.y - rhs.y)
    }
}

0

Bunu denedim: soruya göre benim için çalışan

func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
    if gestureRecognizer is UIPanGestureRecognizer {
        return true
    } else {
        return false
    }
}

0

SWIFT 4.2

Daha ileri gittim ve bir yön belirttim Pan Gesture:

enum PanDirection {
    case up
    case left
    case right
    case down
}

class PanDirectionGestureRecognizer: UIPanGestureRecognizer {
    
    fileprivate 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)
        
        guard state != .failed else { return }

        let vel = velocity(in: view)

        let velocities: [PanDirection: CGFloat]
            = [.up: -vel.y,
               .left: -vel.x,
               .right: vel.x,
               .down: vel.y]

        let sortedKeys = velocities.sorted { $0.1 < $1.1 }

        if let key = sortedKeys.last?.key,
            key != direction {
            state = .cancelled
        }
    }
}

(Kullanılmış: https://github.com/fastred/SloppySwiper ve https://stackoverflow.com/a/30607392/5790492 )


0

İşte Swift 5'te özel bir kaydırma hareketi

U yönünü ve yöndeki maksimum açıyı kısıtlayabilir, ayrıca yönündeki minimum hızını da kısıtlayabilirsiniz.

enum PanDirection {
    case vertical
    case horizontal
}

struct Constaint {
    let maxAngle: Double
    let minSpeed: CGFloat

    static let `default` = Constaint(maxAngle: 50, minSpeed: 50)
}


class PanDirectionGestureRecognizer: UIPanGestureRecognizer {

    let direction: PanDirection

    let constraint: Constaint


    init(direction orientation: PanDirection, target: AnyObject, action: Selector, constraint limits: Constaint = Constaint.default) {
        direction = orientation
        constraint = limits
        super.init(target: target, action: action)
    }

    override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent) {
        super.touchesMoved(touches, with: event)
        let tangent = tan(constraint.maxAngle * Double.pi / 180)
        if state == .began {
            let vel = velocity(in: view)
            switch direction {
            case .horizontal where abs(vel.y)/abs(vel.x) > CGFloat(tangent) || abs(vel.x) < constraint.minSpeed:
                state = .cancelled
            case .vertical where abs(vel.x)/abs(vel.y) > CGFloat(tangent) || abs(vel.y) < constraint.minSpeed:
                state = .cancelled
            default:
                break
            }
        }
    }
}

şöyle çağır:

    let pan = PanDirectionGestureRecognizer(direction: .vertical, target: self, action: #selector(self.push(_:)))
    view.addGestureRecognizer(pan)

    @objc func push(_ gesture: UIPanGestureRecognizer){
        if gesture.state == .began{
            // command for once
        }
    }

veya

    let pan = PanDirectionGestureRecognizer(direction: .horizontal, target: self, action: #selector(self.push(_:)), constraint: Constaint(maxAngle: 5, minSpeed: 80))
    view.addGestureRecognizer(pan)

-1

PanGestureRecognizer arayüzü aşağıdaki tanımları içerir:

unsigned int    _canPanHorizontally:1;
unsigned int    _canPanVertically:1;

Bunu kontrol etmedim, ama belki de alt sınıftan erişilebilir.


3
umut verici görünüyor, ancak bu API gösterilmiyor. Özel API'lerin kullanımı genellikle Apple tarafından reddedilir.
William Denniss
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.