How to pause an animation in SWIFT? - ios

I have seen several answers here on stackoverflow but they are all diferent to what I really need, and also they are all in Objective C...
So what I have is an animation of an object called stick that starts up and then goes down to the screen. When I press a button, I want it to pause and I want the stick to be precisely at that spot!
Also, hand is the button that stops the animation, that only stops when hand and stick and overlap.
Here's my code:
#IBAction func touchHand(sender: UIButton) {
let stickPresentationFrame = (stick.layer.presentationLayer() as! CALayer).frame
if !CGRectIsNull(CGRectIntersection(stickPresentationFrame, hand.frame)){
println("sup")
}
}
override func viewDidAppear(animated: Bool) {
UIView.animateWithDuration(2, animations: {self.stick.center = CGPointMake(self.stick.center.x, 760)}, completion: nil)
}
Any ideas? Thanks

Related

When using scrollView.zoom I get different results from pressing a button to calling a function

I am trying to zoom to a button on a UIScrollView. The first function is one that I run if I segue to the vc, the second is run from a button in the view.
When running the second function it goes perfectly and centres correctly
On the first however, despite being identical code, it has an offset in the x of about 40-50 pixels (Shows a point left of the button, not any part of it) although this offset changes dependent on the button.
func moveScrollviewToRoom(){
scrollView.zoom(to: artButton.frame, animated: true)
}
#IBAction func moveScrollview(_ sender: Any) {
scrollView.zoom(to: artButton.frame, animated: true)
}
The function was called here:
override func viewDidLoad() {
super.viewDidLoad()
if moveToRoom != "" {
moveScrollviewToRoom()
}
Any help would be greatly appreciated
Cheers!
Two points...
1 - Views and subviews have not yet been positioned by auto-layout in viewDidLoad(), so the artButton.frame will not be correct.
2 - Calling any function that animates a view from viewDidLoad() won't give the desired results, because the view is not even visible yet.
Move your call to viewDidAppear():
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
if moveToRoom != "" {
moveScrollviewToRoom()
}
}

Why are animated calls of UIView.isHidden = false compounded?

As you can see I'm having trouble formulating the question. Let me try to explain:
I'm using a search bar in my swift ios app. In order to get a desired animation effect I put it in a vertical stack view and then animate its isHidden property. This way search bar pushes down other children of the stack view as it animates in or pulls them up as it animates out. So far so good.
I've noticed a behavior that I think is strange. Could be a bug or could be me not understanding how things work. Basically if I call search bar hiding method x times in a row I need to call search bar showing method x times before it would show. I'd expect to have to make just one call to show search bar regardless of how many times I called hiding method. The issue doesn't exist other way around: if I call search bar showing code x times I only need to call hiding method once for it to go away. This doesn't happen if I set isHidden without animating it...
Here's a sample code and a video of the issue. I'd appreciate it if someone would help me understand this behavior.
class ViewController: UIViewController {
#IBOutlet weak var searchBar: UISearchBar! {
didSet {
searchBar.isHidden = true
}
}
#IBAction func showAction(_ sender: UIButton) {
expandSearch()
}
#IBAction func hideAction(_ sender: UIButton) {
collapseSearch()
}
private func expandSearch() {
UIView.animate(withDuration: 0.3){
self.searchBar.isHidden = false
}
}
private func collapseSearch() {
UIView.animate(withDuration: 0.3){
self.searchBar.isHidden = true
}
searchBar.resignFirstResponder()
}
}
You should not call an asynchronous animation of searchbar x times, instead of I suggest you to keep its state in variable, something like isSearchBarHidden,
and check it before show/hide search bar. You could use just one method with such signature showSearchBar(show: Bool) and setting this variable there.
#IBAction func showAction(_ sender: UIButton) {
showSearchBar(true)
}
#IBAction func hideAction(_ sender: UIButton) {
showSearchBar(false)
}
private
func showSearchBar(_ show: Bool) {
guard isSearchBarHidden != show else {
return
}
UIView.animate(withDuration: 0.3, animations: {
self.searchBar.isHidden = show
}) {
self.isSearchBarHidden = show
if !show && searchBar.isFerstResponder {
searchBar.resignFirstResponder
}
}
}
private
var isSearchBarHidden: Bool = true
Also it is a good practice to check if your textView/textField/searchBar isFirstResponder before call resignFirstResponder.
Hope it will help. Good luck!

How to change colour of scrollbar when calling flashScrollIndicator() in Swift 3

I just discovered how to properly use flashScrollIndicator but I'm having trouble changing the colour of the scroller when it flashes. I want to change it to red. This is what I have for my function to flash the scroller when the viewTable loads up:
override func viewDidAppear(_ animated: Bool) {
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
self.tableView.flashScrollIndicators()
}
I also was able to create a function that actually changes the scroller but only when the user actually scrolls, not when the scroller simply flashes. If its any help, this is the code for it:
func scrollViewDidScroll(_ scrollView: UIScrollView) {
let verticalIndicator = scrollView.subviews.last as? UIImageView
verticalIndicator?.backgroundColor = UIColor.red
}
Any help would be great! Thanks.
You just need to put those two pieces together. If you add the color-changing code to viewDidAppear, it should work just like it does in scrollViewDidScroll.
override func viewDidAppear(_ animated: Bool) {
let verticalIndicator = tableView.subviews.last as? UIImageView
verticalIndicator?.backgroundColor = UIColor.red
self.tableView.flashScrollIndicators()
}
While this is possible, I don't recommend it for a few reasons:
It's fragile. Since this isn't a readily available public API, there's no guarantee this will work in future iOS versions.
It's probably a bad design choice. Do you really want to point the user's attention to a differently colored scroll bar? Most users familiar with other apps are used to the default look of the scroll bars, so it communicates their position in the view without much extra attention. Also it doesn't look very nice when I just tried it.
The alternative solution is to simply set the scroll indicator style to one of the available options (default, white, or black).
tableView.indicatorStyle = .default

Swift animations in a new view happen instantly

One my primary view, animations execute according to the delay and durations set. However, when I segue to a new view, the animations are all complete instantly. How is this? This only happens when this animations told to execute from code in the viewDidLoad()
override func viewDidLoad() {
super.viewDidLoad()
setProgress()
}
// the function below annimates a circular progress view
func setProgress() {
var to:Double = ((360.0 / 4) * increment)
progressCircle.animateFromAngle(360.0, toAngle: to, duration: 5) { completed in
if completed {
print("animation stopped, completed")
} else {
print("animation stopped, was interrupted")
}
}
}
viewDidLoad occurs before the view is presented on screen, if you want to run animations you should probably run them in viewDidAppear so that the view is loaded on screen and the components are visible.
call setProgress() in viewDiDAppear
override public func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
setProgress()
}
Maybe you can add delay and keep it running on a timer after viewdidload event of controller. Therefore you may reach your goal and run the animations in the time that you want.

Swift: dispatch_async is effect is inconsistent for animation

I have a globalstate in my app. Depending on the state the GUI is different.
When I go from the start View A to View B I have globalstate 3
It should show an information screen, but it doesn't. BUT: When the View B has loaded only once and I jump from View C/D/E back to View B, then the code work perfectly. (You have to be in View A to get in View B.)
I use a lot dispatch_async(dispatch_get_main_queue.. that isn't good style, is it?
Why is my animation not loading at the beginning? What is good style? Thank you for answers and sorry for mistakes (english isn't my mothertongue)
override func viewDidLoad() {
super.viewDidLoad()
dispatch_async(dispatch_get_main_queue(), {
self.animateTheInformationViewWhenGlobalStateIsThree()
})
}
func animateTheInformationViewWhenGlobalStateIsThree() {
print("GLOGBALSTATE \(globalState)") //it is 3
if globalState == 3 {
setGlobalState(3)
dispatch_async(dispatch_get_main_queue(), {
GUITools.animateTheInformationView(self.tableView, animateBottomLayout: self.animationBottomConstraint, value: self.negativValue)
})
print("THE POSITIV VALUE THE NEGATIV")
}
//GUITools-Static-Class:
class func animateTheInformationView(tableView: UITableView, animateBottomLayout: NSLayoutConstraint, value: CGFloat) {
dispatch_async(dispatch_get_main_queue(), {
animateBottomLayout.constant += value
UIView.animateWithDuration(Constants.animationTime, animations: { () -> Void in
tableView.layoutIfNeeded()
},completion: {
(value: Bool) in
})
})
}
EDIT
With viewDidAppear it works. But the animation isn't a real animation. The tableView "jumps". So there is no sliding/animation.
I deleted all dispatch_async..
override func viewDidAppear(animated: Bool) {
self.animateTheInformationViewWhenGlobalStateIsSeven()
}
viewDidLoad() does not mean that your view is already visible. Since it's not visible yet you cannot apply animations to it.
viewDidLoad() is only meant to configure your view controller's view and set up your view hierarchy - i.e. to add subviews.
What you want to use is viewWillAppear() (or viewDidAppear()) to start your animation as soon as the view becomes (or became) visible.
Also all the dispatch_async calls are most likely unnecessary. You usually only need them when you are not on the main (= UI) thread. Simply remove them.

Resources