Reputation: 7367
I have a function which switches between UICollectionView:
func toggleCollectionView(target: NSObject, targetName:String){
self.view.userInteractionEnabled = false
if let tempTarget = target as? UICollectionView {
//if selected item is same as active one, won't do anything
if(targetName != activeToolbarName){
tempTarget.hidden = false
tempTarget.frame.origin.y = screenSize.height
UIView.animateWithDuration(0.4, delay: 0.0, options: .CurveEaseOut, animations: {
if let tempActiveToolbar = self.activeToolbar as? UICollectionView {
tempActiveToolbar.frame.origin.y = self.screenSize.height
}
tempTarget.frame.origin.y = self.screenSize.height - tempTarget.frame.height - self.selectorsContainer.frame.height
}, completion: { finished in
if let tempActiveToolbar = self.activeToolbar as? UICollectionView {
tempActiveToolbar.hidden = true
self.activeToolbar = target
self.activeToolbarName = targetName
self.view.userInteractionEnabled = true
}
})
}
}
}
It's triggered by several button on the screen like this:
@IBAction func showFontsTool(sender: UIBarButtonItem) {
toggleCollectionView(fontsCV, targetName:"fontsCV")
}
If user tap on buttons very fast, completion
block won't be called sometimes and self.view.userInteractionEnabled
won't be enabled. How can I make sure completion
block will be called always after starting animation?
Update
Fixed function which works fine:
func toggleCollectionView(target: NSObject, targetName:String){
if let tempTarget = target as? UICollectionView {
//if selected item is same as active one, won't do anything
if(targetName != activeToolbarName){
tempTarget.hidden = false
tempTarget.frame.origin.y = screenSize.height
if (runningAnimation == false){
runningAnimation = true
self.activeToolbarName = targetName
UIView.animateWithDuration(0.4, delay: 0.0, options: .CurveEaseOut, animations: {
if let tempActiveToolbar = self.activeToolbar as? UICollectionView {
tempActiveToolbar.frame.origin.y = self.screenSize.height
}
tempTarget.frame.origin.y = self.screenSize.height - tempTarget.frame.height - self.selectorsContainer.frame.height
}, completion: { finished in
if let tempActiveToolbar = self.activeToolbar as? UICollectionView {
tempActiveToolbar.hidden = true
self.activeToolbar = target
self.runningAnimation = false }
})
}
}
}
}
Upvotes: 1
Views: 117
Reputation: 9246
First, you must declare a Global Bool variable.
var isAnimating:BOOL = False
Now in button action,
@IBAction func showFontsTool(sender: UIBarButtonItem) {
if(!isAnimating) //if animation is not happening then call the method
{
toggleCollectionView(fontsCV, targetName:"fontsCV")
}
else //if animation is happening then return
{
return;
}
}
Now in animation method i.e func toggleCollectionView
,set the value for isAnimating
variable, like this :-
func toggleCollectionView(target: NSObject, targetName:String){
isAnimating=True //notify animation has started, so that this method don't get fire on button click
self.view.userInteractionEnabled = false
if let tempTarget = target as? UICollectionView {
//if selected item is same as active one, won't do anything
if(targetName != activeToolbarName){
tempTarget.hidden = false
tempTarget.frame.origin.y = screenSize.height
UIView.animateWithDuration(0.4, delay: 0.0, options: .CurveEaseOut, animations: {
if let tempActiveToolbar = self.activeToolbar as? UICollectionView {
tempActiveToolbar.frame.origin.y = self.screenSize.height
}
tempTarget.frame.origin.y = self.screenSize.height - tempTarget.frame.height - self.selectorsContainer.frame.height
}, completion: { finished in
if let tempActiveToolbar = self.activeToolbar as? UICollectionView {
tempActiveToolbar.hidden = true
self.activeToolbar = target
self.activeToolbarName = targetName
self.view.userInteractionEnabled = true
isAnimating=False //now set bool value on this completion handler to false so that this method can get fired again.
}
})
}
}
}
Upvotes: 1
Reputation: 6781
This is workaround:
Create a boolean variable animationTriggering
that sets itself to true when your button is pressed. This variable can act as a flag for you to check; If it's true, don't execute the UIView animation a second time. You'll only execute the UIView animation when it's in the false state.
After the UIView animation completes, set it back to false so it can be triggered again next time.
I'm guessing the problem is due to multi-threaded issues. I've encountered this before and I got around it doing this, but I really have no confidence in claiming I know what the problem was.
If I could hazard a guess, it's that UIView animations have an internal asynchronous thread that is some how interfering with each other if you send it requests too quickly.
Upvotes: 1