I set up an interface very much like youtube in the sense that if a user presses on a UIView, I'll hide or show all of the subviews with an animation.
func setupControlViewVisiblity(playingVideo: Bool = false, duration: TimeInterval = 1.0, isExpanded: Bool = false){
var alpha:CGFloat = 1
if isShowingControls {
//If we're showing then we're about to hide everything so alpha is 0
alpha = 0
}
UIView.animate(withDuration: duration, delay: 0, usingSpringWithDamping: 1, initialSpringVelocity: 1, options: .curveEaseOut, animations: {
self.pausePlayButton.alpha = alpha
self.backButton.alpha = alpha
self.infoLabel.alpha = alpha
self.fullScreenButton.alpha = alpha
self.currentTimeLabel.alpha = alpha
self.videoLengthLabel.alpha = alpha
self.videoSlider.alpha = alpha
}, completion:nil)
isShowingControls = !isShowingControls
}
The issue that I'm having is that if I call this method and I show the controls (isShowingControls is true), my UIButtons will not recognize the first tap. This literally only happens when the views alpha go from 0 to 1 then I try to tap a button.
I tried doing the animation on the main queue... doesn't work. If I change the alpha from 0 to 1.... wait a few seconds... then tap the button.. it works. How can I make it work right after the UIButtons alpha changed?
Thanks
EDIT: I guess it has to do with the animation duration because when I switch it down to 0.2 it works. How can I keep it a bit higher and get this to work?
By default UIVIew animations do not allow user interaction while the animation is animating.
Luckily there is an animation option allowUserInteraction. Not much more information but you can see it in documentation here. Also worth checking out all the options here.
Try this:
UIView.animate(withDuration: duration, delay: 0, usingSpringWithDamping: 1, initialSpringVelocity: 1, options: [.curveEaseOut, .allowUserInteraction], animations: {
// Update the views
}, completion:nil)
Related
I'm using auto layout to animate the elements in a stack view. I've set the leading and trailing constraints, both with a priority of 1000 (required). However, as you can see from the animated gif, the stack view is initially attached on the left but not on the right.
UIView.animate(withDuration: 0.125, delay: animationDelay, options: [.curveEaseOut],
animations: {
someView.transform = CGAffineTransform.identity
someView.alpha = 1
someView.isHidden = false
}, completion: nil)
Only occurs in iphone X
I'm making a view appear and disappear from bottom of screen using CGAffinetransform(translationY), everything works fine in every devices but in iphone x. Two labels in a stack view as a subview of a uiview when animating labels start from 20px higher then animation starts.
UIView.animate(withDuration: 0.5, delay: 0, options: .curveEaseInOut, animations: {
self.stackOrderDetailsContainer.transform = CGAffineTransform(translationX: 0, y: self.stackOrderDetailsContainer.frame.size.height)
self.blackTransparentView.transform = .identity
self.blackTransparentView.alpha = 0
self.viewArrowHolder.transform = CGAffineTransform(rotationAngle: CGFloat(-2*Double.pi))
}, completion: nil)
edit: Added code snippet
Let's say a UIView has a background image A, and I want to make an animation so A slowly turns into another image B.
how can I do this effect with images in background (png, for example)? I know that with just background colors, I can do this just by using UIView.animate
Look into using Core Image, particularly the 11 CICategoryTransition filter. Move your before/after CIImages into a CALayer, and animate.
You cannot have two images in the same time (unless you haven't two imageView). That is the way with one UIImageView:
UIView.animateWithDuration(1, delay: 0, options: UIViewAnimationOptions.TransitionCrossDissolve, animations: {
self.imageView.alpha = 0.0
}, completion: nil)
UIView.animateWithDuration(1, delay: 0, options: UIViewAnimationOptions.TransitionCrossDissolve, animations: {
self.imageView.alpha = 1.0
self.imageView.image = self.secondImage
}, completion: nil)
The contentsproperty of CALayer is animatable, and you can assign CGImages to it. An animated change could look like this:
CATransaction.begin()
CATransaction.animationDuration = 2.0;
theView.layer.contents = theNewImage.CGImage
CATransaction.commit();
I have an AccountsViewController which has UIVisualEffectView with blur effect in background (subview at index 0), so that it covers previous controller with blur during transition. But when I then switch to another app and open multitasking menu again to switch back to my app, its blurry UIVisualEffectView seem to be damaged (as shown on the screenshot 1). When I make my app active again, the view gets "repaired" and looks ok again (as shown on the second screenshot)
Some code from my custom transitioning class where I add the blur view
// in the animateTransition.. method
guard let container = transitionContext.containerView() else {
return
}
let blurEffectView: UIVisualEffectView
if container.viewWithTag(101) != nil {
blurEffectView = container.viewWithTag(101)! as! UIVisualEffectView
}
else {
blurEffectView = UIVisualEffectView(frame: container.frame)
blurEffectView.translatesAutoresizingMaskIntoConstraints = false
blurEffectView.tag = 101
container.addSubview(blurEffectView)
}
if self.isPresenting {
// We're presenting a view controller over another
toViewController.view.transform = CGAffineTransformMakeScale(0.7, 0.7)
container.addSubview(toViewController.view)
container.sendSubviewToBack(blurEffectView)
toViewController.view.alpha = 0
UIView.animateWithDuration(self.transitionDuration(transitionContext), delay: 0, usingSpringWithDamping: 0.8, initialSpringVelocity: 0.8, options: [], animations: { () -> Void in
fromViewController.view.transform = CGAffineTransformMakeScale(0.8, 0.8)
toViewController.view.transform = CGAffineTransformMakeScale(1, 1)
blurEffectView.effect = UIBlurEffect(style: .Dark)
toViewController.view.alpha = 1
}, completion: { (completed) -> Void in
self.context?.completeTransition(completed)
})
}
else {
// We're dismissing a view controller
UIView.animateWithDuration(self.transitionDuration(transitionContext), delay: 0, usingSpringWithDamping: 0.8, initialSpringVelocity: 0.8, options: [], animations: { () -> Void in
fromViewController.view.transform = CGAffineTransformMakeScale(0.7, 0.7)
toViewController.view.transform = CGAffineTransformMakeScale(1, 1)
blurEffectView.effect = nil
fromViewController.view.alpha = 0
}, completion: { (completed) -> Void in
fromViewController.view.removeFromSuperview()
self.context?.completeTransition(completed)
})
}
Seems like I've figured it out myself: this issue appears only when there is an UIWindow back (which is black) showing through.
In this question I had a transition animation container with these views:
fromViewController's view (with a scale transform 0.8;0.8)
UIVisualEffectView with .Dark style blur applied to it
toViewController's view
Because of fromViewController's view size (which was smaller than the screen because of scale transform), the black UIWindow view was showing through. And according to my research this causes UIVisualEffectView to produce a glitch described in this question.
The solution is to add a black UIView with the frame of an UIWindow and add it to the bottom of the container's view hierarchy:
let blackView = UIView(frame: container.frame)
blackView.backgroundColor = UIColor.blackColor()
container.insertSubview(blackView, atIndex: 0)
And then this glitch seem to appear no longer.
I have this piece of animation in a switch statement that runs and does what it's supposed to. The animation is called successfully every twenty seconds inside a timer. However, the animation does not happen any time but the first time. In my code below you will see println statements that I used to try and highlight the problem. Each println statement prints but no animation happens. What is it that I'm missing?
func popAnimation(indexNumber: Int) {
let image = self.overlayImageView[indexNumber]
image.hidden = false
image.alpha = 0.0
UIView.animateWithDuration(0.75, delay: 0.0, usingSpringWithDamping: 0.5, initialSpringVelocity: 1.0, options: nil, animations: { () -> Void in
println("animation ran")
println(image.alpha)
image.image = UIImage(named: "lowCountOverlay")
image.alpha = 1.0
}, completion: { (Bool) -> Void in
UIView.animateWithDuration(0.75, delay: 0.0, usingSpringWithDamping: 0.5, initialSpringVelocity: 1.0, options: nil, animations: { () -> Void in
println("animation 2 ran")
println(image.alpha)
image.alpha = 0.0
}, completion: { (Bool) -> Void in
println("animation 3 ran")
image.hidden = true
})
})
}
UPDATE: If I remove image.hidden from the second completion block it works fine and animates repeatedly. Which is interesting because if it's not hidden it should be covering other interactive content in the view, but it's not. The other content is perfectly accessible in simulator. The UIImageView image is definitely the top layer in Storyboard.
I could be off, but it seems like you're running from an NSTimer which may or may not be in the main thread.
UI updates need to take place in the main thread, try wrapping the completion handler in a GCD dispatch directed to the main thread like the following and see if that helps? Maybe even force the whole thing to the main thread.
UIView.animateWithDuration(0.75, delay: 0.0, usingSpringWithDamping: 0.5, initialSpringVelocity: 1.0, options: nil, animations: { () -> Void in
// First animation
}, , completion: { (Bool) -> Void in
dispatch_async(dispatch_get_main_queue(),{
// second animation
})
})
I think you need to look for your bug elsewhere. You don't seem to be missing anything in the codeāas I'm able to run the animation repeatedly without any issues. I've copied your code as-is to a separate project and the animation works every time.
Try replacing:
let image = self.overlayImageView[indexNumber]
image.hidden = false
image.alpha = 0.0
with
let image = self.overlayImageView[indexNumber]
image.superview.bringSubviewToFront(image)
image.hidden = false
image.alpha = 0.0