J'ai évoqué d'innombrables autres questions sur un bouton-poussoir mais il n'y en a pas beaucoup qui soient liées à Swift. Une fonction est connectée à un bouton à l'aide de l'événement touchUpInside:
@IBAction func singleFire(sender: AnyObject){
//code
}
... et une autre fonction destinée à appeler la fonction ci-dessus plusieurs fois lorsque le même bouton est maintenu enfoncé et à s'arrêter lorsque le bouton n'est plus enfoncé:
@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()
}
}
Je ne sais pas trop ce que je fais de mal et je ne sais pas comment configurer les événements tactiles sur le même bouton pour une fonction différente.
Vous voulez une répétition rapide du tir lorsque votre bouton est maintenu enfoncé.
Vos méthodes buttonDown
et buttonUp
doivent être définies au niveau supérieur, et non à l'intérieur d'une autre fonction. À des fins de démonstration, il est plus facile de ne pas connecter @IBAction
s à partir du storyboard et de configurer le bouton dans viewDidLoad
name__:
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])
}
}
De plus, j'ai remplacé .touchUpOutside
par [.touchUpInside, .touchUpOutside]
(pour intercepter les deux retouches) et appelé singleFire
sur le buttonDown
initial pour un tir unique. Avec ces modifications, une pression sur le bouton se déclenche immédiatement, puis toutes les 0.3
secondes tant que le bouton est maintenu enfoncé.
Le bouton peut être connecté au Storyboard au lieu de le configurer dans viewDidLoad
name__. Dans ce cas, ajoutez @IBAction
à buttonDown
et buttonUp
name__. ensuite Control-Cliquez sur votre bouton dans le scénarimage et faites-le glisser du cercle situé à côté de . Appuyez vers le bas jusqu'à func buttonDown
, puis faites-le glisser des cercles situé à côté de Retouchez à l'intérieur et Retouchez l'extérieur vers func buttonUp
.
Dans ma réponse initiale, j'ai répondu à la question de savoir comment un bouton reconnaît à la fois un appui long et un appui long. Dans la question clarifiée, il semble que vous souhaitiez que ce bouton "se déclenche" continuellement tant que l'utilisateur maintient son doigt appuyé. Si tel est le cas, un seul identificateur de geste est nécessaire.
Par exemple, dans Interface Builder, faites glisser un outil de reconnaissance des gestes appuyés depuis la bibliothèque d'objets vers le bouton, puis définissez la "Durée minimale" sur zéro:
Ensuite vous pouvez control-Faites glisser la reconnaissance de geste de presse longue vers votre code dans l'éditeur adjoint et ajoutez un @IBAction
pour gérer la pression longue:
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")
}
Ou, si vous souhaitez également arrêter de tirer lorsque l'utilisateur fait glisser son doigt hors du bouton, cochez la case suivante pour afficher l'emplacement du geste:
@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
}
}
Ma réponse initiale, répondant à la question différente de savoir comment reconnaître à la fois les tapotements et les appuis prolongés sur un bouton, est la suivante:
Personnellement, j'utiliserais des outils de reconnaissance de gestes au toucher et aux appuis longs, par exemple
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")
}
}
Si vous le souhaitez, vous pouvez également appuyer sur .Ended
en effectuant un appui long. Cela dépend de l'UX souhaitée.
Pour votre information, vous pouvez également ajouter ces deux dispositifs de reconnaissance de mouvements directement dans Interface Builder (il suffit de faire glisser les mouvements respectifs de la bibliothèque d'objets vers le bouton, puis control-fait glisser de la reconnaissance de geste vers les fonctions @IBAction
), mais il était plus facile d'illustrer ce qui se passait en le montrant par programme.
J'ai mis à jour les exemples de codes @vacawama vers Swift 3. Merci.
@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])
}