UIPanGestureRecognizer - เฉพาะแนวตั้งหรือแนวนอน


146

ฉันมีมุมมองที่UIPanGestureRecognizerต้องลากมุมมองในแนวตั้ง ดังนั้นในการเรียกตัวจำแนกลายมือฉันจึงอัปเดตพิกัด y เพื่อย้ายเท่านั้น ผู้ควบคุมของมุมมองนี้มีส่วนUIPanGestureRecognizerที่จะลากมุมมองในแนวนอนเพียงอัปเดตพิกัด x

ปัญหาคือตอนแรกUIPanGestureRecognizerคือการจัดกิจกรรมเพื่อย้ายมุมมองในแนวตั้งดังนั้นฉันจึงไม่สามารถใช้ท่าทางผู้ควบคุม

ฉันเหนื่อย

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

และทั้งสองจะทำงานได้ แต่ฉันไม่ต้องการที่ ฉันต้องการให้ตรวจจับในแนวนอนเฉพาะเมื่อการเคลื่อนไหวเป็นแนวนอนอย่างชัดเจน ดังนั้นมันจะดีถ้าUIPanGestureRecognizerมีคุณสมบัติทิศทาง

ฉันจะบรรลุพฤติกรรมนี้ได้อย่างไร ฉันพบว่าเอกสารเกิดความสับสนอย่างมากดังนั้นอาจมีบางคนอธิบายได้ดีกว่าที่นี่


มันก็โอเคที่จะตอบคำถามของคุณเองและยอมรับคำตอบถ้าคุณคิดวิธีแก้ปัญหา
jtbandes

@ JoeBlow จริงเหรอ? ดังนั้นบางทีคุณอาจทำหมวดหมู่ของท่าทางการปัดเพื่อรับการแปลและความเร็วของท่าทาง?
Roman Truba

2
ฉันไม่เข้าใจสิ่งที่คุณพูด หากคุณต้องการที่จะตรวจสอบรูดแนวนอนนี้มีทั้งหมดและสมบูรณ์ในตัวระบบปฏิบัติการ งานทั้งหมดนั้นทำเพื่อคุณโดยสิ้นเชิง คุณไม่ต้องทำอะไรเลย! :) เพียงวางโค้ดสองบรรทัดในตัวอย่างนี้ .. stackoverflow.com/a/20988648/294884 โปรดทราบว่าคุณสามารถเลือกซ้ายเท่านั้น "" ถูกต้องเท่านั้น "หรือ" ทั้งคู่ "
Fattie

คำตอบ:


212

เพียงทำสิ่งนี้สำหรับตัวจดจำท่าทางเลื่อนแนวตั้งมันใช้งานได้สำหรับฉัน:

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

และสำหรับ Swift:

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

3
ลองสิ่งนี้ แต่การแปลมักจะ == (0,0) ดังนั้นจึงไม่แม่นยำ
zxcat

12
ปัญหา (0,0) ไม่ชัดเจนเมื่อใช้ velocityInView: แทนที่จะใช้ translationInView:
cbh2000

1
@ cbh2000 ฉันปรับปรุงคำตอบที่จะใช้แทนvelocityInView translationInView
Hejazi

19
@JoeBlow A UISwipeGestureRecognizer เป็นวิธีที่ง่ายในการปิดการเปลี่ยนภาพเพื่อตอบสนองต่อท่าทางการปัดนิ้ว แต่มันเป็นท่าทางที่ไม่ต่อเนื่อง หากมีใครบางคนกำลังมองหาวิธีการที่ต่อเนื่อง - เพื่อสร้างการเปลี่ยนแปลงด้วยท่าทาง - UIPanGestureRecognizer เป็นวิธีที่จะไป
Levi McCallum

นี่คือทางออกที่ฉลาด
Jakub Truhlář

79

ฉันสร้างวิธีแก้ปัญหาด้วยคลาสย่อยเช่นในคำตอบ @LocoMike ที่ให้ไว้ แต่ใช้กลไกการตรวจจับที่มีประสิทธิภาพมากขึ้นผ่านความเร็วเริ่มต้นตามที่ @Hejazi มอบให้ ฉันใช้สวิฟท์ด้วยเช่นกัน แต่นี่ควรจะง่ายต่อการแปลเป็น Obj-C หากต้องการ

ข้อดีกว่าโซลูชันอื่น ๆ :

  • เรียบง่ายและรัดกุมกว่าโซลูชัน subclassing อื่น ๆ ไม่มีสถานะเพิ่มเติมในการจัดการ
  • การตรวจจับทิศทางเกิดขึ้นก่อนที่จะส่งการกระทำเริ่มดังนั้นตัวเลือกรูปแบบการเลื่อนของคุณจะไม่ได้รับข้อความใดหากมีการกวาดทิศทางที่ไม่ถูกต้อง
  • หลังจากกำหนดทิศทางเริ่มต้นแล้วจะไม่มีการปรึกษาตรรกะของทิศทางอีกต่อไป ซึ่งจะส่งผลให้พฤติกรรมที่ต้องการโดยทั่วไปของการเปิดใช้งานตัวจำแนกลายมือของคุณหากทิศทางเริ่มต้นถูกต้อง แต่จะไม่ยกเลิกท่าทางหลังจากเริ่มแล้วหากนิ้วของผู้ใช้ไม่ได้เคลื่อนที่ไปตามทิศทางอย่างสมบูรณ์

นี่คือรหัส:

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

4
นี่คือคำตอบที่ดีที่สุด UIPanGestureRecognizerมันเลวร้ายเกินไปว่าแอปเปิ้ลยังไม่ได้เพิ่มฟังก์ชันการทำงานเช่นนี้ไป
NRitH

คุณสามารถให้ตัวอย่างการใช้งานได้หรือไม่?
user82395214

น่ารักจัง! ขอบคุณ! ทำงานได้อย่างสมบูรณ์แบบเมื่อวางซ้อนทั้งแนวนอนและแนวตั้ง: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 Han

โอ้นี่มันสุดยอดมาก! ขอบคุณ!
Baran Emre

51

ฉันคิดออกสร้าง subclass ของ 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

สิ่งนี้จะทริกเกอร์ท่าทางเมื่อผู้ใช้เริ่มลากในลักษณะการทำงานที่เลือก ตั้งค่าคุณสมบัติทิศทางเป็นค่าที่ถูกต้องและคุณพร้อมแล้ว


ฉันคิดว่า 'รีเซ็ต' ไม่ได้รับการเรียกในตอนแรก เพิ่มinitWithTarget:action:วิธีการและเรียกว่าการรีเซ็ตและทั้งหมดเป็นไปด้วยดี
colinta

5
ในการใช้งานในปัจจุบันDirectionPanGestureRecognizerจะไม่สนใจ drags ที่รวดเร็วเว้นแต่คุณจะตั้งค่าkDirectionPanThreshold = 20หรือดังนั้นในกรณีนี้มันสามารถส่งสัญญาณเตือนที่ผิดพลาดได้ ฉันแนะนำให้วางabs(_moveX) > abs(_moveY)แทนabs(_moveX) > kDirectionPanThresholdและเปลี่ยนตัวพิมพ์แนวนอนตามลำดับ
Dennis Krut

2
ฉันควรเพิ่มสิ่งนี้มีประโยชน์สำหรับฉันเช่นกัน แต่สิ่งที่ฉันต้องเพิ่มเพื่อให้ตัวจดจำท่าทางเลื่อนเพื่อเรียกใช้อยู่ในส่วนอื่นของถ้าภายใต้บรรทัดที่_drag = YESฉันเพิ่มself.state = UIGestureRecognizerStateChanged;
bolnad

13

ฉันพยายาม จำกัด พื้นที่ที่ถูกต้องในแนวนอนด้วย 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 degree ในแนวนอนเท่านั้นที่สามารถเรียกใช้ท่าทางแพนนี้


2
คำตอบที่ดี สิ่งนี้ช่วยฉันได้มากเมื่อฉันผสมท่าทาง UIScrollView และท่าทางปกติ ฉันคิดว่าตัวอย่างตั้งใจจะพูดว่า "thresholdAngle" แทนที่จะเป็น "enableThreshold" และคุณไม่ค่อยควรใช้ atan () เพราะมันสามารถผลิต NAN ได้ ใช้ atan2 () แทน
Brainware

9

คำตอบ 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

}

6

ทางออกต่อไปนี้แก้ไขปัญหาของฉัน:

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

นี่เป็นเพียงการตรวจสอบว่าแพนเกิดขึ้นในมุมมองหลักหรือ tableView หรือไม่


3
เหตุใดจึงเรียกใช้ -isEqual: เปรียบเทียบว่าสองมุมมองเหมือนกันหรือไม่ การตรวจสอบเอกลักษณ์อย่างง่ายควรจะเพียงพอ gestureRecognizer.view == self.view
openfrog

6

คำตอบของ Lee ในเวอร์ชั่น Swift 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
            }
        }
    }
}

4

ฉันรับคำตอบของLee Goodrichและขยายออกไปตามที่ฉันต้องการโดยเฉพาะการแพนทิศทางเดียว ใช้มันแบบนี้:let pan = PanDirectionGestureRecognizer(direction: .vertical(.up), target: self, action: #selector(handleCellPan(_:)))

ฉันยังเพิ่มความคิดเห็นบางอย่างเพื่อให้ชัดเจนขึ้นเล็กน้อยว่าการตัดสินใจทำอะไรจริง ๆ

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

ข้อผิดพลาดใน-override func touchesMoved Method does not override any method from its superclass
AnBisw

@Annjawn คุณต้องใช้ "import UIKit.UIGestureRecognizerSubclass"
shawnynicole

ตกลง. ฉันไม่ทราบว่า ฉันคิดว่าการนำเข้า UIKit จะนำเข้าโดยอัตโนมัติ ฉันจะลองดู
AnBisw

2

คุณสามารถค้นหาทิศทางที่ลากผ่านUIView UIPanGestureRecognizerกรุณาปฏิบัติตามรหัส

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

ที่จริงนี่ไม่ใช่ทางออกที่ดีเพราะมีเพียง 88 คะแนนจากซ้ายเท่านั้นที่สามารถเลื่อน
Borut Tomazin

2

สวิฟต์ 4.2

การแก้ปัญหาเป็นเพียงการสนับสนุนท่าทางการเลื่อนในแนวตั้งเช่นเดียวกับแนวนอน

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

โซลูชันที่ 1 :

@objc func panAction(pan: UIPanGestureRecognizer) {

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

โซลูชันที่ 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)
    }

จากนั้นรูปแบบการปัดนิ้วจะกลืนรูปแบบการแพน แน่นอนคุณไม่จำเป็นต้องทำอะไรswipeActionเลย


1

นี่คือวิธีที่ฉันแก้ไข:

ครั้งแรกที่ฉันเปิดใช้งานการจดจำ PanGesture พร้อมกัน

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

return YES;

จากนั้นฉันแยกท่าทางแพนในแนวนอนและแนวตั้ง (แอคคิวมูเลเตอร์เป็นคุณสมบัติของ NSMutableArray):

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

ฉันผลักตัวอย่างที่นี่:

เพิ่มกระทะที่กำหนดเองใน scrollview


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

panGestureRecognizerคุณอาจจะใช้ง่าย ไม่จำเป็นต้องใช้ pandirectionregognizerหรืออะไร เพียงใช้ค่า y ของtranslationInview ลากโค้ดด้านล่างเพื่อดูการลากขึ้นและลงเท่านั้น

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

รหัสนี้เพียงแค่เปิดมุมมอง ไม่มีการล็อคทิศทาง
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];}

ฉันสร้าง PanGestureRecognizer @selector วิธีการกระทำสำหรับวัตถุที่จำเป็นต้องเลื่อนแนวนอนเท่านั้น

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

1

วิธีที่รวดเร็ว

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

สำหรับผู้ใช้ Swift ทุกคนที่นี่สิ่งนี้จะทำงาน :)

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 และส่งไปยัง Swift 3

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

ฉันชอบที่จะแบ่งปันวิธีการของฉันเพราะวิธีการอื่น ๆ ทั้งหมดจะขึ้นอยู่กับอย่างใดอย่างหนึ่งUIGestureRecognizerDelegateหรือ UIPanGestureRecognizersubclassing

วิธีการของฉันขึ้นอยู่กับรันไทม์และการส่าย ฉันไม่แน่ใจ 100% เกี่ยวกับวิธีการนี้ แต่คุณสามารถทดสอบและปรับปรุงด้วยตนเอง

กำหนดทิศทางของการใด ๆUIPanGestureRecognizerด้วยรหัสบรรทัดเดียว:

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

ใช้pod 'UIPanGestureRecognizerDirection'หรือรหัส:

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

ฉันลองสิ่งนี้: ซึ่งใช้งานได้สำหรับฉันตามคำถามที่อธิบาย

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

0

SWIFT 4.2

ฉันไปไกลกว่านั้นและทำทิศทางแพนแพน:

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

(ใช้แล้ว: https://github.com/fastred/SloppySwiperและhttps://stackoverflow.com/a/30607392/5790492 )


0

นี่คือรูปแบบการแพนที่กำหนดเองในSwift 5

คุณสามารถ จำกัด ทิศทางและมุมสูงสุดในทิศทางนั้นคุณสามารถ จำกัด ความเร็วขั้นต่ำในทิศทางนั้น

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

โทรแบบนี้:

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

หรือ

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

-1

PanGestureRecognizer interface มีคำจำกัดความต่อไปนี้:

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

ฉันไม่ได้ตรวจสอบสิ่งนี้ แต่อาจเข้าถึงได้ผ่านคลาสย่อย


3
ดูมีแนวโน้ม แต่ API นั้นจะไม่ถูกเปิดเผย การใช้งาน API ส่วนตัวมักส่งผลให้ Apple ปฏิเสธ
William Denniss
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.