Подтвердить что ты не робот

Нажмите и удерживайте кнопку "повторить огонь"

Я упомянул о многих других проблемах с кнопкой нажатия и удержания, но с Swift мало что связано. У меня есть одна функция, подключенная к кнопке с помощью события touchUpInside:

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

... и еще одна функция, предназначенная для вызова функции выше, когда одна и та же кнопка удерживается нажатой, и останавливается, когда кнопка больше не нажата:

@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()
    }
}

Я не уверен, что я делаю неправильно, и я не знаю, как настроить сенсорные события на ту же кнопку для другой функции.

4b9b3361

Ответ 1

Вы хотите быстро повторить огонь, когда ваша кнопка удерживается.

buttonDown и buttonUp должны быть определены на верхнем уровне, а не внутри другой функции. Для демонстрационных целей, яснее отказаться от проводки @IBAction с раскадровки и просто настроить кнопку в 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])
    }
}

Кроме того, я изменил .touchUpOutside на [.touchUpInside,.touchUpOutside] (чтобы поймать оба события касания) и вызвать singleFire на начальной buttonDown для одиночного огня. При этих изменениях нажатие кнопки запускается немедленно, а затем срабатывает каждые 0.3 секунды до тех пор, пока кнопка удерживается нажатой.


Кнопка может быть подключена в раскадровке вместо настройки в viewDidLoad. В этом случае добавьте @IBAction в buttonDown и buttonUp. Затем Control -click на вашей кнопке в раскадровке и перетащите ее из круга рядом с Touch Down, чтобы func buttonDown, и перетащите ее из кругов рядом с Touch Up Inside и Touch Up func buttonUp для func buttonUp.

enter image description here

Ответ 2

В моем первоначальном ответе я ответил на вопрос о том, как кнопка распознает как крана, так и длинную печать. В уточненном вопросе, кажется, вы хотите, чтобы эта кнопка постоянно загорелась, пока пользователь держит палец вниз. Если в этом случае требуется только один распознаватель жестов.

Например, в Interface Builder перетащите длинный идентификатор распознавания жестов из библиотеки объектов на кнопку, а затем установите значение "Min Duration" равным нулю:

введите описание изображения здесь

Затем вы можете control -drag от длинного распознавателя жестов нажатия к вашему коду в помощнике редактора и добавить @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 (просто перетащите соответствующие жесты из библиотеки объектов на кнопку, а затем control -расс от распознавателя жестов до функций @IBAction), но было легче проиллюстрировать, что происходит, показывая его программно.

Ответ 3

Я обновил коды примеров @vacawama до скорости 3. Спасибо.

@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])
}