长按后滑动

3

好的,我正在帮助将一个Android游戏转换成iOS。这个游戏基于2048,但使用字母而不是数字。我已经做了很多工作,但仍在学习Objective C/iOS的技巧。到目前为止,我已经让瓷砖/网格工作,移动也可以了,但我需要一点帮助。目标是允许用户长按一个瓷砖来选择它,然后滑动手指到相邻的瓷砖开始拼写单词。我已经实现了长按部分,但我不知道如何让它长按并滑动。除此之外,我已经有了一个滑动,允许用户移动瓷砖。在这里搜索时,我看到了一些关于子类化的建议,所以我想我需要子类化UISwipeGestureRecognizer方法。我已经放置了同时手势识别器,但不确定从这里去哪里。

因此,这里有几个问题。

  1. 最好的方法是什么?实现每个UISwipeGestureRecognizer的子类?

  2. 我的当前滑动检测会干扰吗?(现在只要滑动就会沿着滑动方向移动瓷砖)

  3. 我猜我需要做一个(如果长按)然后激活子类化的滑动方法?

  4. 回答上述问题的任何示例都将非常有帮助。我不是让你替我完成,但至少指点一下大致方向。谢谢!

以下是代码。

//  Grid.m
#import "Grid.h"
#import "Tile.h"

- (void)didLoadFromCCB {
    // listen for swipes to the left
    UISwipeGestureRecognizer * swipeLeft= [[UISwipeGestureRecognizer alloc]initWithTarget:self action:@selector(swipeLeft)];
    swipeLeft.direction = UISwipeGestureRecognizerDirectionLeft;
    [[[CCDirector sharedDirector] view] addGestureRecognizer:swipeLeft];

    // listen for swipes to the right
    UISwipeGestureRecognizer * swipeRight= [[UISwipeGestureRecognizer alloc]initWithTarget:self action:@selector(swipeRight)];
    swipeRight.direction = UISwipeGestureRecognizerDirectionRight;
    [[[CCDirector sharedDirector] view] addGestureRecognizer:swipeRight];

    // listen for swipes up
    UISwipeGestureRecognizer * swipeUp= [[UISwipeGestureRecognizer alloc]initWithTarget:self action:@selector(swipeUp)];
    swipeUp.direction = UISwipeGestureRecognizerDirectionUp;
    [[[CCDirector sharedDirector] view] addGestureRecognizer:swipeUp];

    // listen for swipes down
    UISwipeGestureRecognizer * swipeDown= [[UISwipeGestureRecognizer alloc]initWithTarget:self action:@selector(swipeDown)];
    swipeDown.direction = UISwipeGestureRecognizerDirectionDown;
    [[[CCDirector sharedDirector] view] addGestureRecognizer:swipeDown];

    // listen for long press
    UILongPressGestureRecognizer *longpress = [[UILongPressGestureRecognizer alloc]initWithTarget:self action:@selector(onLongPress:)];
    [longpress setMinimumPressDuration:0.5];
    [[[CCDirector sharedDirector] view] addGestureRecognizer:longpress];
}

- (void)swipeLeft {
    [self move:ccp(-1, 0)];
}
- (void)swipeRight {
    [self move:ccp(1, 0)];
}
- (void)swipeDown {
    [self move:ccp(0, -1)];
}
- (void)swipeUp {
    [self move:ccp(0, 1)];
}

// detect longpress, convert to NodeSpace and check if touch location is within tile boundingbox. If yes, set background white, text black.
- (void)onLongPress:(UILongPressGestureRecognizer *) recognizer {
    CGPoint touchPoint = [[CCDirector sharedDirector] convertToGL:[recognizer locationInView:[recognizer view]]];
    touchPoint = [self convertToNodeSpace:touchPoint];

    if (recognizer.state == UIGestureRecognizerStateBegan) {
        for (Tile *tile in self.children) {
            if([tile isKindOfClass:[Tile class]]) {

                CGRect tileBoundingBox = tile.boundingBox;
                if (CGRectContainsPoint(tileBoundingBox, touchPoint)) {

                    tile.backgroundNode.color = [CCColor whiteColor];
                    tile.valueLabel.color = [CCColor blackColor];
                    [self spellWord:tile.value];
                    [_word setString:[_word lowercaseString]];
                    CCLOG(@"%@", _word);
                }
            }
        }
    }

    if (recognizer.state == UIGestureRecognizerStateChanged) {

    }

    if (recognizer.state == UIGestureRecognizerStateEnded) {
        for (Tile *tile in self.children) {
            if([tile isKindOfClass:[Tile class]]) {

                CGRect tileBoundingBox = tile.boundingBox;
                if (CGRectContainsPoint(tileBoundingBox, touchPoint)) {
                    tile.backgroundNode.color = [tile getColor:tile.value];
                    tile.valueLabel.color = [self getContrastColor:r green:g blue:b];

                }
            }
        }
    }
}

// allow for simultaneous gestures
- (BOOL)gestureRecognizer:(UIGestureRecognizer *) recognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
    return YES;
}
1个回答

0
回答你的问题:
  1. 我认为这不是需要子类化 UILongPressGestureRecognizer 的编码情况。尽管如此,子类化通常是一种很好的方式来清理视图控制器代码,以便您不必在视图控制器类中编写复杂的手势识别器代码。但是(据我所知),这里没有任何要求。通常,只有在需要某些特殊自定义行为时(例如,如果某些复杂标准失败,则手势失败),才会深入研究手势识别器的子类化。在这之前,我建议您先尝试使用标准手势实现所需的 UX。

  2. 我唯一能想到的滑动手势相互干扰的原因是您已经指定了 shouldRecognizeSimultaneouslyWithGestureRecognizer 应该返回 YES。这在需要多个识别器同时运行的情况下使用,但这似乎在这里并不必要(而且只会带来问题)。

    我不确定您是否真的想要一个单独的滑动手势,还是只想要一个手势(“长按并拖动”)。如果您需要那个单独的滑动手势,通常会通过指定 requireGestureRecognizerToFail 来指定手势识别器的相对优先级(例如,要求滑动在长按失败后才能被识别)。但是,如果您只有一个手势(“长按并拖动”),那么只需要一个手势识别器。

  3. 这似乎是不必要的。如果您想在识别长按后检测移动,可以将“长按后移动”代码放在 onLongPressUIGestureRecognizedStateChangedif 语句中,在用户抬起手指之前发生。 UILongPressGestureRecognizer 是一个连续的手势识别器,它将在手势最初被识别后随着用户手指移动而持续更新。


我知道你没有要求代码,但如果你想要一个滑动手势,以及一个长按手势,这实际上是将其拾取并拖动的想法,你可以像下面这样做。请注意,我使滑动手势需要长按失败,因此如果用户正在长按,则优先考虑它,否则它会进行滑动。但你可能根本不需要滑动手势,所以如果不需要,可以完全将其删除:

#import <UIKit/UIGestureRecognizerSubclass.h>

- (void)viewDidLoad {
    [super viewDidLoad];

    UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(handleLongPress:)];
    [self.view addGestureRecognizer:longPress];

    // if you needed a second gesture, a swipe, completely distinct from the long press and drag
    // gesture, you could add it like so:
    //
    // UISwipeGestureRecognizer *swipe = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(handleSwipe:)];
    // [swipe requireGestureRecognizerToFail:longPress];
    // // do additional swipe configuration
    // [self.view addGestureRecognizer:swipe];
}

- (void)handleSwipe:(UISwipeGestureRecognizer *)gesture
{
    // do your separate swipe stuff here
}

- (void)handleLongPress:(UILongPressGestureRecognizer *)gesture
{
    static UIView *tileToMove;
    static CGPoint startCenter;
    static CGPoint startLocation;

    CGPoint location = [gesture locationInView:self.view];

    switch (gesture.state) {
        case UIGestureRecognizerStateBegan:
        {
            // find the tile

            tileToMove = [self findTileToMove:location];
            if (tileToMove) {
                // if found, capture state ...

                startCenter = tileToMove.center;
                startLocation = location;

                // ... and animate "pick up tile", so the user gets positive feedback
                // that the drag/swipe portion of the gesture is starting.

                [UIView animateWithDuration:0.25 animations:^{
                    tileToMove.transform = CGAffineTransformMakeScale(1.2, 1.2);
                }];
            } else {
                gesture.state = UIGestureRecognizerStateFailed;
            }
            break;
        }
        case UIGestureRecognizerStateChanged:
        {
            // move the tile as the user's finger moves

            CGPoint translate = CGPointMake(location.x - startLocation.x, location.y - startLocation.y);

            // note, if you want to constrain the translation to be, for example, on the
            // x-axis alone, you could do something like:
            //
            // CGPoint translate = CGPointMake(location.x - startLocation.x, 0);

            tileToMove.center = CGPointMake(startCenter.x + translate.x, startCenter.y + translate.y);
            break;
        }
        case UIGestureRecognizerStateEnded:
        {
            // animate "drop the tile"

            [UIView animateWithDuration:0.25 animations:^{
                tileToMove.transform = CGAffineTransformIdentity;

                // if you want the tile to "snap" to some location having let it go,
                // set the `center` or `frame` here.
            }];

            // clear our variables, just in case

            tileToMove = nil;
            startCenter = CGPointZero;
            startLocation = CGPointZero;
            break;
        }
        default:
            break;
    }
}

- (UIView *)findTileToMove:(CGPoint)location
{
    for (UIView *tile in self.tiles) {
        if (CGRectContainsPoint(tile.frame, location)) {
            return tile;
        }
    }

    return nil;
}

这可能不是您要寻找的确切UI,但它说明了:

  1. 如何拥有两个手势,其中一个需要另一个失败才能建立手势之间的优先级(仅在您需要两个不同手势时才是问题,您可能不需要);

  2. 没有shouldRecognizeSimultaneouslyWithGestureRecognizer方法,因为我不希望它们同时被识别。请注意,只有在您真正需要两个手势时才需要它,您可能需要也可能不需要;和

  3. 如何拥有一个长按,不仅识别初始长按,还识别后续的滑动/拖动移动。


好的,这有些意义,但我该如何设置呢?我怎样才能让它区分普通滑动和长按后的滑动呢? - Dan
@Dan - 我在想你是否真的需要两个手势。你是真的有两个手势(标准滑动和长按后拖动),还是只有一个(那个长按和拖动手势)?如果你只需要后者,那么显然解决方案就简化了(根本不需要滑动手势)。我已经相应地修改了我的答案。 - Rob
我实际上需要这两种手势。单独的滑动手势用于移动方块。所有的“方块”都在一个由数组组成的网格中。没有长按的情况下,滑动手势可以移动方块,因此这是绝对必要的。长按+拖动手势用于选择您长按的方块(我已经做到了),然后当您将手指拖动到相邻的方块时,它会选择您拖动手指经过的每个方块并将字母加在一起拼出一个单词,该单词将与预加载的字典进行比较。长按不涉及移动方块。 - Dan
@Dan,没问题。所以,你有两个手势,没有shouldRecognizeSimultaneouslyWithGestureRecognizer,只需像你概述的那样调整长按处理程序即可。但是,你应该像我上面概述的那样执行这两个手势(显然取消注释滑动手势代码)。你应该可以顺利完成! - Rob

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接