I'm building an app that simulates Conway's Game Of Life. I'm trying to run an infinite animation when a RUN button is pressed. Here's my code:
//When RUN button is clicked, call run repeat
#IBAction func run(sender: AnyObject) {
UIView.animateWithDuration(3, delay: 2, options: [.Repeat], animations: {self.runrepeat()}, completion: nil)
}
//Run repeat calls run, calculating next generation of the board
func runrepeat() {
board.run()
//Update the appearance of all the buttons based on their new values
for cellButton in self.cellButtons {
cellButton.setTitle("\(cellButton.getLabelText())",
forState: .Normal)
}
}
When the RUN UI button is pressed, I want run() to be called, which should continuously call runrepeat() every 3 seconds. board.run() runs algorithms to determine the configuration of the next generation of cells, and the for cellButton{} loop updates that appearances of all the cells.
However, as is, runrepeat() is only called once, so the next generation appears on the board and the animation stops, without any delays. My RUN button is correctly executing runrepeat(), but only once. I want it to repeat forever.
I tried this too:
//Run repeat calls run, calculating next generation of the board
func runrepeat() {
while(true){
board.run()
//Update the appearance of all the buttons based on their new values
for cellButton in self.cellButtons {
cellButton.setTitle("\(cellButton.getLabelText())",
forState: .Normal)
}
}
}
but the infinite while loop just causes my program to freeze up. The updating of the screen never happens.
Can someone please help me with executing a continuous function call, screen update loop?
Related
I'm currently working on an AR app and I want to trigger an action (for example a scene change) only when a button was pressed beforehand. Here's the process I'm trying to build:
Object spawns in front of user
User taps on button to select a tool
User taps on the object and the action sequence from the RC project gets triggered
So far I created a the initial loading of the Scene,
guard let anchor = try? Excavation.loadDirtpile() else {
return
}
a variable for the button state
var shovelPressed: Bool = false
And a function that sets the state
#IBAction func shovelButtonPressed(_ sender: Any) {
shovelPressed = true
}
This is my first time working with Swift and Reality Composer so I'm quite confused :D
I hope you can help!
I have a button linked to an IBAction that takes around 5-10 seconds to complete.
While the IBAction function is running I want to print progress messages in my view/window.
The only message that prints is the last one, and it prints after the IBAction has completed (ie the button is not blue).
When I step through the function in the debugger, no messages are visible until I'm out of the function (ie button is not blue).
Any idea on how to update the window while the callback is being executed?
embed the IBAction logic inside a background queue:
DispatchQueue.global(qos: .background).async {
// background code
}
and anytime you need to print the progress in your view/window embed that part inside:
DispatchQueue.main.async {
// user interface code
}
generally you may want to try this:
DispatchQueue.global(qos: .background).async {
//background code
DispatchQueue.main.async {
//your main thread
}
}
I want to execute a scroll animation after NSFetchedResultsController has finished animating moving a tableview row between sections.
The update of the section happens when I dismiss a view controller. This triggers the animation of the tableview which works fine. after this animation I want the tableview to scroll to the new location. However this does not work the scrolling starts before the animation has finished.
self.dismissViewControllerAnimated(true, completion: {
shoppingItem.category = self.categoryTextField.text // this line triggers the move between sections
dispatch_async(dispatch_get_main_queue(), {
tableView.scrollToRowAtIndexPath(indexPath, atScrollPosition: .Bottom, animated: true)
do {
try self.coreDataStack.context.save()
} catch {
fatalCoreDataError(error)
}
})
})
I realize that the solution lies in managing the threads but I cannot figure out how to set this up correctly.
SOLUTION:
By delaying the scrolling by one second the animation works fine:
var time = dispatch_time(dispatch_time_t(DISPATCH_TIME_NOW), 1 * Int64(NSEC_PER_SEC))
dispatch_after(time, dispatch_get_main_queue(), {
self.indexPathOfChangedItem = self.fetchedResultsController.indexPathForObject(shoppingItem)
self.itemsTableView.scrollToRowAtIndexPath(self.indexPathOfChangedItem, atScrollPosition: .Bottom, animated: true)
})
Note that the definition of the new indexpath, called indexPathOfChangedItem also has to be in the delayed call as this index path is not up to date until the animation triggered by the NSFetchedResultsController is completed.
The FRC isn't doing the animation, it's just observing the change and telling you, your code then tells the table view and it does the animation.
The animation takes no parameters to tell you when it's done or to specify how long it should take :-( raise a radar with Apple.
The simple option is to guess how long the animation runs for and dispatch after a delay to run your scroll after the animation. It's probably trial and error to find the right amount of time to wait, and once found it's unlikely to change / be different for other devices.
I am trying to implement buttons with changeable background colors in Swift. But I do not want to change color of whole button. I want it to change like progress bar. For example, there will be a specific time value, and background color of button should change from right to left with animation in that time.
I saw apps like that. Can anyone give a clue for how to do that?
Don't be constrained by the idea that a button is only a UIButton. A button can be a view with a gesture recogniser, or a view with a transparent button over the top of it. It could be a button with a background image that you keep updating. There are many options for how to construct what you describe. It depends exactly what effect you want as to how it should be done. Buttons showing progress would often be disabled until progress is complete so you could easily use a progress indication view which is replaced by a 'real' button when progress is complete.
Maybe this helps you.
func changeButtonColors(){
//let myBtn
let timer = NSTimer.scheduledTimerWithTimeInterval(2.0, target: self, selector: "changeColor", userInfo: nil, repeats: true)
timer.fire()
}
//cloros to iterate over each time
let colorChoice = [UIColor.blackColor(), UIColor.blueColor(), UIColor.redColor()]
var counter = 0 //tracking variable
//this gets called when by the timer
//currently it is supposed to go on forever.
func changeColor(){
//suppose i have the button outlet named myBtn
UIView.animateWithDuration(2.0, animations: { () -> Void in
myBtn.backgroundColor = colorChoice[counter]
if counter < colorChoice.count{
counter++
}else{
counter = 0
//or disable the alert
//timer.invalidate()
}
})
}
Here myBtn is a outlet to the View Controller. We use Timer to trigger the function call at a specific time in our case every 2 second. This can be derived by other app logic.
The called function then iterates over a array of UIColor and applies one to the background of the button or view. The workflow is this. Change things where you require.
I have two states of UIView:
I have animation between them using #IBaction for button:
#IBAction func tapped(sender: UIButton) {
flag = !flag
UIView.animateWithDuration(1.0) {
if self.flag {
NSLayoutConstraint.activateConstraints([self.myConstraint])
} else {
NSLayoutConstraint.deactivateConstraints([self.myConstraint])
}
self.view.layoutIfNeeded() //additional line
}
}
Animation is working only when I add additional line:
But when I remove that line, then my UIView is updated but without any animation, it happens immediately.
Why this line makes such difference? How is it working?
As I understand it, changing a constraint doesn't update the layout of your views right away. It queues the changes, and they only take place the next time the system updates layout.
By putting layoutIfNeeded inside your animation block it forces the view changes to be applied during the animation block. Otherwise they take place later, after the animation has been set up.