web-dev-qa-db-ja.com

UIView animateWithDurationでアニメーション化されている間は、UIButtonをタッチできません

私は次のコードを持っています:

[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を使用しているにもかかわらず、アニメーション中にボタンがタッチに反応しないことです。これは少し奇妙です。

多分これはコアアニメーションでうまくいくのでしょうか?もしそうなら、どうすればいいですか?

44
Shai Mishali

ボタンのタッチ可能な部分は、アニメーション化されているときにボタンの表示フレームと一致しません。

内部的には、ボタンのフレームはアニメーションの最終的な値に設定されます。この領域をタップできることがわかるはずです。これでうまくいきます。

移動するボタンを押すには、ボタンの.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;
        }
    }
}
53
jrturton

スウィフト5

私の場合、button.alpha = 0、オプションとしてUIViewAnimationOptionAllowUserInteractionを設定しても、ボタンの相互作用が機能しなくなります。

理由

アニメーションを定義するかどうかにかかわらず、ビューのプロパティはビューのレイヤーにすぐに適用されます。このため、view.alpha=0viewを完全に非表示にします。

解決

簡単、単純に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
        })
55
nahung89

オプションの順序は重要です。最初にUIViewAnimationOptionAllowUserInteractionを配置してから、他のオプションを追加する必要があります。

Swift 3使用:.allowUserInteraction

28
Elvis

私は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;
}
1
Xu Junwen

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

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.
    }
}
1
Schemetrical

Swift 3. 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
    }
}
1
Sekhar22

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)
            }
        }
    }
0
Mahmoud Shahoud

魅力のように働く

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 答えも正しいけど

0
Tim

画面には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 }
        }
    
0
Mile Dev