現在、カスタムView Controllerコンテナを使用するアプリケーションを作成しています。一度に複数のビューが画面に表示され、1つをタップすると、選択したView Controllerが全画面にアニメーション化します。そうすることで、選択されたView Controllerサブビューも同様に拡大縮小します(フレーム、フォントサイズなど)。しかし、UILabelのフォントプロパティはアニメーション化できず、問題につながります。私は複数のソリューションを試しましたが、すべてがひどいものです。
私が試した解決策は次のとおりです。
これまでのところ、1つが最善の解決策でしたが、満足していません。
[UIView animate ..]を使用してスムーズにアニメーション化するUILabelの代替物がある場合は、他の提案を探しています。
UILabelにしたいことに似た良い例があります: http://www.cocoawithlove.com/2010/09/zoomingviewcontroller-to-animate-uiview.html
編集:このコードは機能します
// 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];
}];
以下のようなアニメーションでUILabel
のサイズとフォントを変更できます。ここに、transform AnimationでUILabel
のフォントを変更する方法の例を示します.
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);
}];
これがお役に立てば幸いです。
Swift 3.0、4.
UIView.animate(withDuration: 0.5) {
label.transform = CGAffineTransform(scaleX: 1.1, y: 1.1) //Scale label area
}
SwiftでUILabel
拡張機能を作成しました。
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()
}
}
}
ラベルテキストが左揃えまたは右揃えの場合、行のコメントを解除します。
また、アニメーション可能なプロパティとしてfontSizeを持つCATextLayerを使用することもできます。
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)
私は自分のプロジェクトでそれを使用しました: https://github.com/yinanq/AngelListJobs
このアニメーションは、必要に応じて、フォントを上から左に揃えたままにします(CGAffineTransformScaleがラベルを中央からスケーリングするのとは異なります)。 CATextLayerの欠点は、CALayersが自動レイアウト制約アニメーションで動作しないことです(CATextLayerのみを含むUIViewを作成し、その制約をアニメーション化することで必要に応じて解決しました)。
フォントサイズの変更をアニメートするためにUILabel
の拡張機能を作成しました
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
}
}
アニメーションの方向を調整する場合は、以下の方法を使用して適切なアンカーポイントを配置します。
スイフト
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
}
}
}
目的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
たとえば、ラベルのフォントサイズを30に変更するアニメーションを作成するには、中央から1秒の長さで拡大します。単に電話する
スイフト
YOUR_LABEL.animate(fontSize: 30, duration: 1, animateAnchorPoint: LabelAnimateAnchorPoint.centerXCenterY)
目的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
}
これらの理由から、ここでの提案はそれぞれ不十分であることがわかりました。
これらのすべての機能を保持し、アニメーションをスムーズに移行させるために、変換アプローチとフォントアプローチを組み合わせました。
インターフェースはシンプルです。 fontSize
プロパティを更新するだけで、フォントのサイズが更新されます。これをアニメーションブロック内で実行すると、アニメーションが実行されます。
@interface UILabel(MPFontSize)
@property(nonatomic) CGFloat fontSize;
@end
実装に関しては、簡単な方法があり、より良い方法があります。
シンプル:
@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
さて、これの問題は、アニメーションの完了までビューのフレームが元のフォントに基づいてサイズ変更されるため、レイアウトが完了時に途切れることがあることです。
intrinsicContentSize
をオーバーライドする必要があるため、この問題の修正は少し難しくなります。これを行うには、UILabel
をサブクラス化するか、メソッドをスウィズルします。汎用のfontSize
プロパティをすべてのUILabel
sで使用できるようにするため、個人的にメソッドを切り替えますが、それはここで共有できないライブラリコードに依存します。サブクラス化を使用してこれを実行する方法を次に示します。
インタフェース:
@interface AnimatableLabel : UILabel
@property(nonatomic) CGFloat fontSize;
@end
実装:
@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
変換を求めていないが、実際の値が変化する場合:
UIView.transition(with: label, duration: 0.25, options: .transitionCrossDissolve, animations: {
self.label.font = UIFont.systemFont(ofSize: 15)
}) { isFinished in }
テキストがある場合、これを行います:
UIView.transition(with: label, duration: 0.25, options: .transitionCrossDissolve, animations: {
self.label.font = UIFont.boldSystemFont(ofSize: 15)
}) { isFinished in }
(Gifは異なるフォントを表示します)