如何设置具有间隔/延迟的循环?

时间:2016-01-16 17:52:22

标签: ios swift uibutton

我正在尝试制作一个Wack-a-Mole游戏。我这样做的方法是让一个按钮随机出现,并在有人点击它后在屏幕周围消失。如果他们在重新出现后一秒钟内没有点击按钮,那么它将消失并找到一个新位置,然后重新出现并等待一秒钟并重复上述步骤。但是,每当我运行此代码时,它都不会这样做。只有当我摆脱'while'语句和'if else'语句时,它才会移动位置。为什么它不会循环,消失,重新出现等?

延迟是这样的:https://stackoverflow.com/a/24318861/5799228

   @IBAction func moveButton(button: UIButton) {
    while self.WaffleButton.hidden == true || false {
        if self.WaffleButton.hidden == false {
            self.WaffleButton.hidden = true
            delay(3) {
                // Find the button's width and height
                let buttonWidth = button.frame.width
                let buttonHeight = button.frame.height

                // Find the width and height of the enclosing view
                let viewWidth = button.superview!.bounds.width
                let viewHeight = button.superview!.bounds.height

                // Compute width and height of the area to contain the button's center
                let xwidth = viewWidth - buttonWidth
                let yheight = viewHeight - buttonHeight

                // Generate a random x and y offset
                let xoffset = CGFloat(arc4random_uniform(UInt32(xwidth)))
                let yoffset = CGFloat(arc4random_uniform(UInt32(yheight)))

                // Offset the button's center by the random offsets.
                button.center.x = xoffset + buttonWidth / 2
                button.center.y = yoffset + buttonHeight / 2
                self.WaffleButton.hidden = false
                self.delay(1) {
                    self.WaffleButton.hidden = true
                }
            }
        } else { delay(3) {
            // Find the button's width and height
            let buttonWidth = button.frame.width
            let buttonHeight = button.frame.height

            // Find the width and height of the enclosing view
            let viewWidth = button.superview!.bounds.width
            let viewHeight = button.superview!.bounds.height

            // Compute width and height of the area to contain the button's center
            let xwidth = viewWidth - buttonWidth
            let yheight = viewHeight - buttonHeight

            // Generate a random x and y offset
            let xoffset = CGFloat(arc4random_uniform(UInt32(xwidth)))
            let yoffset = CGFloat(arc4random_uniform(UInt32(yheight)))

            // Offset the button's center by the random offsets.
            button.center.x = xoffset + buttonWidth / 2
            button.center.y = yoffset + buttonHeight / 2
            self.WaffleButton.hidden = false
            self.delay(1) {
                self.WaffleButton.hidden = true
            }


            }
        }

    }
}

1 个答案:

答案 0 :(得分:1)

您正在以不应使用的方式使用while

这是您应该使用while

的方式
var someCondition = true

while someCondition {

    // this will loop as fast as possible untill someConditionIsTrue is no longer true
    // inside the while statement you will do stuff x number of times
    // then when ready you set someCondition to false
    someCondition = false // stop

}

这是您使用while的方式:

let someConditionThatIsAlwaysTrue = true

while someConditionThatIsAlwaysTrue {

    // condition is always true, so inifinite loop...

    // this creates a function that is executed 3 seconds after the current looping pass of the while loop.
    // while does not wait for it to be finished.
    // while just keeps going.
    // a fraction of a second later it will create another function that will execute 3 seconds later.
    // so after 3 seconds an infite amount of functions will execute with a fraction of a second between them.
    // except they won't, since the main thread is still busy with your infinite while loop.
    delay(3) {
        // stuff
    }
}

如何正确地做到这一点:

  • 请勿使用whilerepeat来计划""延迟代码执行。

  • 在较小的问题中解决问题:

问题1:创建循环

通过具有两个相互触发的函数来创建循环。 我会称他们为executeexecuteAgain

因此execute触发executeAgainexecuteAgain触发器execute,然后重新开始 - >循环!

您还可以创建execute函数,而不是直接调用executeAgainstart。这不是必需的,但它是为循环功能设置条件的好地方。 start将调用execute并开始循环。

要停止循环,您需要创建一个更改某些条件的stop函数。 executeexecuteAgain将检查此情况,并且只有在检查成功时才会继续循环。 stop使此检查失败。

var mustLoop : Bool = false

func startLoop() {
    mustLoop = true
    execute()
}

func execute() {
    if mustLoop {
        executeAgain()
    }
}

func executeAgain() {
    if mustLoop {
        execute()
    }
}

func stop() {
    mustLoop = false
}

问题2:延迟执行

如果在NSObject的子类中需要延迟,最明显的选择是NSTimer。大多数UI类(如UIButtonUIViewController)都是NSObject的子类。

NSTimer也可以设置为重复。这也会创建一个每x秒执行一次的循环。但是,由于你实际上有两个交替的动作,因此采用更详细的循环模式更有意义。

NSTimer在x个时间后执行一个函数(作为Selector("nameOfFunction")传递)。

var timer : NSTimer?

func planSomething() {
    timer = NSTimer.scheduledTimerWithTimeInterval(3, target: self, selector: Selector("doSomething"), userInfo: nil, repeats: false)
}

func doSomething() {
    // stuff
}

如果您需要延迟其他课程/结构(或者您不喜欢NSTimer),您可以使用 matt {{3}的delay功能}}

它将在x个时间后执行您在closure中输入的任何内容。

func planSomething() {
    delay(3) {
        doSomething()
    }
}

func doSomething() {
    // stuff
}

结合两种解决方案:

通过使用上面的循环模式,您现在拥有不同的功能。而不是直接调用它们来保持循环。您插入所选的延迟方法并将下一个函数传递给它。

因此,NSTimerSelector指向executeexecuteAgaindelay会将其置于closure

如何优雅地实施它:

我会继承UIButton来实现所有这些。然后你可以保持你的UIViewController更清洁。只需选择IB中的子类并照常连接IBOutlet。

posted

此子类具有timer属性,可替代您的延迟。 按钮操作wacked()也在init方法中设置。

UIViewController拨打按钮的start()功能。这将启动timer

timer会触发appear()disappear

wacked()将停止计时器并隐藏按钮。

class WackingButton : UIButton {

    var timer : NSTimer?

    var hiddenTime : NSTimeInterval = 3
    var popUpTime : NSTimeInterval = 1

    override init(frame: CGRect) {
        super.init(frame: frame)
        self.addTarget(self, action: "wacked", forControlEvents: UIControlEvents.TouchUpInside)
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        self.addTarget(self, action: "wacked", forControlEvents: UIControlEvents.TouchUpInside)
    }

    func start() {
        timer = NSTimer.scheduledTimerWithTimeInterval(hiddenTime, target: self, selector: Selector("appear"), userInfo: nil, repeats: false)
    }

    func appear() {

        self.center = randomPosition()

        self.hidden = false

        timer?.invalidate()
        timer = NSTimer.scheduledTimerWithTimeInterval(popUpTime, target: self, selector: Selector("dissappear"), userInfo: nil, repeats: false)
    }

    func dissappear() {

        self.hidden = true

        timer?.invalidate()
        timer = NSTimer.scheduledTimerWithTimeInterval(hiddenTime, target: self, selector: Selector("appear"), userInfo: nil, repeats: false)
    }

    func wacked() {
        self.hidden = true
        timer?.invalidate()
    }

    func randomPosition() -> CGPoint {

        // Find the width and height of the enclosing view
        let viewWidth = self.superview?.bounds.width ?? 0 // not really correct, but only fails when there is no superview and then it doesn't matter anyway. Won't crash...
        let viewHeight = self.superview?.bounds.height ?? 0

        // Compute width and height of the area to contain the button's center
        let xwidth = viewWidth - frame.width
        let yheight = viewHeight - frame.height

        // Generate a random x and y offset
        let xoffset = CGFloat(arc4random_uniform(UInt32(xwidth)))
        let yoffset = CGFloat(arc4random_uniform(UInt32(yheight)))

        // Offset the button's center by the random offsets.
        let x = xoffset + frame.width / 2
        let y = yoffset + frame.height / 2

        return CGPoint(x: x, y: y)
    }
}

您的UIViewController

class ViewController: UIViewController {

    @IBOutlet weak var button1: WackingButton!

    override func viewDidAppear(animated: Bool) {
        button1.start()
    }
}