UILabelをアニメーション化すると、説明したものと同様の結果が得られました。縮小するアニメーションのみが機能し、拡大するアニメーションはフルサイズにジャンプしました。
UITextViewも試してみましたが、それもうまくいきませんでした。
私の解決策は、coverViewという名前の別のUIViewを取得し、それをUILabelの下に配置することでした。UILabelが展開されると、アニメーション化せずにサイズが変更され、coverViewは展開された部分を非表示にします。次に、アニメーションでcoverViewのorigin.yとsize.heightが変更され、展開されたUILabelが表示されます。UILabelが縮小されると、アニメーションでcoverViewのorigin.yとsize.heightが変更され、UILabelが徐々に非表示になります。アニメーションの完了ブロックで、UILabelのフレームが縮小されたサイズにサイズ変更されます。
このためのカスタムUIViewを作成しました:AnimatedLabel。以下に完全なコードを見つけることができます。私はすぐにそれを書き、コメントしませんでしたが、理解するのは難しいことではありません。
使用方法(IBでも動作します):
// Init
AnimatedLabel *animLabel = [[AnimatedLabel alloc] initWithFrame:/*desired CGRect*/];
// Set the surroundingBackgroundColor, this color will be used for the coverView
// you should set it to the container view's backgroundColor
// IMPORTANT: transparency is not supported! (if its transparent you'll see the UILabel)
animLabel.surroundingBackgroundColor = self.view.backgroundColor;
// Set the text
animLabel.label.text = /*text text text ...*/;
// Call sizeToFit to modify the label's size to fit the text perfectly
// so that the text is not moved after the label is expanded (UILabel always
// centers the text vertically and if the label
// is not resized you will see that the text is repositioned after the animation)
[animLabel sizeToFit];
// Open the label
[animLabel open];
// Close the label
[animLabel close];
// You can resize the animLabel and the subviews will be correctly resized
animLabel.frame = /*CGRect*/;
AnimatedLabel.h
//
// AnimatedLabel.h
// labeltest
//
// Created by Alpar Szotyori on 20/06/2012.
// Use it, modify it, improve it, share it!
//
#import <UIKit/UIKit.h>
@interface AnimatedLabel : UIView
@property (nonatomic, strong) UILabel *label;
@property (nonatomic, strong) UIColor *surroundingBackgroundColor;
@property (nonatomic) BOOL isOpen;
// Public methods
- (void)open;
- (void)close;
@end
AnimatedLabel.m:
//
// AnimatedLabel.m
// labeltest
//
// Created by Alpar Szotyori on 20/06/2012.
// Use it, modify it, improve it, share it!
//
#import "AnimatedLabel.h"
@interface NSString (visibleText)
- (NSString*)stringVisibleInRect:(CGRect)rect withFont:(UIFont*)font constrainedToSize:(CGSize)size;
@end
@implementation NSString (visibleText)
- (NSString*)stringVisibleInRect:(CGRect)rect withFont:(UIFont*)font constrainedToSize:(CGSize)size
{
BOOL addEllipse = NO;
NSString *visibleString = @"";
for (int i = 2; i <= self.length; i++)
{
NSString *testString = [self substringToIndex:i];
CGSize stringSize = [testString sizeWithFont:font constrainedToSize:size];
if (stringSize.height > rect.size.height || stringSize.width > rect.size.width) {
addEllipse = YES;
break;
}
visibleString = testString;
}
if (addEllipse) {
if (visibleString.length >= 3) {
visibleString = [[visibleString substringToIndex:visibleString.length - 3] stringByAppendingString:@"…"];
}
}
return visibleString;
}
@end
@interface AnimatedLabel()
@property (nonatomic, strong) UIView *coverView;
@property (nonatomic, strong) UIColor *backgrndColor;
@property (nonatomic) CGFloat origLabelHeight;
@property (nonatomic, strong) NSString *origLabelText;
@property (nonatomic) BOOL animating;
@property (nonatomic) BOOL labelKVOSet;
- (void)changeHeight:(CGFloat)height animated:(BOOL)animated;
- (void)addKVOToLabel;
- (void)removeKVOFromLabel;
@end
@implementation AnimatedLabel
#pragma mark - Properties
@synthesize label;
@synthesize surroundingBackgroundColor = i_surroundingBackgroundColor;
@synthesize isOpen;
- (void)setSurroundingBackgroundColor:(UIColor *)surroundingBackgroundColor {
i_surroundingBackgroundColor = surroundingBackgroundColor;
self.coverView.backgroundColor = i_surroundingBackgroundColor;
}
#pragma mark - Initialization
- (id)initWithCoder:(NSCoder *)aDecoder {
self = [super initWithCoder:aDecoder];
if (self) {
self.label = [[UILabel alloc] initWithFrame:CGRectMake(0.0, 0.0, self.frame.size.width, self.frame.size.height)];
self.label.numberOfLines = 0;
self.label.backgroundColor = self.backgrndColor;
self.origLabelHeight = self.label.frame.size.height;
[self addKVOToLabel];
self.coverView = [[UIView alloc] initWithFrame:CGRectZero];
self.coverView.backgroundColor = [UIColor whiteColor];
[self addSubview:self.label];
[self addSubview:self.coverView];
self.animating = NO;
self.isOpen = NO;
}
return self;
}
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
self.label = [[UILabel alloc] initWithFrame:CGRectMake(0.0, 0.0, frame.size.width, frame.size.height)];
self.label.numberOfLines = 0;
self.label.backgroundColor = self.backgrndColor;
self.origLabelHeight = self.label.frame.size.height;
[self addKVOToLabel];
self.coverView = [[UIView alloc] initWithFrame:CGRectZero];
self.coverView.backgroundColor = [UIColor whiteColor];
[self addSubview:self.label];
[self addSubview:self.coverView];
self.animating = NO;
self.isOpen = NO;
}
return self;
}
#pragma mark - Overriden methods
- (void)setFrame:(CGRect)frame {
if (!self.animating && self.isOpen) {
self.isOpen = NO;
[self changeHeight:self.origLabelHeight animated:NO];
frame.size.height = self.origLabelHeight;
}
[super setFrame:frame];
if (!self.animating) {
self.label.frame= CGRectMake(0.0, 0.0, frame.size.width, frame.size.height);
self.origLabelHeight = self.label.frame.size.height;
[self removeKVOFromLabel];
NSString *visibleText = [self.origLabelText stringVisibleInRect:self.label.frame withFont:self.label.font constrainedToSize:CGSizeMake(frame.size.width, LONG_MAX)];
self.label.text = visibleText;
[self.label sizeToFit];
[self addKVOToLabel];
}
}
- (void)setBackgroundColor:(UIColor *)backgroundColor {
[super setBackgroundColor:[UIColor clearColor]];
self.backgrndColor = backgroundColor;
self.label.backgroundColor = self.backgrndColor;
}
- (UIColor *)backgroundColor {
return self.backgrndColor;
}
- (void)sizeToFit {
[self removeKVOFromLabel];
NSString *visibleText = [self.origLabelText stringVisibleInRect:self.label.frame withFont:self.label.font constrainedToSize:CGSizeMake(self.frame.size.width, LONG_MAX)];
self.label.text = visibleText;
[self.label sizeToFit];
self.origLabelHeight = self.label.frame.size.height;
[self addKVOToLabel];
self.frame = CGRectMake(self.frame.origin.x, self.frame.origin.y, self.label.frame.size.width, self.label.frame.size.height);
}
#pragma mark - KVO methods
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
if ([keyPath isEqualToString:@"text"]) {
self.origLabelText = [change objectForKey:NSKeyValueChangeNewKey];
}
}
#pragma mark - Public methods
- (void)open {
self.origLabelHeight = self.label.frame.size.height;
[self removeKVOFromLabel];
self.label.text = self.origLabelText;
[self addKVOToLabel];
[self.label sizeToFit];
if (self.origLabelHeight == self.label.frame.size.height) {
return;
}
[self changeHeight:self.label.frame.size.height animated:YES];
self.isOpen = YES;
}
- (void)close {
if (self.frame.size.height == self.origLabelHeight) {
return;
}
[self changeHeight:self.origLabelHeight animated:YES];
self.isOpen = NO;
}
#pragma mark - Private methods
#pragma mark - Properties
@synthesize coverView, backgrndColor, origLabelHeight, origLabelText, animating, labelKVOSet;
#pragma mark - Methods
- (void)changeHeight:(CGFloat)height animated:(BOOL)animated {
if (self.frame.size.height == height) {
return;
}
if (height > self.frame.size.height) {
self.animating = YES;
[self.label sizeToFit];
height = self.label.frame.size.height;
self.coverView.frame = CGRectMake(0.0, self.frame.size.height, self.frame.size.width, height - self.frame.size.height);
self.frame = CGRectMake(self.frame.origin.x, self.frame.origin.y, self.frame.size.width, height);
self.label.frame = CGRectMake(0.0, 0.0, self.frame.size.width, self.frame.size.height);
if (animated) {
[UIView animateWithDuration:0.5 animations:^(){
self.coverView.frame = CGRectMake(0.0, self.frame.size.height, self.frame.size.width, 0.0);
} completion:^(BOOL completed){
self.animating = NO;
}];
} else {
self.coverView.frame = CGRectMake(0.0, self.frame.size.height, self.frame.size.width, 0.0);
self.animating = NO;
}
} else {
self.animating = YES;
if (animated) {
[UIView animateWithDuration:0.5 animations:^(){
self.coverView.frame = CGRectMake(0.0, height, self.frame.size.width, self.frame.size.height - height);
} completion:^(BOOL completed){
self.frame = CGRectMake(self.frame.origin.x, self.frame.origin.y, self.frame.size.width, height);
self.coverView.frame = CGRectMake(0.0, height, self.frame.size.width, 0.0);
self.label.frame = CGRectMake(0.0, 0.0, self.frame.size.width, height);
self.animating = NO;
}];
} else {
self.frame = CGRectMake(self.frame.origin.x, self.frame.origin.y, self.frame.size.width, height);
self.coverView.frame = CGRectMake(0.0, height, self.frame.size.width, 0.0);
self.label.frame = CGRectMake(0.0, 0.0, self.frame.size.width, height);
self.animating = NO;
}
}
}
- (void)addKVOToLabel {
if (self.label == nil) {
return;
}
if (!self.labelKVOSet) {
self.labelKVOSet = YES;
[self.label addObserver:self forKeyPath:@"text" options:NSKeyValueObservingOptionNew context:nil];
}
}
- (void)removeKVOFromLabel {
if (self.label == nil) {
return;
}
if (self.labelKVOSet) {
self.labelKVOSet = NO;
[self.label removeObserver:self forKeyPath:@"text"];
}
}
@end