Is it possible to fade between two view background images? - ios

I would like to know if it's possible for some button to cause a fade from one background image to another? The following code I have results in an abrupt change from the current image, background-image-1, to the new image, background-image-2.
func buttonPressed() {
if let image = UIImage(named: "background-image-2") {
UIView.animateWithDuration(2, delay: 0, options: .CurveEaseInOut, animations: {_ in
self.view.backgroundColor = UIColor(patternImage: image)
}, completion: nil)
}
Thanks
EDIT
Though the accepted answer does work, I was finding slight glitches when toggling quickly between images. A similar method using UIView.animateWithDuration solves this:
func buttonPressed() {
let oldImageView = UIImageView(image: UIImage(named: "background-image-1"))
oldImageView.frame = view.frame
let newImageView = UIImageView(image: UIImage(named:"background-image-2"))
newImageView.frame = view.frame
newImageView.alpha = 0
view.insertSubview(newImageView, aboveSubview: backgroundImageView)
view.insertSubview(oldImageView, aboveSubview: newImageView)
UIView.animateWithDuration(1.0, delay: 0.0, options: .CurveEaseInOut, animations: {
oldImageView.alpha = 0
newImageView.alpha = 1
}, completion: { completed in
self.backgroundImageView.image = newImage
newImageView.removeFromSuperview()
oldImageView.removeFromSuperview()
})
}

Say imageView is the UIImageView you are using for your animation. image1 is your original image. image2 is the new image. Try this:
let crossFade: CABasicAnimation = CABasicAnimation(keyPath: "contents")
crossFade.duration = 1
crossFade.fromValue = image1.CGImage
crossFade.toValue = image2.CGImage
self.imageView.image = image2
self.imageView.layer.addAnimation(crossFade, forKey: "animateContents")
Hope this helps.

The background color is supposed to be an animatable property, but a pattern color must not work.
Do you actually want a pattern color, or are you using that in order to insert an image as the background of the view without using the repeating pattern?

Related

How to use completion block for UIView.animate()?

I'm working on a project to learn animations and am having trouble using the completion block for the UIView.animate(withDuration:) func. MY animation is a shoebox that falls from the top of the screen, lands on a pedestal, then opens. Once the box opens I want a UIImageView to come out of the box, grow to full size of the screen and then segue to the next page, but the completion handler that the code for segueing is called before my animation completes and the UIImageView doesn't appear at all.
Here's my code:
override func viewDidAppear(_ animated: Bool) {
UIView.animate(withDuration: 1.5, delay: 0.0, options: .curveEaseInOut,
animations: {
//Opening the box
self.shoeBoxImage.shoeBox.animationImages = self.boxOpeningAnimation
self.shoeBoxImage.shoeBox.animationDuration = 1.5
self.shoeBoxImage.shoeBox.animationRepeatCount = 1
self.shoeBoxImage.shoeBox.contentMode = .scaleAspectFill
self.shoeBoxImage.shoeBox.startAnimating()
//set to the final image
self.shoeBoxImage.shoeBox.image = UIImage(named: "frame13")
},completion: {_ in
let nextPage = UIImageView()
nextPage.frame = CGRect(origin: self.shoeBoxImage.center, size: CGSize(width: 0.0, height: 0.0))
nextPage.image = UIImage(named: "FirstLoadBackgroundImg.jpeg")
nextPage.autoresizesSubviews = true
self.view.addSubview(nextPage)
self.view.bringSubviewToFront(nextPage)
UIView.animate(withDuration: 5.0, animations: {
nextPage.transform = CGAffineTransform(scaleX: 428, y: 926)
})
self.performSegue(withIdentifier: "FinishedLoading", sender: self)
})
}
This is my first time working with animations and programatically creating views so if someone could explain how to make the completion block wait for the animation to complete. In order to make the UIImageView appear and animate then once it's full screen, segue to the next page it would be very much appreciated.
The size is 0, 0. Transforming zero by any scale is still zero. I would advise you to not use transform at all, but rather just set the final frame to be what you want.
E.g.,
let startFrame = CGRect(x: view.bounds.midX, y: view.bounds.midY, width: 0, height: 0)
let endFrame = view.bounds
let imageView = UIImageView(image: ...)
imageView.contentMode = .scaleAspectFill
view.addSubview(imageView)
imageView.frame = startFrame
UIView.animate(withDuration: 3, delay: 0, options: .curveEaseInOut) {
imageView.frame = endFrame
} completion: { _ in
// do something here
}
That yields:
By the way, the performSegue probably should be inside a completion closure of the inner animate call.

How can I animate the frame and alpha of a UIButton's titleLabel simultaneously?

I would like animate the width and alpha of my UIButton's titleLabel. The alpha animation should start after 1 second.
I'm trying to do it with Core Animation:
let animSize = CAKeyframeAnimation(keyPath: "bounds")
animSize.values = [btn1.layer.frame,CGRect(origin: btn1.layer.frame.origin, size: CGSize(width: 300, height: btn1.layer.frame.height))]
animSize.duration = 3
animSize.isRemovedOnCompletion = false
animSize.fillMode = kCAFillModeForwards
btn1.layer.add(animSize, forKey: "bounds")
let animAlpha = CABasicAnimation(keyPath: "opacity")
animAlpha.fromValue = 0.0
animAlpha.toValue = 1.0
animAlpha.timeOffset = 1.0
animAlpha.isRemovedOnCompletion = false
animAlpha.fillMode = kCAFillModeForwards
btn1.titleLabel?.layer.add(animAlpha, forKey: "opacity")
When I run the animations separately, everything's working, but when I run them together, only the first animation ("bounds") is evaluating. I know I can do it with UIView.animate but in that case I will lose the "delay" option. I've also tried to put those animations in a CAAnimationGroup but nothing changed. I want to run those animations simultaneously therefore I can't use a completion block.
Thank you for any advice.
You won't lose the "delay" option with UIView.animate. You can use two animation blocks to achieve the same result:
UIView.animate(withDuration: 3) {
btn1.frame = CGRect(origin: btn1.frame.origin, size: CGSize(width: 300, height: btn1.frame.height))
}
UIView.animate(withDuration: 2, delay: 1, options: [], animations: {
btn1.alpha = 1
}, completion: nil)
You may also try it with autolayout by hooking the button's width constraint as IBOutlet
btn1.withCon.constant = 300
UIView.animate(withDuration: 3) {
self.view.layoutIfNeeded()
}
UIView.animate(withDuration: 2, delay: 1, options: [], animations: {
btn1.alpha = 1
}, completion: nil)

slide transition using UIView.transition() method

I have and table view and when the user clicks a button I want it to reload it's data and change numerous other things. Sadly, my users have not been satisfied with the available options for the transitions/animations, and wanted a sliding transition. I couldn't find anything online to do this, however. Here is my code:
UIView.transition(with: view, duration: 0.5,options: .transitionCurlUp,animations:
{ () -> Void in
self.schedule.reloadData()
self.formatter.dateFormat = "yyyy-MM-dd"
let dayOfWeek = self.week[self.getDayOfWeek(self.currentDate)!-1]
var forReturn = dayOfWeek.getCourseNumber()
if forReturn>3{
forReturn = forReturn + 1
self.refreshButton.isHidden = false
self.noClasses.isHidden = true
}else{
self.refreshButton.isHidden = true
self.noClasses.isHidden = false
}
self.changeWeek()
let startDate = self.formatter.string(from: self.startDateBase)
if self.currentDate == startDate{
self.other1.text = "Today's Schedule"
}else{
self.formatter.dateFormat = "yyyy-MM-dd"
self.currentDate = self.formatter.string(from: self.date)
self.other1.text = dayNames[self.getDayOfWeek(self.currentDate)!-1] + " Schedule"
}
}, completion: nil
);
Thank you.
You can go old school on this:
take a snapshot of the view;
add it to the view hierarchy;
apply offsetting transforms to the snapshot view and the view, itself;
animate the restoration of the main view's transform back to .identity; and
removing the snapshot view when done with the animation.
Thus:
let snapshotView = snapshot()
view.addSubview(snapshotView)
snapshotView.transform = CGAffineTransform(translationX: -view.bounds.size.width, y: 0)
view.transform = CGAffineTransform(translationX: view.frame.size.width, y: 0)
// do whatever updates to the views you want here
tableView.reloadData()
UIView.animate(withDuration: 0.5, animations: {
self.view.transform = .identity
}, completion: { _ in
snapshotView.removeFromSuperview()
})
For the snapshot, I used:
func snapshot() -> UIView {
UIGraphicsBeginImageContextWithOptions(view.bounds.size, true, 0)
view.drawHierarchy(in: view.bounds, afterScreenUpdates: false)
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
let imageView = UIImageView(image: image)
imageView.frame = view.bounds
return imageView
}
By the way, you can also use snapshotView(afterScreenupdates:), which is faster than drawHierarchy, but it doesn't work in 7+ simulator for some reason (see https://forums.developer.apple.com/thread/63438), so I'm not 100% comfortable with it. But if you wanted to do that, you'd do replace the shapshotView declaration with:
let snapshotView = view.snapshotView(afterScreenUpdates: false)!

rounded corners while extending subview beyond superview's bounds

I'm designing a specific animation. The image scales beyond bounds, shrinks to appropriate size and than falls down. You can see what I got so far is this video: https://vid.me/i1bx.
The effect I would like to achieve is that the first imageView (the grey one) has rounded corners. Setting the cornerRadius and clipsToBounds of first imageView to true does that, but disables the scaling effect of second imageView(firstScaleImageView) beyond first imageView's bounds.
Is it possible to have rounded corners and also be capable of expanding the imageView beyond superviews's bounds?
This is my code:
#IBAction func goButtonpressed(sender: UIButton) {
firstScaleImageView = UIImageView(frame: bounceFrame)
firstScaleImageView.image = UIImage(named: "1")
firstScaleImageView.transform = CGAffineTransformScale(firstScaleImageView.transform, 0, 0)
firstImageView.addSubview(firstScaleImageView)
UIView.animateWithDuration(0.4, delay: 0, usingSpringWithDamping: 0.7, initialSpringVelocity: 10, options: UIViewAnimationOptions.CurveLinear, animations: {
self.firstScaleImageView.transform = CGAffineTransformIdentity
}, completion: {_ in
self.firstImageView.clipsToBounds = true
})
}
#IBAction func backButtonPressed(sender: UIButton) {
UIView.animateWithDuration(0.3, animations: {
self.firstScaleImageView.transform = CGAffineTransformTranslate(self.firstScaleImageView.transform, 0, self.firstImageView.frame.height)
}, completion: {_ in
self.firstImageView.clipsToBounds = false
self.firstScaleImageView.removeFromSuperview()
})
}
Try to set clipsToBounds to false (I don't think you need it for the corner radius).

UIImageView animation in Swift with time delay

I'm stuck with this issue where I want a UIImage to glow within two seconds and then to go back to it's normal state.
Given this issue here's what I have at the moment:
I have the image views referenced, from 0 to 9.
#IBOutlet weak var imageOne: UIImageView!
#IBOutlet weak var imageTwo: UIImageView!
etc.
Then I added them to SubViews in the viewDiDLoad() function:
override func viewDidLoad() {
super.viewDidLoad()
self.view.addSubview(imageOne)
self.view.addSubview(imageTwo)
etc.
}
Here is my implementation of the colorisation function:
func colorize(imageView: UIImageView, color: CGColorRef) {
imageView.layer.shadowColor = color
imageView.layer.shadowRadius = 7.0
imageView.layer.shadowOpacity = 0.9
imageView.layer.shadowOffset = CGSizeZero
imageView.layer.masksToBounds = false
}
Now, I'm trying to animate two image views which are called through this. a and b are just randoms acquired from the previous view. In this example, they will be 1 and 3.:
UIView.animateWithDuration(2.0, delay: 0, options: nil, animations: { () -> Void in
self.colorize(labelArray[a.toInt()!], color:self.green)
}, completion: nil)
UIView.animateWithDuration(2.0, delay: 2.0, options: nil, animations: { () -> Void in
self.colorize(labelArray[b.toInt()!], color:self.cyan)
}, completion: nil)
Now, this is what the view looks like beforehand -
And this is what it's like after, but both animations occur at the same time. There is no transition. It just automatically applies the glow -
Thanks for your help!
Since you're trying to animation your UIImageView layers, try using Core Animation instead of UIView animation blocks since Core Animation must be used to animate the shadow layer of a view. If you simply want to fade the shadow in, try this to animate the shadow opacity:
override func performGlowAnimations {
self.colorize(labelArray[a.toInt()!], color:self.green)
// Delay the second call by 2 seconds
let delay = 2.0 * Double(NSEC_PER_SEC)
var time = dispatch_time(DISPATCH_TIME_NOW, Int64(delay))
dispatch_after(time, dispatch_get_main_queue(), {
self.colorize(labelArray[b.toInt()!], color:self.cyan)
})
}
func colorize(imageView: UIImageView, color: CGColorRef) {
// Set the image's shadowColor, radius, offset, and
// set masks to bounds to false
imageView.layer.shadowColor = color
imageView.layer.shadowRadius = 7.0
imageView.layer.shadowOffset = CGSizeZero
imageView.layer.masksToBounds = false
// Animate the shadow opacity to "fade it in"
let shadowAnim = CABasicAnimation()
shadowAnim.keyPath = "shadowOpacity"
shadowAnim.fromValue = NSNumber(float: 0.0)
shadowAnim.toValue = NSNumber(float: 0.9)
shadowAnim.duration = 2.0
imageView.layer.addAnimation(shadowAnim, forKey: "shadowOpacity")
imageView.layer.shadowOpacity = 0.9
}

Resources