如何在按住手指的同时从使用一个 UIButton 切换到另一个?

Posted

技术标签:

【中文标题】如何在按住手指的同时从使用一个 UIButton 切换到另一个?【英文标题】:How can you switch from using one UIButton to another while still holding down finger? 【发布时间】:2012-08-12 01:38:05 【问题描述】:

想象一下你的键盘。想象一下自己将一根手指放在一个键上,然后(在按住的同时)将手指一直移动到键盘上的另一个键上。现在,假设键盘上的每个键都是UIButton。当您将手指放在当前键上时,此键 (UIButton) 会突出显示。然后,当用户移动到另一个键时,第一个键不再突出显示,而当前按下的键被突出显示。

现在,我有一个 6 x 8 的 UIButton 网格,每个 53 x 53 像素。所以,我有 48 个UIButtons。我想复制这种想法。用户手指按下的按钮会有一个稍微亮一点的图像(看起来像被选中的那样),而所有其他按钮都不会稍微亮一点。

这是我的想法,如何去做,

1) 在viewDidLoad 中创建所有48 个UIButtons。为所有UIButtons 添加较亮的图像到UIControlStateHighlighted

2) 为touchUpOutside 添加某种target,以某种方式使当前按钮不突出显示且不可用。 (也许将突出显示的和userInteractionEnabled 设置为否)。但是,我怎样才能知道要使用的下一个按钮呢?我怎么能说我希望用户手指所在的特定UIButton 突出显示并用于检测手势和内容。

另外,这个touchUpOutside 方法可能不起作用,因为所有按钮都紧挨着,我认为你必须拖得很远才能运行这个touchUpOutside

还需要注意的是,按钮的行和列不相等。有时一个按钮实际上是UIImage,但看起来像UIButton。因此,出于某种原因进行某种快速枚举比较帧是行不通的。

【问题讨论】:

您描述了一些低级行为(如果您按下并将手指拖动到另一个按钮的事实),但您没有描述您要解决的更广泛的问题,所以它使回答你的问题有点难。你想做什么?只是检测到一个按钮的触碰并在另一个按钮上触碰?还是类似 Swype 的打字方案中的整个系列?建议的答案可能取决于您要做什么。 我也不明白你关于不快速枚举帧不起作用的最后一点(因为如果你担心不同的子视图类型,你只需检查 isKindOfClass 以了解正确的行为是)。看我的回答。 我假设你的意思是 UIImageView 而不是 UIImage 至于快速枚举,你总是可以像 Ryan 说的那样使用isKindOfClass,但如果它们都是 UIView 的子类,你甚至不需要它,因为它们都应该有 myUIView.frame 属性.看我的回答。 【参考方案1】:

两个观察结果:

    我通常将手势识别器用于此类事情,而不是各种 touchUp... 方法。

    我知道您说过您不想快速枚举子视图,但为什么不呢?仅仅因为某些类与其他类不同不是问题(因为您可以测试isKindOfClass 方法)。 (顺便说一句,我不知道为什么有些会是按钮,有些不会。)仅仅因为它们没有对齐也没有什么区别。

无论如何,对于这样的事情,我经常会将手势识别器放在共享的父视图(例如通常是视图控制器的视图)上,然后枚举子视图以查看当前 CGPoint 包含在哪个视图中?

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

for (UIView *subview in self.view.subviews)

    if (CGRectContainsPoint(subview.frame, location))
    
        // do your appropriate highlighting

        if ([subview isKindOfClass:[UIButton class]])
        
            // something for buttons
        
        else if ([subview isKindOfClass:[UIImageView class]])
        
            // something for imageviews
        

        return;
    

我不知道这对你是否有意义,但对我来说似乎最简单。如果你澄清你的意图,也许我可以进一步完善我的答案。

作为一个实际示例,您可以定义一个连续手势识别器来跟踪单击的第一个和最后一个子视图(如果您不在子视图上开始手势,则不会生成手势,如果您不会在子视图上停止手势,它会取消整个操作),例如:

@interface SubviewGestureRecognizer : UIGestureRecognizer

@property (nonatomic,strong) UIView *firstSubview;
@property (nonatomic,strong) UIView *currentSubview;

@end

@implementation SubviewGestureRecognizer

- (id) initWithTarget:(id)target action:(SEL)action

    self = [super initWithTarget:target action:action];
    if (self)
    
        self.firstSubview = nil;
        self.currentSubview = nil;
    

    return self;


// you might want to tweak this `identifySubview` to only look 
// for buttons and imageviews, or items with nonzero tag properties, 
// or recursively navigate if it encounters container UIViews 
// or whatever suits your app

- (UIView *)identifySubview:(NSSet *)touches

    CGPoint location = [[touches anyObject] locationInView:self.view];

    for (UIView *subview in self.view.subviews)
    
        if (CGRectContainsPoint(subview.frame, location))
        
            return subview;
        
    

    return nil;


- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event

    [super touchesBegan:touches withEvent:event];

    if ([touches count] != 1)
    
        self.state = UIGestureRecognizerStateFailed;
        return;
    

    self.firstSubview = [self identifySubview:touches];
    self.currentSubview = self.firstSubview;

    if (self.firstSubview == nil)
        self.state = UIGestureRecognizerStateFailed;
    else
        self.state = UIGestureRecognizerStateBegan;


- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event

    [super touchesMoved:touches withEvent:event];

    if (self.state == UIGestureRecognizerStateFailed) return;

    self.currentSubview = [self identifySubview:touches];

    self.state = UIGestureRecognizerStateChanged;


- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event

    [super touchesEnded:touches withEvent:event];

    self.currentSubview = [self identifySubview:touches];

    if (self.currentSubview != nil)
        self.state = UIGestureRecognizerStateEnded;
    else
        self.state = UIGestureRecognizerStateFailed;


- (void)reset

    [super reset];

    self.firstSubview = nil;
    self.currentSubview = nil;

然后您可以在viewDidLoad 中进行设置:

SubviewGestureRecognizer *recognizer = [[SubviewGestureRecognizer alloc] initWithTarget:self action:@selector(handleTouches:)];
[self.view addGestureRecognizer:recognizer];

然后这样定义您的处理程序(这适用于按钮和图像视图,利用两者都支持setHighlighted这一事实):

- (void)handleTouches:(SubviewGestureRecognizer *)sender

    static UIControl *previousControl = nil;

    if (sender.state == UIGestureRecognizerStateBegan || sender.state == UIGestureRecognizerStateChanged)
    
        if (sender.state == UIGestureRecognizerStateBegan)
            previousControl = nil;

        UIView *subview = sender.currentSubview;
        if (previousControl != subview)
        
            // reset the old one (if any)

            [previousControl setHighlighted:NO];

            // highlight the new one

            previousControl = (UIControl *)subview;
            [previousControl setHighlighted:YES];
        
    
    else if (sender.state == UIGestureRecognizerStateEnded)
    
        if (previousControl)
        
            [previousControl setHighlighted:NO];
            NSLog(@"successfully touchdown on %@ and touchup on %@", sender.firstSubview, sender.currentSubview);
        
    
    else if (sender.state == UIGestureRecognizerStateCancelled || sender.state == UIGestureRecognizerStateFailed)
    
        [previousControl setHighlighted:NO];
        NSLog(@"cancelled/failed gesture");
    

【讨论】:

好吧,我有点犹豫要不要这样做。因为当用户移动他的手指时,我希望它是即时的。而且,如果我每次做一点点拖动都运行这个方法,我认为可能会出现问题。 我认为您会发现它是即时的,但如果您愿意,显然可以采用其他方法。但在最基本的层面上,我认为所有这些技术,在幕后,可能使用相同的UIResponder 方法,touchesBegantouchesMovedtouchedEnded 等,所以我不确定是否有很大的不同。顺便说一句,我已经用一个自定义手势识别器更新了我的答案,它隐藏了很多“我结束了哪个子视图”逻辑,所以它在你的触摸处理程序例程中更清晰一些。但是,如果您发现另一种更符合您喜好的技术,这里当然不会受到冒犯。 @Sasquatch 除非你有成千上万个按钮,否则我不会担心。过早的优化是根本... 请确保您已导入 UIGestureRecognizerSubclass 类【参考方案2】:

这是一种相当简单的方法。假设您正在以编程方式创建按钮(使用界面生成器执行此操作将是一个巨大的痛苦)。此方法适用于 UIViewController、UIView 和 UIGestureRecognizer 的子类。

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event 

    //get the touch object
    UITouch *myTouch = [touches anyObject];

    //get the location of the touch
    CGPoint *myPoint = [myTouch locationInView:self.view];

    //detect if the touch is in one of your buttons
    if ( CGRectContainsPoint(myButton.frame, myPoint)
        /*do whatever needs to be done when the button is pressed
        repeat the CGRectContainsPoint for all buttons,
        perhaps using a while or for loop to look through an array?*/
    


此方法仅检测用户何时放下手指,您将需要使用这些方法来检测其他动作:

touchesMoved:withEvent 检测手指何时在屏幕上移动而不抬起

touchesEnded:withEvent 检测手指何时离开屏幕

touchesCancelled:withEvent: 检测到应用程序何时中断,例如接听电话或锁定屏幕

【讨论】:

以上是关于如何在按住手指的同时从使用一个 UIButton 切换到另一个?的主要内容,如果未能解决你的问题,请参考以下文章

UIButton 状态

如何在手指从1按钮拖动到下一个按钮时触发UIButton事件

在 iphone 中按住 uibutton 时增加一个值

捕获组合的按钮按下和平移手势

Swift 中的可拖动 UIButton/元素?

oppo怎么截屏