In implementing the following cross-dissolve custom segue on a button press (learned at https://www.youtube.com/watch?v=xq9ZVsLNcWw):
override func perform() {
var src:UIViewController = self.sourceViewController as! UIViewController
var dstn:UIViewController = self.destinationViewController as! UIViewController
src.view.addSubview(dstn.view)
dstn.view.alpha = 0
UIView.animateWithDuration(0.75 , delay: 0.1, options: UIViewAnimationOptions.TransitionCrossDissolve, animations: { () -> Void in
dstn.view.alpha = 1
}) { (finished) -> Void in
dstn.view.removeFromSuperview()
src.presentViewController(dstn, animated: false, completion: nil)
}
}
I am getting the "Unbalanced calls to begin/end appearance transitions" warning/error.
I have thoroughly searched many stackoverflow questions:
Unbalanced calls to begin/end appearance transitions for <UITabBarController: 0x197870>
Keep getting "Unbalanced calls to begin/end appearance transitions for <ViewController>" error
Unbalanced calls to begin/end appearance transitions for UITabBarController
my response: I tried making all segues perform within a dispatch as shown below
let delay = 0.01 * Double(NSEC_PER_SEC)
let time = dispatch_time(DISPATCH_TIME_NOW, Int64(delay))
dispatch_after(time, dispatch_get_main_queue()) {
self.performSegueWithIdentifier("toNonFBLogin", sender: nil)
}
however still to no avail, the warning/error still pops up.
I'm aware what the error means, that it's attempting to present a new viewcontroller before the previous one loads, but not sure how to tackle it.
The only lead I have is that the previous (source) viewcontroller pops up real quickly before the final viewcontroller loads, as seen at 0:04 at
The glitch only actually appears I would guess around 5% of the time, however.
Any ideas?
Looks like this behaviour is the result of some - as of yet - undocumented change in recent iOS releases. Getting frustrated with your exact issue and the lack of satisfying answers I've come up with this:
// create a UIImageView containing a UIImage of `view`'s contents
func createMockView(view: UIView) -> UIImageView {
UIGraphicsBeginImageContextWithOptions(view.frame.size, true, UIScreen.mainScreen().scale)
view.drawViewHierarchyInRect(view.bounds, afterScreenUpdates: true)
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return UIImageView(image: image)
}
override func perform() {
let src:UIViewController = self.sourceViewController as! UIViewController
let dstn:UIViewController = self.destinationViewController as! UIViewController
let mock = createMockView(dstn.view)
src.view.addSubview(mock)
mock.alpha = 0
UIView.animateWithDuration(0.75, delay: 0.1, options: UIViewAnimationOptions.TransitionCrossDissolve,
animations: { () -> Void in
mock.alpha = 1
},
completion: { (finished) -> Void in
src.presentViewController(dstn, animated: false, completion: { mock.removeFromSuperView()})
})
}
createMockView()
will create a UIImageView
containing a snapshot of the destination VCs contents and use that to do the transition animation.
Once the transition is finished the real destination VC is presented without animation causing a seamless transition between both. Finally once the presentation is done the mock view is removed.