Related
I'm trying to show a custom view when I receive a notification from parse.
This view is showed with an animation and its hidden with another animation. This view also has a uitapgesturerecognizer that needs to be fired when the user taps the view.
My problem is that when the second animation gets fired the custom view's uitapgesture doesn't work :\
Any ideas? I paste the code.
Thanks!
func doManageObjectNotification(notification: COPushNotification){
mainView = UIApplication.sharedApplication().keyWindow!
customView = CustomNotificationView()
let height = customView.calculateViewHeight()
customView.frame = CGRectMake(0, -height, mainView.frame.width, height)
customView.targetMethod = notificationWasTapped
customView.setContent(notification)
customView.alpha = 0
mainView.addSubview(customView)
UIView.animateWithDuration(0.75, delay: 0, options: [UIViewAnimationOptions.CurveEaseInOut, UIViewAnimationOptions.AllowUserInteraction], animations: { () -> Void in
// Show the view
self.customView.frame.origin.y = 0
self.customView.alpha = 1
}) { (Bool) -> Void in
UIView.animateWithDuration(0.75, delay: 5, options: [UIViewAnimationOptions.CurveEaseInOut, UIViewAnimationOptions.AllowUserInteraction], animations: {
// Hide the view
self.customView.alpha = 0
}, completion: { finished in
self.customView.removeFromSuperview()
self.customView = nil
})
}
}
Im agree with Lion answer, but I want also focus your attention about customView.frame.origin.y = 0 during animation: if you use autolayout and you try to change frame dimensions or positions instead the correct constraints involved, you can disable you constraints effect causing warnings and unexpected view dimensions and movements. When you have this issue many times the UITapGestureRecognizer stop to responding.
The best way to do it is to create IBOutlets constraints and working with it (for example):
#IBOutlet weak var customViewTopConstraint: NSLayoutConstraint
...
UIView.animateWithDuration(0.75, delay: 0, options: [UIViewAnimationOptions.CurveEaseInOut, UIViewAnimationOptions.AllowUserInteraction], animations: { () -> Void in
// Show the view
self.customViewTopConstraint.constant = 0
For handling tap during animation you should implement touchesBegan method. by this you can detect touch and then check that touch is from your view,if yes then you can perform your desired task.
Hope this will help :)
i have found this code which is responsible for animating a UIView but unfortunately the code does not work and i can not figure the reason (maybe an older version of swift)
this is the code :
(this is helper function according to the creator)
func moveView(#view:UIView, toPoint destination:CGPoint, completion☹()->())?) {
//Always animate on main thread
dispatch_async(dispatch_get_main_queue(), { () Void in
//Use UIView animation API
UIView.animateWithDuration(1.0, delay: 0.0, usingSpringWithDamping:
0.6, initialSpringVelocity: 0.3, options:
UIViewAnimationOptions.AllowAnimatedContent, animations: { () ->
Void in
//do actual move
view.center = destination
}, completion: { (complete) -> Void in
//when animation completes, activate block if not nil
if complete {
if let c = completion {
c()
}
}
})
})
}
and this is the animation
//Create your face object (Just a UIImageView with a face as the image
var face = Face();
face.frame = CGRect(x: 0, y: 0, width: 50, height: 50)
//find our trajectory points
var center = CGPointMake(self.view.frame.size.width/2, self.view.frame.size.height/2);
var left = CGPointMake(center.x *-0.3, center.y)
var right = CGPointMake(center.x *2.2, center.y)
//place our view off screen
face.center = right
self.view.addSubview(face)
//move to center
moveView(view: face, toPoint: center) { () -> () in
//Do your Pop
face.pop()
// Move to left
moveView(view: face, toPoint: left, completion: { () -> () in
}
}
and i quote from the creator of the code
General Steps: Create a new face on the right edge of the screen. Make
the face visible. Move the face to the middle of the screen. Pop the
face Start the process with the next face. Move the first face to the
left as soon as the new face gets to the middle.
Actual slide animation Once again, we will do the following here: Move
view off screen on the right Move to center Pop Move to left
To get the repeating effect, just call this method on a timer
and a summary :
UIView’s animation API is very powerful. Both the pop and movement
animations use depend on this API. If you’re stuck with trying to
create an animation, UIView animation block is usually a good place to
start.
NOTE : im a beginner in IOS development if anyone can please explain the code for me
Indeed this moveView method had a few issues, one being it was written for Swift 1 (but there were also some typos, faulty characters and useless operations).
Here's the fixed version:
func moveView(view view:UIView, toPoint destination: CGPoint, afterAnim: ()->()) {
//Always animate on main thread
dispatch_async(dispatch_get_main_queue(), { () -> Void in
//Use UIView animation API
UIView.animateWithDuration(1.0, delay: 0.0, usingSpringWithDamping:
0.6, initialSpringVelocity: 0.3, options:
UIViewAnimationOptions.AllowAnimatedContent, animations: { () -> Void in
//do actual move
view.center = destination
}, completion: { (complete) -> Void in
//if and when animation completes, callback
if complete {
afterAnim()
}
})
})
}
You can use it like this now:
moveView(view: face, toPoint: center) {
//Do your Pop
face.pop()
// Move to left
moveView(view: face, toPoint: left) {
// Do stuff when the move is finished
}
}
Observe the differences between your version and mine to understand what was obsolete/wrong and how I fixed it. I'll help if you're stuck.
In Camera mode (AVCaptureVideoPreviewLayer) I manage to capture a photo successfully. I would like to indicate this fact to the user- meaning to show him a sudden black flash and a click sound- similar to what he would experience when taking a photo himself.
How do I do that? Is there some built in functionality that does that?
Thanks
There is no built-in functionality to do this, but it's pretty simple to do on your own by adding a black UIView with alpha set to zero in your camera view hierarchy, then playing system sound and animating the "flash" view's alpha when the photo is captured.
In viewDidLoad, loadView, or wherever you assemble your view hierarchy
// Assuming cameraView contains your previewLayer...
flashView = UIView(frame: <set your frame>)
flashView.alpha = 0
flashView.backgroundColor = UIColor.blackColor()
cameraView.addSubview(flashView)
Then, in your capture completion block
// Animate the "flash"
UIView.animateWithDuration(0.1, delay: 0, options: .Autoreverse, animations: { () -> Void in
flashView.alpha = 1
}, completion: nil)
// Play the camera shutter system sound
AudioServicesPlayAlertSound(1108)
For more info on the system sounds, see this question: Playing system sound without importing your own.
For Swift 4:
#IBOutlet weak var lightView: UIView! //First set lightView hidden in the storyboard
//MARK: - Take Screenshot Animation
func flashAnimation(image: UIImage, rect: CGRect) {
self.view.bringSubview(toFront: lightView)
lightView.alpha = 0
lightView.isHidden = false
UIView.animate(withDuration: 0.1, delay: 0.0, options: [.curveEaseOut], animations: {() -> Void in
self.lightView.alpha = 1.0
}, completion: {(finished: Bool) -> Void in
self.hideFlashView()
})
}
func hideFlashView() {
UIView.animate(withDuration: 0.1, delay: 0.0, animations: {() -> Void in
self.lightView.alpha = 0.0
})
}
I want to fade a UIVisualEffectsView with a UIBlurEffect in and out:
var blurEffectView = UIVisualEffectView()
blurEffectView = UIVisualEffectView(effect: UIBlurEffect(style: .dark))
I use a normal animation within a function called by a UIButton to fade it in, same for fading out but .alpha = 0 & hidden = true:
blurEffectView.hidden = false
UIView.animate(withDuration: 1, delay: 0, options: .curveEaseOut) {
self.blurEffectView.alpha = 1
}
Now, fading in both directions does work but it gives me an error when fading out:
<UIVisualEffectView 0x7fdf5bcb6e80> is being asked to animate its opacity. This will cause the effect to appear broken until opacity returns to 1.
Question
How do I successfully fade the UIVisualEffectView in and out without breaking it and having a fading transition?
Note
I tried to put the UIVisualEffectView into a UIView and fade that one, no success
I think this is new in iOS9, but you can now set the effect of a UIVisualEffectView inside an animation block:
let overlay = UIVisualEffectView()
// Put it somewhere, give it a frame...
UIView.animate(withDuration: 0.5) {
overlay.effect = UIBlurEffect(style: .light)
}
Set it to nil to remove.
VERY IMPORTANT - When testing this on the simulator, make sure to set your simulator's Graphics Quality Override to High Quality in order for this to work.
The Apple documentation (currently) states...
When using the UIVisualEffectView class, avoid alpha values that are less than 1.
and
Setting the alpha to less than 1 on the visual effect view or any of its superviews causes many effects to look incorrect or not show up at all.
I believe some important context is missing here...
I'd suggest that the intent is to avoid alpha values that are less than 1 for a persistent view. In my humble opinion this does not apply to the animation of a view.
My point - I'd suggest that alpha values less than 1 are acceptable for animations.
The terminal message states:
UIVisualEffectView is being asked to animate its opacity. This will cause the effect to appear broken until opacity returns to 1.
Reading this carefully, the effect will appear to be broken. My points on this being:
the apparent break only really matters for a view that is persistent - not changing;
a persistent / unchanging UIVisualEffect view with an alpha value less than 1 will not present as intended / designed by Apple; and
the message in the terminal is not an error, just a warning.
To extend #jrturton's answer above that helped me solve my problem, I'd add...
To fade out the UIVisualEffect use the following (Objective-C) code:
UIView.animateWithDuration(1.0, animations: {
// EITHER...
self.blurEffectView.effect = UIBlurEffect(nil)
// OR...
self.blurEffectView.alpha = 0
}, completion: { (finished: Bool) -> Void in
self.blurEffectView.removeFromSuperview()
} )
I successfully use both methods: setting the effect property to nil and setting the alpha property to 0.
Note that setting the effect to nil creates a "nice flash" (for want of a better description) at the end of the animation, while setting the alpha to 0 creates a smooth transition.
(Let me know any syntax errors... I write in obj-c.)
Here is the solution that I ended up which works on both iOS10 and earlier versions using Swift 3
extension UIVisualEffectView {
func fadeInEffect(_ style:UIBlurEffectStyle = .light, withDuration duration: TimeInterval = 1.0) {
if #available(iOS 10.0, *) {
let animator = UIViewPropertyAnimator(duration: duration, curve: .easeIn) {
self.effect = UIBlurEffect(style: style)
}
animator.startAnimation()
}else {
// Fallback on earlier versions
UIView.animate(withDuration: duration) {
self.effect = UIBlurEffect(style: style)
}
}
}
func fadeOutEffect(withDuration duration: TimeInterval = 1.0) {
if #available(iOS 10.0, *) {
let animator = UIViewPropertyAnimator(duration: duration, curve: .linear) {
self.effect = nil
}
animator.startAnimation()
animator.fractionComplete = 1
}else {
// Fallback on earlier versions
UIView.animate(withDuration: duration) {
self.effect = nil
}
}
}
}
You can also check this gist to find an example usage.
Just a workaround - put UIVisualEffectView into a container view and change alpha property for that container. That approach works perfectly for me on iOS 9. Seems it no longer works in iOS 10.
You can change the alpha of the visual effects view without any problems, other than the warning in the console. The view may appear as simply partially transparent, rather than blurred. But this usually isn't a problem if you're just changing the alpha during animation.
Your app isn't going to crash or get rejected for this. Test it on a real device (or eight). If you're happy with how it looks and performs, it's fine. Apple's just warning you that it may not look or perform as well as a visual effects view with an alpha value of 1.
You can take a snapshot of a static underlying view, and fade it in and out without touching the opacity of the blur view. Assuming an ivar of blurView:
func addBlur() {
guard let blurEffectView = blurEffectView else { return }
//snapShot = UIScreen.mainScreen().snapshotViewAfterScreenUpdates(false)
let snapShot = self.view.snapshotViewAfterScreenUpdates(false)
view.addSubview(blurEffectView)
view.addSubview(snapShot)
UIView.animateWithDuration(0.25, animations: {
snapShot.alpha = 0.0
}, completion: { (finished: Bool) -> Void in
snapShot.removeFromSuperview()
} )
}
func removeBlur() {
guard let blurEffectView = blurEffectView else { return }
let snapShot = self.view.snapshotViewAfterScreenUpdates(false)
snapShot.alpha = 0.0
view.addSubview(snapShot)
UIView.animateWithDuration(0.25, animations: {
snapShot.alpha = 1.0
}, completion: { (finished: Bool) -> Void in
blurEffectView.removeFromSuperview()
snapShot.removeFromSuperview()
} )
}
If you want to fade in you UIVisualEffectView - for ios10 use UIViewPropertyAnimator
UIVisualEffectView *blurEffectView = [[UIVisualEffectView alloc] initWithEffect:nil];
blurEffectView.frame = self.view.frame;
blurEffectView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
UIView *blackUIView = [[UIView alloc]initWithFrame:self.view.frame];
[bacgroundImageView addSubview:blackUIView];
[blackUIView addSubview:blurEffectView];
UIViewPropertyAnimator *animator = [[UIViewPropertyAnimator alloc] initWithDuration:4.f curve:UIViewAnimationCurveLinear animations:^{
[blurEffectView setEffect:[UIBlurEffect effectWithStyle:UIBlurEffectStyleDark]];
}];
then you can set percent
[animator setFractionComplete:percent];
for ios9 you can use alpha component
The alpha of the UIVisualEffectView always has to be 1. I think you can achieve the effect by setting the alpha of the background color.
Source : https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIVisualEffectView/index.html
I make an uiview which alpha is 0 and add blurview as subview of that. So i can hide/show or rounding corners it with animation.
I ended up with the following solution, using separate animations for the UIVisualEffectView and the contents. I used the viewWithTag() method to get a reference to the UIView inside the UIVisualEffectView.
let blurEffectView = UIVisualEffectView()
// Fade in
UIView.animateWithDuration(1) { self.blurEffectView.effect = UIBlurEffect(style: .Light) }
UIView.animateWithDuration(1) { self.blurEffectView.viewWithTag(1)?.alpha = 1 }
// Fade out
UIView.animateWithDuration(1) { self.blurEffectView.effect = nil }
UIView.animateWithDuration(1) { self.blurEffectView.viewWithTag(1)?.alpha = 0 }
I would prefer the single animation changing the alpha, but this avoids the error and seems to work just as well.
I just had this problem and the way I got around it was to house the UIVisualEffectsView in a UIView, and animate that UIView's alpha.
This worked well, except that it as soon as the alpha changed below 1.0 it turned to a solid white and looked very jarring. In order to get around this, you must set the UIView's layer property containerView.layer.allowsGroupOpacity = false and this will prevent it from flashing white.
Now you can animate in/fade out the UIView containing the visual effects view and any other subviews using it's alpha property and not have to worry about any graphical glitches or it logging a warning message.
_visualEffectView.contentView.alpha = 0;
To change the alpha of UIVisualEffectView, you should change the contentView of _visualEffectView.If you change alpha of _visualEffectView, you will get this
<UIVisualEffectView 0x7ff7bb54b490> is being asked to animate its opacity. This will cause the effect to appear broken until opacity returns to 1.
Usually, I only want to animate a blur when I'm presenting a view controller over the screen and want to blur the presenting view controller. Here's an extension that adds blur() and unblur() to a view controller in order to facilitate that:
extension UIViewController {
func blur() {
// Blur out the current view
let blurView = UIVisualEffectView(frame: self.view.frame)
self.view.addSubview(blurView)
UIView.animate(withDuration:0.25) {
blurView.effect = UIBlurEffect(style: .light)
}
}
func unblur() {
for childView in view.subviews {
guard let effectView = childView as? UIVisualEffectView else { continue }
UIView.animate(withDuration: 0.25, animations: {
effectView.effect = nil
}) {
didFinish in
effectView.removeFromSuperview()
}
}
}
}
You can of course make this more robust by letting the user choose the effect style, modify the duration, call something when the animation is completed, tag the added visual effect view in blur() to ensure it's the only one removed when you unblur(), etc., but I haven't found the need to do these things so far, since this tends to be a "fire and forget" type of operation.
based on #cc's answer i modified his extension to blur a view
extension UIView {
func blur() {
// Blur out the current view
let blurView = UIVisualEffectView(frame: self.bounds)
self.addSubview(blurView)
UIView.animate(withDuration:0.25) {
blurView.effect = UIBlurEffect(style: .dark)
}
}
func unblur() {
for childView in subviews {
guard let effectView = childView as? UIVisualEffectView else { continue }
UIView.animate(withDuration: 2.5, animations: {
effectView.effect = nil
}) {
didFinish in
effectView.removeFromSuperview()
}
}
}
}
Improving #Tel4tel and #cc response, here is an extension with parameters and a brief explanation.
extension UIView {
// Perform a blur animation in the whole view
// Effect tone can be .light, .dark, .regular...
func blur(duration inSeconds: Double, effect tone: UIBlurEffectStyle) {
let blurView = UIVisualEffectView(frame: self.bounds)
self.addSubview(blurView)
UIView.animate(withDuration: inSeconds) {
blurView.effect = UIBlurEffect(style: tone)
}
}
// Perform an unblur animation in the whole view
func unblur(duration inSeconds: Double) {
for childView in subviews {
guard let effectView = childView as? UIVisualEffectView else { continue
}
UIView.animate(withDuration: inSeconds, animations: {
effectView.effect = nil
}){
didFinish in effectView.removeFromSuperview()
}
}
}
}
Then you can use it like:
view.blur(duration: 5.0, effect: .light)
or
view.unblur(duration: 3.0)
Remember to NOT use it in viewDidLoad() as it will override the animation.
Also, when running on a Simulator, turn the graphics to the Higher level to be able to see the animation (Debug > Graphics Quality Override > High Quality).
I'm spending my turkey day trying to construct a flip animation class that I have been struggling with for weeks.
The goal is this:
Flip two images repeatedly, quickly at first, then slow down and stop, landing on one of the two images that was chosen before the animation began.
Right now both images are in a container view, in a storyboard. Ive tried using transitionFromView transitionFlipFromTop and I think that could work, but at the time I was unable to get it to repeat.
I am in the process of reading the View Programming Guide, but its tough to connect the dots between it, and swift. Being new to programming in general does not help.
Here is where I'm at: I'm now using a CATransform to scale an image from zero height, to full height. Im thinking that if I can somehow chain two animations, the first one showing the first image scaling up, then back down to zero, then animate the second image doing the same thing, that should give me the first part. Then if I could somehow get those two animations to repeat, quickly at first, then slow down and stop.
It seems I need to know how to nest multiple animations, and then be able to apply an animation curve to the nested animation.
I planned on solving the part about landing on a particular image by having two of these nested animations, one of them would have an odd number of flips, the other an even number of flips. Depending on the desired final image, the appropriate animation gets called.
Right now, my current code is able to repeatedly scale an image from zero to full, a set number of times:
import UIKit
import QuartzCore
let FlipAnimatorStartTransform:CATransform3D = {
let rotationDegrees: CGFloat = -15.0
let rotationRadians: CGFloat = rotationDegrees * (CGFloat(M_PI)/180.0)
let offset = CGPointMake(-20, -20)
var startTransform = CATransform3DIdentity
startTransform = CATransform3DScale(CATransform3DMakeRotation(0, 0, 0, 0),
1, 0, 1);
return startTransform
}()
class FlipAnimator {
class func animate(view: UIView) {
let viewOne = view
let viewTwo = view
viewOne.layer.transform = FlipAnimatorStartTransform
viewTwo.layer.transform = FlipAnimatorStartTransform
UIView.animateWithDuration(0.5, delay: 0, options: .Repeat, {
UIView.setAnimationRepeatCount(7)
viewOne.layer.transform = CATransform3DIdentity
},nil)
}
}
Im going to keep chipping away at it. Any help, or ideas, or hints about a better way to go about it would be amazing.
Thanks.
I'm using this function to rotate image horizontally and to change the image during the transition.
private func flipImageView(imageView: UIImageView, toImage: UIImage, duration: NSTimeInterval, delay: NSTimeInterval = 0)
{
let t = duration / 2
UIView.animateWithDuration(t, delay: delay, options: .CurveEaseIn, animations: { () -> Void in
// Rotate view by 90 degrees
let p = CATransform3DMakeRotation(CGFloat(GLKMathDegreesToRadians(90)), 0.0, 1.0, 0.0)
imageView.layer.transform = p
}, completion: { (Bool) -> Void in
// New image
imageView.image = toImage
// Rotate view to initial position
// We have to start from 270 degrees otherwise the image will be flipped (mirrored) around Y axis
let p = CATransform3DMakeRotation(CGFloat(GLKMathDegreesToRadians(270)), 0.0, 1.0, 0.0)
imageView.layer.transform = p
UIView.animateWithDuration(t, delay: 0, options: .CurveEaseOut, animations: { () -> Void in
// Back to initial position
let p = CATransform3DMakeRotation(CGFloat(GLKMathDegreesToRadians(0)), 0.0, 1.0, 0.0)
imageView.layer.transform = p
}, completion: { (Bool) -> Void in
})
})
}
Don't forget to import GLKit.
This flip animation swift code appears little better:
let transition = CATransition()
transition.startProgress = 0;
transition.endProgress = 1.0;
transition.type = "flip";
transition.subtype = "fromRight";
transition.duration = 0.9;
transition.repeatCount = 2;
self.cardView.layer.addAnimation(transition, forKey: " ")
Other option is
#IBOutlet weak var cardView: UIView!
var back: UIImageView!
var front: UIImageView!
self.front = UIImageView(image: UIImage(named: "heads.png"))
self.back = UIImageView(image: UIImage(named: "tails.png"))
self.cardView.addSubview(self.back)
UIView.transitionFromView(self.back, toView: self.front, duration: 1, options: UIViewAnimationOptions.TransitionFlipFromRight , completion: nil)
Please check link
It seems I need to know how to nest multiple animations, and then be able to apply an animation curve to the nested animation.
I don't think so. I don't think you want/need to nest anything. You are not repeating an animation in this story; you are doing different animations each time (because the durations are to differ). This is a sequence of animations. So I think what you need to know is how to do either a keyframe animation or a grouped animation. That way you can predefine a series of animations, each one lasting a certain predefined duration, each one starting after the preceding durations are over.
And for that, I think you'll be happiest at the Core Animation level. (You can't make a grouped animation at the UIView animation level anyway.)