【问题标题】:UIButton can't be touched while animated with UIView animateWithDuration使用 UIView animateWithDuration 进行动画处理时无法触摸 UIButton
【发布时间】:2012-01-10 21:05:18
【问题描述】:

我有以下代码:

[UIView animateWithDuration:0.3
                      delay:0.0
                    options:UIViewAnimationCurveEaseOut | UIViewAnimationOptionAllowUserInteraction
                 animations:^{
                     CGRect r = [btn frame];
                     r.origin.y -= 40;
                     [btn setFrame: r];
                 }
                 completion:^(BOOL done){
                     if(done){
                         [UIView animateWithDuration:0.3
                                               delay:1
                                             options:UIViewAnimationOptionCurveEaseIn | UIViewAnimationOptionAllowUserInteraction
                                          animations:^{
                                              CGRect r = [btn frame];
                                              r.origin.y += 40;
                                              [btn setFrame: r];
                                          }
                                          completion:^(BOOL done){if(done) zombiePopping = 0; }];
                     }

                 }];

问题是,即使我使用UIViewAnimationOptionAllowInteraction,按钮在动画时似乎也没有响应触摸,这对我来说有点奇怪。

也许这主要是通过 Core Animation 来完成的?如果是这样,我会怎么做?

【问题讨论】:

    标签: iphone objective-c ios core-animation uiviewanimation


    【解决方案1】:

    斯威夫特 5

    就我而言,当我设置 button.alpha = 0 时,按钮交互停止工作,无论我是否设置 UIViewAnimationOptionAllowUserInteraction 作为选项。

    原因

    无论您是否定义动画,视图的属性都会立即应用于视图的图层。因此,当您设置view.alpha=0 时,您将完全隐藏view

    解决方案

    简单,只需减少alpha=0.1(甚至是0.05

    UIView.animate(withDuration: 2,
                           delay: 0,
                           options: [.allowUserInteraction, .overrideInheritedOptions, .curveEaseOut, .repeat, .autoreverse],
                           animations: {
                               self.button.layer.opacity = 0.01 // 0.0 will make the button unavailable to touch
            })
    

    【讨论】:

    • 这个小技巧需要 100 个赞。 1 秒变化,一切正常。太棒了
    • 绝对是最佳答案。
    • 最佳答案。
    • Swift 代码 UIView.animate(withDuration: 2.0, delay: 0, options:[UIViewAnimationOptions.repeat, UIViewAnimationOptions.allowUserInteraction], 动画: { button.transform = CGAffineTransform(scaleX: 1.5, y: 1.5 ) }, 完成:nil);
    • 也值得研究,虽然我没有测试过,如果你确实需要完全淡出,你可以将 view.layer.opacity 设置为 0.0 并且仍然保持可用触摸出于某种原因。
    【解决方案2】:

    我在屏幕上有 4 个按钮,它们应该用作向左、向右、向下和向上的导航。我使用了来自@nahung89 答案的简单技巧,并添加了完成以获得我需要的全部效果。 代码如下:

    1. 将所有按钮连接到 ViewController 中的 Outlet 集合

      @IBOutlet var navigationButtons: [UIButton]!
      
    2. 在 viewDidAppear 中添加了这个功能:

          navigationButtons.forEach { button in
              UIView.animate(withDuration: 0.5,
                                      delay: 5,
                                      options: [.curveLinear, .allowUserInteraction],
                                      animations: {
                                          button.alpha = 0.1
              }) { _ in button.alpha = 0 }
          }
      

    【讨论】:

      【解决方案3】:

      如果你有 UIButton 的子类,最简单的方法是重写 hitTest 之类的

      public override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
              return self.layer.presentation()?.hitTest(self.convert(point, to: superview)).flatMap { _ in return self } ?? nil
          }
      

      【讨论】:

        【解决方案4】:

        选项的顺序很重要,你必须先放置UIViewAnimationOptionAllowUserInteraction,然后添加其他选项。

        在 Swift 3 中使用:.allowUserInteraction

        【讨论】:

        • 你能详细说明为什么顺序应该很重要吗?这是一个 OptionSet 类型,它暗示了一个位掩码,您提供每个标志的顺序根本不应该出现。我也没有看到投票的理由,因为没有提供证实您的主张的来源。
        • @DevAndArtist 你可以自己尝试一下,然后你就会知道我为什么说顺序很重要。让我知道您是否有不同的结果,或者很好,没有人为回答问题付费。
        【解决方案5】:

        Swift 3.0 在 viewdidload 的顶部创建一个超级视图

        let superView = UIView(frame: view.frame)
            superView.isUserInteractionEnabled = true
            superView.backgroundColor = UIColor.clear
            superView.alpha = 0.1
            view.addSubview(superView)
        

        现在像这样实现 touchesbagan

        override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        
            guard let touch = (touches as NSSet).anyObject() as? UITouch else {
                return
            }
        
            let touchLocation = touch.location(in: self.view)
            if btnone.layer.presentation()?.hitTest(touchLocation) != nil {
                print("1") // Do stuff for button 
            }
            if btntwo.layer.presentation()?.hitTest(touchLocation) != nil {
                print("2") // Do stuff
            }
            if btnthree.layer.presentation()?.hitTest(touchLocation) != nil {
                print(3) // Do stuff
            }
            if btnfour.layer.presentation()?.hitTest(touchLocation) != nil {
                print(4) // Do stuff
            }
        }
        

        【讨论】:

          【解决方案6】:

          像魅力一样工作

          override func hitTest(point: CGPoint, withEvent event: UIEvent?) -> UIView? { if bounds.contains(point) && !hidden && userInteractionEnabled && enabled { return self } return super.hitTest(point, withEvent: event) }

          虽然this的回答也是正确的

          【讨论】:

            【解决方案7】:

            Swift 版本: '999' 是用于标识目标视图的标记值。

            override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
                    let touch = touches.first
                    let touchLocation = (touch?.locationInView(self.rootView))!
            
                    for view in self.view.subviews {
                        if let layer = view.layer.presentationLayer()?.hitTest(touchLocation) where view.tag == 999 {
                            // Here view is the tapped view
                            print(view)
                        }
                    }
                }
            

            【讨论】:

              【解决方案8】:

              对于懒惰的人来说,Swift (2.0) 中的答案相同。如有必要,替换为循环和出口集合:

              override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
                  let touch = touches.first
                  let touchLocation = touch!.locationInView(self.view)
                  if self.button.layer.presentationLayer()!.hitTest(touchLocation) != nil {
                      action() // Your action, preferably the original button action.
                  }
              }
              

              【讨论】:

                【解决方案9】:

                我使用 NSTimer 实现了这一点: 首先,执行启动动画,然后你应该启动一个计时器,这是演示代码:

                -(void)fun···
                {
                    [UIView animateWithDuration:0.3
                                      delay:0.0
                                    options:UIViewAnimationCurveEaseOut |  UIViewAnimationOptionAllowUserInteraction
                                 animations:^{
                                     CGRect r = [btn frame];
                                     r.origin.y -= 40;
                                     [btn setFrame: r];
                                 }
                                 completion:^(BOOL done){}];
                
                    then , you should start an timer,example:
                    //start timer
                   if (timer != nil)
                   {
                       [timer invalidate];
                       timer = nil;
                   }
                   [self performSelector:@selector(closeButton) withObject:nil afterDelay:0];
                }
                
                
                - (void)closeButton
                {
                    if (timer != nil)
                    {
                        return;
                    }
                
                    //start timer
                    timer = [NSTimer scheduledTimerWithTimeInterval:1.5f target:self selector:@selector(timerEvent) userInfo:nil repeats:NO];
                    [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
                }
                
                - (void)timerEvent
                {
                    [UIView animateWithDuration:0.3
                                                               delay:1
                                                             options:UIViewAnimationOptionCurveEaseIn | UIViewAnimationOptionAllowUserInteraction
                                                          animations:^{
                                                              CGRect r = [btn frame];
                                                              r.origin.y += 40;
                                                              [btn setFrame: r];
                                                          }
                                                          completion:^(BOOL done){if(done) zombiePopping = 0; }];
                    [timer invalidate];
                    timer = nil;
                }
                

                【讨论】:

                  【解决方案10】:

                  按钮的可触摸部分在动画时不会与按钮的可见框架重合。

                  在内部,按钮的框架将设置为动画的最终值。你应该会发现你可以点击这个区域,它会起作用。

                  要点击移动按钮,您需要对按钮的.layer.presentationLayer 属性进行点击测试(需要导入 QuartzCore 框架来执行此操作)。这通常在视图控制器中的触摸处理方法中完成。

                  如果您需要更多信息,我很乐意扩展此答案。

                  以下是通过点击测试表示层来响应触摸事件的方法。此代码位于管理按钮的视图控制器子类中。当我这样做时,我对初始触摸感兴趣,而不是触摸结束(通常是在点击时注册),但原理是一样的。

                  记得导入 QuartzCore 框架并将其添加到您的项目中。

                  - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
                  {
                      UITouch *touch = [touches anyObject];
                      CGPoint touchLocation = [touch locationInView:self.view];
                      for (UIButton *button in self.buttonsOutletCollection)
                      {
                          if ([button.layer.presentationLayer hitTest:touchLocation])
                          {
                              // This button was hit whilst moving - do something with it here
                              break;
                          }
                      }
                  }
                  

                  【讨论】:

                  • 您的另一个选择是在重复计时器上以非常小的增量移动按钮,就像在游戏更新循环中一样。如果它们在一个数组中,那么对 8 个按钮的命中测试非常简单。
                  • 你能给我一些代码示例吗?我不确定我的选择是什么,再次感谢:)
                  • 按钮在一个数组中(IBOutletCollection)
                  • 我已经更新了答案。我没有计时器选项的示例,我只是这样做了。
                  • @Stebra 你不要调用这个方法。 UIKit 在你的视图控制器上为你调用它。
                  猜你喜欢
                  • 2014-12-18
                  • 1970-01-01
                  • 2016-02-12
                  • 1970-01-01
                  • 2011-02-18
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  相关资源
                  最近更新 更多