可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
i want to fade between two UIButton images for the purpose of setting favorites in a UITableView.
Currently the transition is done without effect - it just changes the images directly on click/touch:
trans_img = [UIImage imageNamed:@"fav_on.png"];
NSArray *subviews = [owningCell subviews];
UIButton *favbutton = [subviews objectAtIndex:2];
favbutton.imageView.animationImages =
[NSArray arrayWithObjects:trans_img,
nil];
[favbutton.imageView startAnimating];
Everything I found was a transition between UIViews :(
It would be nice if the image fav_off gets smoothly changed into fav_on and the other way round like a fadein/fadeout.
回答1:
You could try to transition the alpha values like this to get the effect that you want:
trans_img = [UIImage imageNamed:@"fav_on.png"];
NSArray *subviews = [owningCell subviews];
UIButton *favbutton = [subviews objectAtIndex:2];
[UIView animateWithDuration:0.5 animations:^{
favbutton.alpha = 0.0f;
} completion:^(BOOL finished) {
favbutton.imageView.animationImages = [NSArray arrayWithObjects:trans_img,nil];
[favbutton.imageView startAnimating];
[UIView animateWithDuration:0.5 animations:^{
favbutton.alpha = 1.0f;
}];
}];
回答2:
It seems like what you're looking for is this. It animates the images on a UIButton without adding new images, creating an array or changing alpha values!
CABasicAnimation *crossFade = [CABasicAnimation animationWithKeyPath:@"contents"];
crossFade.duration = 0.7;
crossFade.fromValue = (id)oldImage.CGImage;
crossFade.toValue = (id)newImage.CGImage;
crossFade.removedOnCompletion = NO;
crossFade.fillMode = kCAFillModeForwards;
[button.imageView.layer addAnimation:crossFade forKey:@"animateContents"];
//Make sure to add Image normally after so when the animation
//is done it is set to the new Image
[button setImage:newImage forState:UIControlStateNormal];
回答3:
Here in swift:
import UIKit
extension UIButton {
func changeImageAnimated(image: UIImage?) {
guard let imageView = self.imageView, currentImage = imageView.image, newImage = image else {
return
}
let crossFade: CABasicAnimation = CABasicAnimation(keyPath: "contents")
crossFade.duration = 0.3
crossFade.fromValue = currentImage.CGImage
crossFade.toValue = newImage.CGImage
crossFade.removedOnCompletion = false
crossFade.fillMode = kCAFillModeForwards
imageView.layer.addAnimation(crossFade, forKey: "animateContents")
}
}
self.playAndPauseVideo.changeImageAnimated(UIImage(named: "pauseVideo"))
回答4:
Thank you to @jkanter for the great answer. I made mine into a Swift 3.0 extension that I thought might also be useful for anyone who stumbles upon this post.
extension UIButton {
func setImage(_ image: UIImage?, for state: UIControlState, animated: Bool) {
guard animated, let oldImage = imageView?.image, let newImage = image else {
// Revert to default functionality
setImage(image, for: state)
return
}
let crossFade = CABasicAnimation(keyPath:"contents")
crossFade.duration = 0.35
crossFade.fromValue = oldImage.cgImage
crossFade.toValue = newImage.cgImage
crossFade.isRemovedOnCompletion = false
imageView?.layer.add(crossFade, forKey: "animateContents")
setImage(image, for: state)
}
}
回答5:
This is a slight improvement to @Ponja's answer that generally works well. Unfortunately, the newImage doesn't "stick", so if you wanted to toggle between two images, the first fade would work smoothly, but going back to the original image would "snap" back with no fade. Fixed it by using a CATransaction:
import UIKit
extension UIButton {
func changeImageAnimated(image: UIImage?) {
guard let imageView = self.imageView, currentImage = imageView.image, newImage = image else {
return
}
CATransaction.begin()
CATransaction.setCompletionBlock {
self.setImage(newImage, forState: UIControlState.Normal)
}
let crossFade: CABasicAnimation = CABasicAnimation(keyPath: "contents")
crossFade.duration = 0.3
crossFade.fromValue = currentImage.CGImage
crossFade.toValue = newImage.CGImage
crossFade.removedOnCompletion = false
crossFade.fillMode = kCAFillModeForwards
imageView.layer.addAnimation(crossFade, forKey: "animateContents")
CATransaction.commit()
}
}
回答6:
@SuperDuperTango's answer with tintColor added:
extension UIButton {
func changeImageAnimated(image: UIImage?) {
guard let imageView = self.imageView, currentImage = imageView.image, newImage = image else {
return
}
CATransaction.begin()
CATransaction.setCompletionBlock {
self.setImage(newImage, forState: UIControlState.Normal)
}
let crossFade: CABasicAnimation = CABasicAnimation(keyPath: "contents")
crossFade.duration = 0.3
crossFade.fromValue = currentImage.CGImage
crossFade.toValue = newImage.CGImage
crossFade.removedOnCompletion = false
crossFade.fillMode = kCAFillModeForwards
imageView.layer.addAnimation(crossFade, forKey: "animateContents")
CATransaction.commit()
let crossFadeColor: CABasicAnimation = CABasicAnimation(keyPath: "contents")
crossFadeColor.duration = 0.3
crossFadeColor.fromValue = UIColor.blackColor()
crossFadeColor.toValue = UIColor(red: 232.0/255.0, green: 85.0/255.0, blue: 71.0/255.0, alpha: 1.0)
crossFadeColor.removedOnCompletion = false
crossFadeColor.fillMode = kCAFillModeForwards
imageView.layer.addAnimation(crossFadeColor, forKey: "animateContents")
CATransaction.commit()
}
}
回答7:
@SuperDuperTango's answer in Swift 4.2:
extension UIButton {
func changeImageAnimated(image: UIImage?) {
guard let imageView = self.imageView, let currentImage = imageView.image, let newImage = image else { return }
CATransaction.begin()
CATransaction.setCompletionBlock {
self.setImage(newImage, for: .normal)
}
let crossFade: CABasicAnimation = CABasicAnimation(keyPath: "contents")
crossFade.duration = 0.3
crossFade.fromValue = currentImage.cgImage
crossFade.toValue = newImage.cgImage
crossFade.isRemovedOnCompletion = false
crossFade.fillMode = CAMediaTimingFillMode.forwards
imageView.layer.add(crossFade, forKey: "animateContents")
CATransaction.commit()
}
}
回答8:
jkanter answer in Swift:
let crossFade = CABasicAnimation.init(keyPath: "contents")
crossFade.duration = 0.7
crossFade.fromValue = oldImage.cgImage
crossFade.toValue = newImage.cgImage
crossFade.isRemovedOnCompletion = false
crossFade.fillMode = kCAFillModeForwards
button.imageView?.layer.add(crossFade, forKey: "animateContents")
//Make sure to add Image normally after so when the animation
//is done it is set to the new Image
button.setImage(newImage, for: .normal)