| Index: ios/chrome/browser/ui/fade_truncated_label.mm
|
| diff --git a/ios/chrome/browser/ui/fade_truncated_label.mm b/ios/chrome/browser/ui/fade_truncated_label.mm
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..8aa4d7da478e54673e0707afc929259c1aa844aa
|
| --- /dev/null
|
| +++ b/ios/chrome/browser/ui/fade_truncated_label.mm
|
| @@ -0,0 +1,190 @@
|
| +// Copyright 2014 The Chromium Authors. All rights reserved.
|
| +// Use of this source code is governed by a BSD-style license that can be
|
| +// found in the LICENSE file.
|
| +
|
| +#import "ios/chrome/browser/ui/fade_truncated_label.h"
|
| +
|
| +#include <algorithm>
|
| +#include "base/mac/objc_property_releaser.h"
|
| +#import "ios/chrome/browser/ui/animation_util.h"
|
| +#import "ios/chrome/browser/ui/reversed_animation.h"
|
| +#import "ui/gfx/ios/uikit_util.h"
|
| +
|
| +namespace {
|
| +// Animation key used for frame animations.
|
| +NSString* const kFadeTruncatedLabelAnimationKey =
|
| + @"FadeTruncatedLabelAnimationKey";
|
| +}
|
| +
|
| +@interface FadeTruncatedLabel () {
|
| + base::mac::ObjCPropertyReleaser _propertyReleaser_FadeTruncatedLabel;
|
| +}
|
| +
|
| +// Layer used to apply fade truncation to label.
|
| +@property(nonatomic, retain) CAGradientLayer* maskLayer;
|
| +
|
| +// Temporary label used during animations.
|
| +@property(nonatomic, retain) UILabel* animationLabel;
|
| +
|
| +// Returns the percentage of the label's width at which to begin the fade
|
| +// gradient.
|
| ++ (CGFloat)gradientPercentageForText:(NSString*)text
|
| + withAttributes:(NSDictionary*)attributes
|
| + inBounds:(CGRect)bounds;
|
| +
|
| +@end
|
| +
|
| +@implementation FadeTruncatedLabel
|
| +
|
| +@synthesize maskLayer = _maskLayer;
|
| +@synthesize animationLabel = _animationLabel;
|
| +
|
| +- (instancetype)initWithFrame:(CGRect)frame {
|
| + self = [super initWithFrame:frame];
|
| + if (self) {
|
| + // Initialize property releaser.
|
| + _propertyReleaser_FadeTruncatedLabel.Init(self, [FadeTruncatedLabel class]);
|
| +
|
| + // Set background color and line break mode.
|
| + self.backgroundColor = [UIColor clearColor];
|
| + self.lineBreakMode = NSLineBreakByClipping;
|
| +
|
| + // Instantiate |maskLayer| and add as mask.
|
| + self.maskLayer = [[self class] maskLayerForText:self.text
|
| + withAttributes:@{
|
| + NSFontAttributeName : self.font
|
| + }
|
| + inBounds:{CGPointZero, frame.size}];
|
| + self.layer.mask = self.maskLayer;
|
| + }
|
| + return self;
|
| +}
|
| +
|
| +#pragma mark - UILabel overrides
|
| +
|
| +- (void)drawTextInRect:(CGRect)rect {
|
| + // Draw if not animating.
|
| + if (!self.animationLabel)
|
| + [super drawTextInRect:rect];
|
| +
|
| + // Update the mask gradient.
|
| + [CATransaction begin];
|
| + [CATransaction setDisableActions:YES];
|
| + self.maskLayer.frame = rect;
|
| + NSDictionary* fontAttributes = @{NSFontAttributeName : self.font};
|
| + CGFloat gradientBeginPercentage =
|
| + [[self class] gradientPercentageForText:self.text
|
| + withAttributes:fontAttributes
|
| + inBounds:self.maskLayer.bounds];
|
| + self.maskLayer.locations = @[ @(gradientBeginPercentage), @(1.0) ];
|
| + [CATransaction commit];
|
| +}
|
| +
|
| +#pragma mark - Animations
|
| +
|
| +- (void)animateFromBeginFrame:(CGRect)beginFrame
|
| + toEndFrame:(CGRect)endFrame
|
| + duration:(CFTimeInterval)duration
|
| + timingFunction:(CAMediaTimingFunction*)timingFunction {
|
| + CAAnimation* frameAnimation = nil;
|
| +
|
| + // Animate the label.
|
| + frameAnimation = FrameAnimationMake(self.layer, beginFrame, endFrame);
|
| + frameAnimation.duration = duration;
|
| + frameAnimation.timingFunction = timingFunction;
|
| + [self.layer addAnimation:frameAnimation
|
| + forKey:kFadeTruncatedLabelAnimationKey];
|
| +
|
| + // When animating, add a temporary label using the larger frame size so that
|
| + // truncation can occur via the gradient rather than by the edge of the text
|
| + // layer's backing store.
|
| + CGSize animationTextSize =
|
| + CGSizeMake(std::max(beginFrame.size.width, endFrame.size.width),
|
| + std::max(beginFrame.size.height, endFrame.size.height));
|
| + self.animationLabel = [[[UILabel alloc]
|
| + initWithFrame:{CGPointZero, animationTextSize}] autorelease];
|
| + self.animationLabel.text = self.text;
|
| + self.animationLabel.textColor = self.textColor;
|
| + self.animationLabel.font = self.font;
|
| + [self addSubview:self.animationLabel];
|
| +
|
| + // Animate the mask layer.
|
| + CGRect beginBounds = {CGPointZero, beginFrame.size};
|
| + CGRect endBounds = {CGPointZero, endFrame.size};
|
| + frameAnimation = FrameAnimationMake(self.maskLayer, beginBounds, endBounds);
|
| + frameAnimation.duration = duration;
|
| + frameAnimation.timingFunction = timingFunction;
|
| + NSDictionary* attributes = @{NSFontAttributeName : self.font};
|
| + CGFloat beginGradientPercentage =
|
| + [[self class] gradientPercentageForText:self.text
|
| + withAttributes:attributes
|
| + inBounds:beginBounds];
|
| + CGFloat endGradientPercentage =
|
| + [[self class] gradientPercentageForText:self.text
|
| + withAttributes:attributes
|
| + inBounds:endBounds];
|
| + CABasicAnimation* gradientAnimation =
|
| + [CABasicAnimation animationWithKeyPath:@"locations"];
|
| + gradientAnimation.fromValue = @[ @(beginGradientPercentage), @(1.0) ];
|
| + gradientAnimation.toValue = @[ @(endGradientPercentage), @(1.0) ];
|
| + gradientAnimation.duration = duration;
|
| + gradientAnimation.timingFunction = timingFunction;
|
| + CAAnimation* animation =
|
| + AnimationGroupMake(@[ frameAnimation, gradientAnimation ]);
|
| + [self.maskLayer addAnimation:animation
|
| + forKey:kFadeTruncatedLabelAnimationKey];
|
| +}
|
| +
|
| +- (void)reverseAnimations {
|
| + // Reverse the animations, but leave the animation label in place.
|
| + ReverseAnimationsForKeyForLayers(kFadeTruncatedLabelAnimationKey,
|
| + @[ self.layer, self.maskLayer ]);
|
| +}
|
| +
|
| +- (void)cleanUpAnimations {
|
| + // Remove animation label and redraw.
|
| + [self.animationLabel removeFromSuperview];
|
| + self.animationLabel = nil;
|
| + [self setNeedsDisplay];
|
| + // Remove animations from layers.
|
| + RemoveAnimationForKeyFromLayers(kFadeTruncatedLabelAnimationKey,
|
| + @[ self.layer, self.maskLayer ]);
|
| +}
|
| +
|
| +#pragma mark - Class methods
|
| +
|
| ++ (CGFloat)gradientPercentageForText:(NSString*)text
|
| + withAttributes:(NSDictionary*)attributes
|
| + inBounds:(CGRect)bounds {
|
| + CGSize textSize =
|
| + ui::AlignSizeToUpperPixel([text sizeWithAttributes:attributes]);
|
| + CGFloat gradientBeginPercentage = 1.0;
|
| + if (textSize.width > bounds.size.width) {
|
| + // Fade width is chosen to match GTMFadeTruncatingLabel.
|
| + CGFloat fadeWidth =
|
| + std::min<CGFloat>(bounds.size.height * 2, floor(bounds.size.width / 4));
|
| + gradientBeginPercentage =
|
| + (bounds.size.width - fadeWidth) / bounds.size.width;
|
| + }
|
| + return gradientBeginPercentage;
|
| +}
|
| +
|
| ++ (CAGradientLayer*)maskLayerForText:(NSString*)text
|
| + withAttributes:(NSDictionary*)attributes
|
| + inBounds:(CGRect)bounds {
|
| + CAGradientLayer* maskLayer = [CAGradientLayer layer];
|
| + maskLayer.bounds = bounds;
|
| + maskLayer.colors = @[
|
| + reinterpret_cast<id>([UIColor blackColor].CGColor),
|
| + reinterpret_cast<id>([UIColor clearColor].CGColor)
|
| + ];
|
| + maskLayer.startPoint = CGPointMake(0.0, 0.5);
|
| + maskLayer.endPoint = CGPointMake(1.0, 0.5);
|
| + CGFloat gradientBeginPercentage = [self gradientPercentageForText:text
|
| + withAttributes:attributes
|
| + inBounds:bounds];
|
| + maskLayer.locations = @[ @(gradientBeginPercentage), @(1.0) ];
|
| + return maskLayer;
|
| +}
|
| +
|
| +@end
|
|
|