2015-12-12 7 views
11

私は、押して保持するボタンについては数え切れないほどの質問をしましたが、Swiftにはあまり関係がありません。無「繰り返しの再開」のための押し続けるボタン

@IBAction func singleFire(sender: AnyObject){ 
    //code 
} 

...と同じボタンを押している間、繰り返し上記の関数を呼び出すことを意図している他の機能、およびボタンがあるときに停止します。私は1つのtouchUpInsideイベントを使用して、ボタンに接続された機能を持っています長く押す:

@IBAction func speedFire(sender: AnyObject){ 

    button.addTarget(self, action: "buttonDown:", forControlEvents: .TouchDown) 
    button.addTarget(self, action: "buttonUp:", forControlEvents: .TouchUpOutside) 

    func buttonDown(sender: AnyObject){ 
     timer = NSTimer.scheduledTimerWithTimeInterval(0.3, target: self, selector: "singleFire", userInfo: nil, repeats: true)  
    } 

    func buttonUp(sender: AnyObject){ 
     timer.invalidate() 
    } 
} 

私は私が間違ってやっているかわからないんだけど、と私はどのように異なる機能のために、同じボタンに設定タッチイベントをするのか分かりません。

答えて

21

あなたのボタンを押したままにすると、急速に繰り返して欲しいです。

buttonDownbuttonUpのメソッドは、トップレベルで定義する必要があり、別の関数の内部で定義する必要はありません。デモの目的のために、ストーリーボードから@IBAction Sをアップ配線だけviewDidLoadにボタンを設定見送るために明確である:

class ViewController: UIViewController { 

    @IBOutlet weak var button: UIButton! 
    var timer: Timer? 
    var speedAmmo = 20 

    @objc func buttonDown(_ sender: UIButton) { 
     singleFire() 
     timer = Timer.scheduledTimer(timeInterval: 0.3, target: self, selector: #selector(rapidFire), userInfo: nil, repeats: true) 
    } 

    @objc func buttonUp(_ sender: UIButton) { 
     timer?.invalidate() 
    } 

    func singleFire() { 
     print("bang!") 
    } 

    @objc func rapidFire() { 
     if speedAmmo > 0 { 
      speedAmmo -= 1 
      print("bang!") 
     } else { 
      print("out of speed ammo, dude!") 
      timer?.invalidate() 
     } 
    } 

    override func viewDidLoad() { 
     super.viewDidLoad() 

     // These could be added in the Storyboard instead if you mark 
     // buttonDown and buttonUp with @IBAction 
     button.addTarget(self, action: #selector(buttonDown), for: .touchDown) 
     button.addTarget(self, action: #selector(buttonUp), for: [.touchUpInside, .touchUpOutside]) 
    } 
} 
また、私は(両方キャッチイベントをタッチアップする)と singleFireを呼び出す [.touchUpInside, .touchUpOutside].touchUpOutsideを変更しました火災の場合は buttonDownとなります。これらの変更で、ボタンを押すと直ちにボタンが押され、ボタンが押されている間、毎秒 0.3秒間だけ起動します。


ボタン代わりviewDidLoadでそれを設定するストーリーボード中に配線することができます。この場合、@IBActionbuttonDownbuttonUpに追加します。その後タッチアップインサイドの隣にあなたのストーリーボード内のボタンと次ダウンfunc buttonDownにタッチ、円からドラッグまで円からドラッグにコントロール +クリックしてfunc buttonUpに外をタッチアップ。

enter image description here

+0

すごく、ありがとう!しかし、単なる不思議なことに、singleFireを@IBAction関数としてトップレベルの標準関数に移動する必要があります。これはbuttonDown()とIBAction singleFireが同時に発生するためですか?もう一度おねがいします – Tim

+0

タイマーに渡される関数 'singleFire'のセレクタは、パラメータを取らないことを示しているので、 '@ IBAction'とタイマーから呼び出すのは意味がありません。 1つの '.TouchDown'ハンドリング関数を持って、それがsingleFireを呼び出させるようにするのは明らかです。 – vacawama

+0

偉大な、説明のおかげで。 – Tim

12

私の元の答えでは、ボタンをタップと長押しの両方を認識させる方法の質問に答えました。明らかになった質問では、ユーザーが指を押さえている限り、このボタンを連続的に「発射」したいと思われます。その場合、ジェスチャー認識機能は1つだけ必要です。

はたとえば、Interface Builderで、ボタン上にオブジェクトライブラリから長押しジェスチャー認識をドラッグして、ゼロに「最低期間」を設定:することができます制御その後

enter image description here

をアシスタントエディタでコードの長押しジェスチャー認識から押しながらドラッグして長押しを処理するために@IBActionを追加:

var timer: NSTimer? 

@IBAction func longPressHandler(gesture: UILongPressGestureRecognizer) { 
    if gesture.state == .Began { 
     timer = NSTimer.scheduledTimerWithTimeInterval(0.3, target: self, selector: "handleTimer:", userInfo: nil, repeats: true) 
    } else if gesture.state == .Ended || gesture.state == .Cancelled { 
     timer?.invalidate() 
     timer = nil 
    } 
} 

func handleTimer(timer: NSTimer) { 
    NSLog("bang") 
} 

あるいは、あなたはまた、ユーザーがボタンの外に指をドラッグしたときに発射を停止したい場合は、ジェスチャーの場所のチェックを追加します。

@IBAction func longPressHandler(gesture: UILongPressGestureRecognizer) { 
    if gesture.state == .Began { 
     timer = NSTimer.scheduledTimerWithTimeInterval(0.3, target: self, selector: "handleTimer:", userInfo: nil, repeats: true) 
    } else if gesture.state == .Ended || gesture.state == .Cancelled || (gesture.state == .Changed && !CGRectContainsPoint(gesture.view!.bounds, gesture.locationInView(gesture.view))) { 
     timer?.invalidate() 
     timer = nil 
    } 
} 

私のオリジナルの答えを、両方のタップを認識する方法の別の質問に答えると、ボタンを長く押すが、以下である:あなたがしたい場合は、長押しジェスチャーで、

override func viewDidLoad() { 
    super.viewDidLoad() 

    let longPress = UILongPressGestureRecognizer(target: self, action: "handleLongPress:") 
    button.addGestureRecognizer(longPress) 

    let tap = UITapGestureRecognizer(target: self, action: "handleTap:") 
    tap.requireGestureRecognizerToFail(longPress) 
    button.addGestureRecognizer(tap) 
} 

func handleTap(gesture: UITapGestureRecognizer) { 
    print("tap") 
} 

func handleLongPress(gesture: UILongPressGestureRecognizer) { 
    if gesture.state == .Began { 
     print("long press") 
    } 
} 

個人的に

、私はタップして長押しジェスチャー認識装置、例えばを使用したいです.Endedにあなたの行動を実行することもできます。それは単に目的のUXに依存します。

FYI、あなたはまた、右Interface Builderでこれら二つのジェスチャー認識機能を追加することができ、あまりにも、(単にボタンの上のオブジェクトライブラリからそれぞれのジェスチャーをドラッグして、制御@IBAction機能にジェスチャー認識から押しながらドラッグ)プログラマチックに表示することで何が起きているのかを簡単に説明することができました。

+0

返信いただきありがとうございます。 speedFire()の 'timer = NSTimer.scheduledTimerWithTimeInterval(0.3、target:self、selector:" singleFire "、userInfo:nil、repeats:true)'は優雅な解決策のようですが、何らかの理由でクラッシュします。 – Tim

+1

@Tim - もしその道に行くなら、singleFire: "'(コロンはメソッドにパラメータがあることを指定します)が必要です。また、 'func singleFire(timer:NSTimer){...}'と定義するべきでしょう。そのパラメータは "送信者"だけではなくタイマーになるので、それを明確にするかもしれません。そして、このタイマーハンドラの場合には当てはまらない、Interface Builderを介してアクションに接続しようとしているメソッドだけを対象としているので、これを '@ IBAction'にしなかったことに注意してください。しかし、ジェスチャーレコグナイザーの長所は、タイマーが必要ないということです。 – Rob

+0

多くのことを明確にしてくれる、うれしいロブ。私はそれにも行くだろう。 – Tim

1

私はSWIFT 3.感謝に例のコード@vacawama更新。

@IBOutlet var button: UIButton! 

var timer: Timer! 
var speedAmmo = 100 

@IBAction func buttonDown(sender: AnyObject) { 
    singleFire() 
    timer = Timer.scheduledTimer(timeInterval: 0.1, target: self, selector:#selector(rapidFire), userInfo: nil, repeats: true) 
} 

@IBAction func buttonUp(sender: AnyObject) { 
    timer.invalidate() 
} 

func singleFire() { 
    if speedAmmo > 0 { 
     speedAmmo -= 1 
     print("bang!") 
    } else { 
     print("out of speed ammo, dude!") 
     timer.invalidate() 
    } 
} 

func rapidFire() { 
    if speedAmmo > 0 { 
     speedAmmo -= 1 
     print("bang!") 
    } else { 
     print("out of speed ammo, dude!") 
     timer.invalidate() 
    } 
} 

override func viewDidLoad() { 
    super.viewDidLoad() 

    button.addTarget(self, action:#selector(buttonDown(sender:)), for: .touchDown) 
    button.addTarget(self, action:#selector(buttonUp(sender:)), for: [.touchUpInside, .touchUpOutside]) 
} 
関連する問題