Je crée actuellement une application qui utilise un conteneur View Controller personnalisé. Plusieurs vues sont affichées à l'écran en même temps et lorsqu'une est tapée, le contrôleur de vue sélectionné s'anime en plein écran. Ce faisant, les sous-vues des contrôleurs de vue sélectionnés sont également mises à l'échelle (cadre, taille de police, etc.). Cependant, la propriété de police d'UILabel n'est pas animable, ce qui entraîne des problèmes. J'ai essayé plusieurs solutions, mais tout est nul.
Les solutions que j'ai essayées sont:
L'une a été la meilleure solution jusqu'à présent, mais je n'en suis pas satisfaite.
Je recherche d'autres suggestions si quelqu'un en a un ou un substitut UILabel qui s'anime facilement en utilisant [UIView animate ..].
Voici un bon exemple similaire à ce que j'aimerais que mon UILabel fasse: http://www.cocoawithlove.com/2010/09/zoomingviewcontroller-to-animate-uiview.html
EDIT: Ce code fonctionne
// Load View
self.label = [[UILabel alloc] init];
self.label.text = @"TEXT";
self.label.font = [UIFont boldSystemFontOfSize:20.0];
self.label.backgroundColor = [UIColor clearColor];
[self.label sizeToFit];
[self.view addSubview:self.label];
// Animation
self.label.font = [UIFont boldSystemFontOfSize:80.0];
self.label.transform = CGAffineTransformScale(self.label.transform, .25, .25);
[self.label sizeToFit];
[UIView animateWithDuration:1.0 animations:^{
self.label.transform = CGAffineTransformScale(self.label.transform, 4.0, 4.0);
self.label.center = self.view.center;
} completion:^(BOOL finished) {
self.label.font = [UIFont boldSystemFontOfSize:80.0];
self.label.transform = CGAffineTransformScale(self.label.transform, 1.0, 1.0);
[self.label sizeToFit];
}];
Vous pouvez changer la taille et la police de votre UILabel
avec une animation comme ci-dessous .. ici je viens de mettre l'exemple de comment changer la police de UILabel
avec transformer Animation ..
yourLabel.font = [UIFont boldSystemFontOfSize:35]; // set font size which you want instead of 35
yourLabel.transform = CGAffineTransformScale(yourLabel.transform, 0.35, 0.35);
[UIView animateWithDuration:1.0 animations:^{
yourLabel.transform = CGAffineTransformScale(yourLabel.transform, 5, 5);
}];
J'espère que cela vous sera utile ..
Swift 3.0, 4.
UIView.animate(withDuration: 0.5) {
label.transform = CGAffineTransform(scaleX: 1.1, y: 1.1) //Scale label area
}
J'ai créé l'extension UILabel
dans Swift.
import UIKit
extension UILabel {
func animate(font: UIFont, duration: TimeInterval) {
// let oldFrame = frame
let labelScale = self.font.pointSize / font.pointSize
self.font = font
let oldTransform = transform
transform = transform.scaledBy(x: labelScale, y: labelScale)
// let newOrigin = frame.Origin
// frame.Origin = oldFrame.Origin // only for left aligned text
// frame.Origin = CGPoint(x: oldFrame.Origin.x + oldFrame.width - frame.width, y: oldFrame.Origin.y) // only for right aligned text
setNeedsUpdateConstraints()
UIView.animate(withDuration: duration) {
//L self.frame.Origin = newOrigin
self.transform = oldTransform
self.layoutIfNeeded()
}
}
}
Décommentez les lignes si le texte de l'étiquette est aligné à gauche ou à droite.
Vous pouvez également utiliser CATextLayer qui a fontSize comme propriété animable.
let startFontSize: CGFloat = 20
let endFontSize: CGFloat = 80
let textLayer = CATextLayer()
textLayer.string = "yourText"
textLayer.font = yourLabel.font.fontName as CFTypeRef?
textLayer.fontSize = startFontSize
textLayer.foregroundColor = UIColor.black.cgColor
textLayer.contentsScale = UIScreen.main.scale //for some reason CATextLayer by default only works for 1x screen resolution and needs this line to work properly on 2x, 3x, etc. ...
textLayer.frame = parentView.bounds
parentView.layer.addSublayer(textLayer)
//animation:
let duration: TimeInterval = 1
textLayer.fontSize = endFontSize //because upon completion of the animation CABasicAnimation resets the animated CALayer to its original state (as opposed to changing its properties to the end state of the animation), setting fontSize to endFontSize right BEFORE the animation starts ensures the fontSize doesn't jump back right after the animation.
let fontSizeAnimation = CABasicAnimation(keyPath: "fontSize")
fontSizeAnimation.fromValue = startFontSize
fontSizeAnimation.toValue = endFontSize
fontSizeAnimation.duration = duration
fontSizeAnimation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)
textLayer.add(fontSizeAnimation, forKey: nil)
Je l'ai utilisé dans mon projet: https://github.com/yinanq/AngelListJobs
Cette animation maintient la police en haut à gauche alignée (contrairement à CGAffineTransformScale qui met à l'échelle l'étiquette depuis le centre), pro ou con selon vos besoins. Un inconvénient de CATextLayer est que les CALayers ne fonctionnent pas avec l'animation de contraintes de mise en page automatique (dont j'ai eu besoin et que j'ai résolue en créant une UIView contenant uniquement le CATextLayer et en animant ses contraintes).
J'ai créé une extension pour UILabel
pour animer le changement de taille de police
extension UILabel {
func animate(fontSize: CGFloat, duration: TimeInterval) {
let startTransform = transform
let oldFrame = frame
var newFrame = oldFrame
let scaleRatio = fontSize / font.pointSize
newFrame.size.width *= scaleRatio
newFrame.size.height *= scaleRatio
newFrame.Origin.x = oldFrame.Origin.x - (newFrame.size.width - oldFrame.size.width) * 0.5
newFrame.Origin.y = oldFrame.Origin.y - (newFrame.size.height - oldFrame.size.height) * 0.5
frame = newFrame
font = font.withSize(fontSize)
transform = CGAffineTransform.init(scaleX: 1 / scaleRatio, y: 1 / scaleRatio);
layoutIfNeeded()
UIView.animate(withDuration: duration, animations: {
self.transform = startTransform
newFrame = self.frame
}) { (Bool) in
self.frame = newFrame
}
}
Si vous souhaitez ajuster la direction de l'animation, utilisez la méthode ci-dessous et mettez un point d'ancrage approprié.
rapide
struct LabelAnimateAnchorPoint {
// You can add more suitable archon point for your needs
static let leadingCenterY = CGPoint.init(x: 0, y: 0.5)
static let trailingCenterY = CGPoint.init(x: 1, y: 0.5)
static let centerXCenterY = CGPoint.init(x: 0.5, y: 0.5)
static let leadingTop = CGPoint.init(x: 0, y: 0)
}
extension UILabel {
func animate(fontSize: CGFloat, duration: TimeInterval, animateAnchorPoint: CGPoint) {
let startTransform = transform
let oldFrame = frame
var newFrame = oldFrame
let archorPoint = layer.anchorPoint
let scaleRatio = fontSize / font.pointSize
layer.anchorPoint = animateAnchorPoint
newFrame.size.width *= scaleRatio
newFrame.size.height *= scaleRatio
newFrame.Origin.x = oldFrame.Origin.x - (newFrame.size.width - oldFrame.size.width) * animateAnchorPoint.x
newFrame.Origin.y = oldFrame.Origin.y - (newFrame.size.height - oldFrame.size.height) * animateAnchorPoint.y
frame = newFrame
font = font.withSize(fontSize)
transform = CGAffineTransform.init(scaleX: 1 / scaleRatio, y: 1 / scaleRatio);
layoutIfNeeded()
UIView.animate(withDuration: duration, animations: {
self.transform = startTransform
newFrame = self.frame
}) { (Bool) in
self.layer.anchorPoint = archorPoint
self.frame = newFrame
}
}
}
OBJECTIF-C
// You can add more suitable archon point for your needs
#define kLeadingCenterYAnchorPoint CGPointMake(0.f, .5f)
#define kTrailingCenterYAnchorPoint CGPointMake(1.f, .5f)
#define kCenterXCenterYAnchorPoint CGPointMake(.5f, .5f)
#define kLeadingTopAnchorPoint CGPointMake(0.f, 0.f)
@implementation UILabel (FontSizeAnimating)
- (void)animateWithFontSize:(CGFloat)fontSize duration:(NSTimeInterval)duration animateAnchorPoint:(CGPoint)animateAnchorPoint {
CGAffineTransform startTransform = self.transform;
CGRect oldFrame = self.frame;
__block CGRect newFrame = oldFrame;
CGPoint archorPoint = self.layer.anchorPoint;
CGFloat scaleRatio = fontSize / self.font.pointSize;
self.layer.anchorPoint = animateAnchorPoint;
newFrame.size.width *= scaleRatio;
newFrame.size.height *= scaleRatio;
newFrame.Origin.x = oldFrame.Origin.x - (newFrame.size.width - oldFrame.size.width) * animateAnchorPoint.x;
newFrame.Origin.y = oldFrame.Origin.y - (newFrame.size.height - oldFrame.size.height) * animateAnchorPoint.y;
self.frame = newFrame;
self.font = [self.font fontWithSize:fontSize];
self.transform = CGAffineTransformScale(self.transform, 1.f / scaleRatio, 1.f / scaleRatio);
[self layoutIfNeeded];
[UIView animateWithDuration:duration animations:^{
self.transform = startTransform;
newFrame = self.frame;
} completion:^(BOOL finished) {
self.layer.anchorPoint = archorPoint;
self.frame = newFrame;
}];
}
@end
Par exemple, pour animer la modification de la taille de la police d'étiquette à 30, la durée est de 1 seconde à partir du centre et d'une échelle plus grande. Appelez simplement
rapide
YOUR_LABEL.animate(fontSize: 30, duration: 1, animateAnchorPoint: LabelAnimateAnchorPoint.centerXCenterY)
OBJECTIF-C
[YOUR_LABEL animateWithFontSize:30
duration:1
animateAnchorPoint:kCenterXCenterYAnchorPoint];
Swift 3.0 & Swift 4.
UIView.animate(withDuration: 0.5, delay: 0.1, options: .curveLinear, animations: {
label.transform = label.transform.scaledBy(x:4,y:4) //Change x,y to get your desired effect.
} ) { (completed) in
//Animation Completed
}
J'ai trouvé chacune des suggestions ici inadéquate pour ces raisons:
Afin de conserver toutes ces fonctionnalités et d'obtenir une transition d'animation fluide, j'ai combiné l'approche de transformation et l'approche de police.
L'interface est simple. Mettez simplement à jour la propriété fontSize
et vous mettrez à jour la taille de la police. Faites-le dans un bloc d'animation et il s'animera.
@interface UILabel(MPFontSize)
@property(nonatomic) CGFloat fontSize;
@end
Quant à l'implémentation, il y a la manière simple, et il y a la meilleure manière.
Simple:
@implementation UILabel(MPFontSize)
- (void)setFontSize:(CGFloat)fontSize {
CGAffineTransform originalTransform = self.transform;
UIFont *targetFont = [self.font fontWithSize:fontSize];
[UIView animateWithDuration:0 delay:0 options:0 animations:^{
self.transform = CGAffineTransformScale( originalTransform,
fontSize / self.fontSize, fontSize / self.fontSize );
} completion:^(BOOL finished) {
self.transform = originalTransform;
if (finished)
self.font = targetFont;
}];
}
- (CGFloat)fontSize {
return self.font.pointSize;
};
@end
Maintenant, le problème avec cela est que la mise en page peut bégayer à la fin, car le cadre de la vue est dimensionné en fonction de la police d'origine jusqu'à la fin de l'animation, moment auquel le cadre est mis à jour pour accueillir la police cible sans animation.
La résolution de ce problème est un peu plus difficile car nous devons remplacer intrinsicContentSize
. Vous pouvez le faire en sous-classant UILabel
ou en accélérant la méthode. Personnellement, j'ai accéléré la méthode, car elle me permet de conserver une propriété générique fontSize
disponible pour tous les UILabel
, mais cela dépend du code de bibliothèque que je ne peux pas partager ici. Voici comment procéder en utilisant le sous-classement.
Interface:
@interface AnimatableLabel : UILabel
@property(nonatomic) CGFloat fontSize;
@end
La mise en oeuvre:
@interface AnimatableLabel()
@property(nonatomic) UIFont *targetFont;
@property(nonatomic) UIFont *originalFont;
@end
@implementation AnimatableLabel
- (void)setFontSize:(CGFloat)fontSize {
CGAffineTransform originalTransform = self.transform;
self.originalFont = self.font;
self.targetFont = [self.font fontWithSize:fontSize];
[self invalidateIntrinsicContentSize];
[UIView animateWithDuration:0 delay:0 options:0 animations:^{
self.transform = CGAffineTransformScale( originalTransform,
fontSize / self.fontSize, fontSize / self.fontSize );
} completion:^(BOOL finished) {
self.transform = originalTransform;
if (self.targetFont) {
if (finished)
self.font = self.targetFont;
self.targetFont = self.originalFont = nil;
[self invalidateIntrinsicContentSize];
}
}];
}
- (CGFloat)fontSize {
return self.font.pointSize;
};
- (CGSize)intrinsicContentSize {
@try {
if (self.targetFont)
self.font = self.targetFont;
return self.intrinsicContentSize;
}
@finally {
if (self.originalFont)
self.font = self.originalFont;
}
}
@end
Pour ceux qui ne recherchent pas une transformation, mais un changement de valeur réel:
UIView.transition(with: label, duration: 0.25, options: .transitionCrossDissolve, animations: {
self.label.font = UIFont.systemFont(ofSize: 15)
}) { isFinished in }
quand il y a du texte, faites ceci:
UIView.transition(with: label, duration: 0.25, options: .transitionCrossDissolve, animations: {
self.label.font = UIFont.boldSystemFont(ofSize: 15)
}) { isFinished in }
(Gif montre une police différente)