OLD | NEW |
(Empty) | |
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #import "ios/chrome/browser/ui/fade_truncated_label.h" |
| 6 |
| 7 #include <algorithm> |
| 8 #include "base/mac/objc_property_releaser.h" |
| 9 #import "ios/chrome/browser/ui/animation_util.h" |
| 10 #import "ios/chrome/browser/ui/reversed_animation.h" |
| 11 #import "ui/gfx/ios/uikit_util.h" |
| 12 |
| 13 namespace { |
| 14 // Animation key used for frame animations. |
| 15 NSString* const kFadeTruncatedLabelAnimationKey = |
| 16 @"FadeTruncatedLabelAnimationKey"; |
| 17 } |
| 18 |
| 19 @interface FadeTruncatedLabel () { |
| 20 base::mac::ObjCPropertyReleaser _propertyReleaser_FadeTruncatedLabel; |
| 21 } |
| 22 |
| 23 // Layer used to apply fade truncation to label. |
| 24 @property(nonatomic, retain) CAGradientLayer* maskLayer; |
| 25 |
| 26 // Temporary label used during animations. |
| 27 @property(nonatomic, retain) UILabel* animationLabel; |
| 28 |
| 29 // Returns the percentage of the label's width at which to begin the fade |
| 30 // gradient. |
| 31 + (CGFloat)gradientPercentageForText:(NSString*)text |
| 32 withAttributes:(NSDictionary*)attributes |
| 33 inBounds:(CGRect)bounds; |
| 34 |
| 35 @end |
| 36 |
| 37 @implementation FadeTruncatedLabel |
| 38 |
| 39 @synthesize maskLayer = _maskLayer; |
| 40 @synthesize animationLabel = _animationLabel; |
| 41 |
| 42 - (instancetype)initWithFrame:(CGRect)frame { |
| 43 self = [super initWithFrame:frame]; |
| 44 if (self) { |
| 45 // Initialize property releaser. |
| 46 _propertyReleaser_FadeTruncatedLabel.Init(self, [FadeTruncatedLabel class]); |
| 47 |
| 48 // Set background color and line break mode. |
| 49 self.backgroundColor = [UIColor clearColor]; |
| 50 self.lineBreakMode = NSLineBreakByClipping; |
| 51 |
| 52 // Instantiate |maskLayer| and add as mask. |
| 53 self.maskLayer = [[self class] maskLayerForText:self.text |
| 54 withAttributes:@{ |
| 55 NSFontAttributeName : self.font |
| 56 } |
| 57 inBounds:{CGPointZero, frame.size}]; |
| 58 self.layer.mask = self.maskLayer; |
| 59 } |
| 60 return self; |
| 61 } |
| 62 |
| 63 #pragma mark - UILabel overrides |
| 64 |
| 65 - (void)drawTextInRect:(CGRect)rect { |
| 66 // Draw if not animating. |
| 67 if (!self.animationLabel) |
| 68 [super drawTextInRect:rect]; |
| 69 |
| 70 // Update the mask gradient. |
| 71 [CATransaction begin]; |
| 72 [CATransaction setDisableActions:YES]; |
| 73 self.maskLayer.frame = rect; |
| 74 NSDictionary* fontAttributes = @{NSFontAttributeName : self.font}; |
| 75 CGFloat gradientBeginPercentage = |
| 76 [[self class] gradientPercentageForText:self.text |
| 77 withAttributes:fontAttributes |
| 78 inBounds:self.maskLayer.bounds]; |
| 79 self.maskLayer.locations = @[ @(gradientBeginPercentage), @(1.0) ]; |
| 80 [CATransaction commit]; |
| 81 } |
| 82 |
| 83 #pragma mark - Animations |
| 84 |
| 85 - (void)animateFromBeginFrame:(CGRect)beginFrame |
| 86 toEndFrame:(CGRect)endFrame |
| 87 duration:(CFTimeInterval)duration |
| 88 timingFunction:(CAMediaTimingFunction*)timingFunction { |
| 89 CAAnimation* frameAnimation = nil; |
| 90 |
| 91 // Animate the label. |
| 92 frameAnimation = FrameAnimationMake(self.layer, beginFrame, endFrame); |
| 93 frameAnimation.duration = duration; |
| 94 frameAnimation.timingFunction = timingFunction; |
| 95 [self.layer addAnimation:frameAnimation |
| 96 forKey:kFadeTruncatedLabelAnimationKey]; |
| 97 |
| 98 // When animating, add a temporary label using the larger frame size so that |
| 99 // truncation can occur via the gradient rather than by the edge of the text |
| 100 // layer's backing store. |
| 101 CGSize animationTextSize = |
| 102 CGSizeMake(std::max(beginFrame.size.width, endFrame.size.width), |
| 103 std::max(beginFrame.size.height, endFrame.size.height)); |
| 104 self.animationLabel = [[[UILabel alloc] |
| 105 initWithFrame:{CGPointZero, animationTextSize}] autorelease]; |
| 106 self.animationLabel.text = self.text; |
| 107 self.animationLabel.textColor = self.textColor; |
| 108 self.animationLabel.font = self.font; |
| 109 [self addSubview:self.animationLabel]; |
| 110 |
| 111 // Animate the mask layer. |
| 112 CGRect beginBounds = {CGPointZero, beginFrame.size}; |
| 113 CGRect endBounds = {CGPointZero, endFrame.size}; |
| 114 frameAnimation = FrameAnimationMake(self.maskLayer, beginBounds, endBounds); |
| 115 frameAnimation.duration = duration; |
| 116 frameAnimation.timingFunction = timingFunction; |
| 117 NSDictionary* attributes = @{NSFontAttributeName : self.font}; |
| 118 CGFloat beginGradientPercentage = |
| 119 [[self class] gradientPercentageForText:self.text |
| 120 withAttributes:attributes |
| 121 inBounds:beginBounds]; |
| 122 CGFloat endGradientPercentage = |
| 123 [[self class] gradientPercentageForText:self.text |
| 124 withAttributes:attributes |
| 125 inBounds:endBounds]; |
| 126 CABasicAnimation* gradientAnimation = |
| 127 [CABasicAnimation animationWithKeyPath:@"locations"]; |
| 128 gradientAnimation.fromValue = @[ @(beginGradientPercentage), @(1.0) ]; |
| 129 gradientAnimation.toValue = @[ @(endGradientPercentage), @(1.0) ]; |
| 130 gradientAnimation.duration = duration; |
| 131 gradientAnimation.timingFunction = timingFunction; |
| 132 CAAnimation* animation = |
| 133 AnimationGroupMake(@[ frameAnimation, gradientAnimation ]); |
| 134 [self.maskLayer addAnimation:animation |
| 135 forKey:kFadeTruncatedLabelAnimationKey]; |
| 136 } |
| 137 |
| 138 - (void)reverseAnimations { |
| 139 // Reverse the animations, but leave the animation label in place. |
| 140 ReverseAnimationsForKeyForLayers(kFadeTruncatedLabelAnimationKey, |
| 141 @[ self.layer, self.maskLayer ]); |
| 142 } |
| 143 |
| 144 - (void)cleanUpAnimations { |
| 145 // Remove animation label and redraw. |
| 146 [self.animationLabel removeFromSuperview]; |
| 147 self.animationLabel = nil; |
| 148 [self setNeedsDisplay]; |
| 149 // Remove animations from layers. |
| 150 RemoveAnimationForKeyFromLayers(kFadeTruncatedLabelAnimationKey, |
| 151 @[ self.layer, self.maskLayer ]); |
| 152 } |
| 153 |
| 154 #pragma mark - Class methods |
| 155 |
| 156 + (CGFloat)gradientPercentageForText:(NSString*)text |
| 157 withAttributes:(NSDictionary*)attributes |
| 158 inBounds:(CGRect)bounds { |
| 159 CGSize textSize = |
| 160 ui::AlignSizeToUpperPixel([text sizeWithAttributes:attributes]); |
| 161 CGFloat gradientBeginPercentage = 1.0; |
| 162 if (textSize.width > bounds.size.width) { |
| 163 // Fade width is chosen to match GTMFadeTruncatingLabel. |
| 164 CGFloat fadeWidth = |
| 165 std::min<CGFloat>(bounds.size.height * 2, floor(bounds.size.width / 4)); |
| 166 gradientBeginPercentage = |
| 167 (bounds.size.width - fadeWidth) / bounds.size.width; |
| 168 } |
| 169 return gradientBeginPercentage; |
| 170 } |
| 171 |
| 172 + (CAGradientLayer*)maskLayerForText:(NSString*)text |
| 173 withAttributes:(NSDictionary*)attributes |
| 174 inBounds:(CGRect)bounds { |
| 175 CAGradientLayer* maskLayer = [CAGradientLayer layer]; |
| 176 maskLayer.bounds = bounds; |
| 177 maskLayer.colors = @[ |
| 178 reinterpret_cast<id>([UIColor blackColor].CGColor), |
| 179 reinterpret_cast<id>([UIColor clearColor].CGColor) |
| 180 ]; |
| 181 maskLayer.startPoint = CGPointMake(0.0, 0.5); |
| 182 maskLayer.endPoint = CGPointMake(1.0, 0.5); |
| 183 CGFloat gradientBeginPercentage = [self gradientPercentageForText:text |
| 184 withAttributes:attributes |
| 185 inBounds:bounds]; |
| 186 maskLayer.locations = @[ @(gradientBeginPercentage), @(1.0) ]; |
| 187 return maskLayer; |
| 188 } |
| 189 |
| 190 @end |
OLD | NEW |