blob: 884c1bc0f610d377b9c14f576d1d8d7521bfe539 [file] [log] [blame]
// Copyright 2016 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/reading_list/number_badge_view.h"
#import <Foundation/Foundation.h>
#include "base/format_macros.h"
#import "ios/chrome/browser/ui/reading_list/text_badge_view.h"
#import "ios/chrome/common/material_timing.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
namespace {
const CGFloat kAnimationDuration = ios::material::kDuration3;
// The margin on all sides of the label.
const CGFloat kLabelMargin = 2.5f;
} // namespace
@interface NumberBadgeView ()
@property(nonatomic, assign) NSInteger displayNumber;
// The pill-shaped badge that displays |displayNumber|.
@property(nonatomic, readonly, strong) TextBadgeView* textBadge;
// Indicate whether |textBadge| has been added as a subview of the
// |NumberBadgeView|.
@property(nonatomic, assign) BOOL didAddSubviews;
@end
@implementation NumberBadgeView
@synthesize displayNumber = _displayNumber;
@synthesize textBadge = _textBadge;
@synthesize didAddSubviews = _didAddSubviews;
#pragma mark - Lifecycle
- (instancetype)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
_didAddSubviews = NO;
}
return self;
}
#pragma mark - Public methods
- (void)setNumber:(NSInteger)number animated:(BOOL)animated {
// If the previous number and current number match, do nothing.
if (self.displayNumber != number) {
self.displayNumber = number;
if (animated) {
// If the view is being animated in, then |hidden| needs to be set to
// |NO|. Otherwise the view is being animated out, in which case |hidden|
// is already |NO|.
self.hidden = NO;
[UIView animateWithDuration:kAnimationDuration
animations:^{
if (number > 0) {
self.alpha = 1.0;
// Only setting when > 0 as this makes the animation
// look better than switching to 0 then fading out.
[self.textBadge
setText:[NSString stringWithFormat:@"%" PRIdNS, number]];
} else {
self.alpha = 0.0;
}
[self setNeedsLayout];
[self layoutIfNeeded];
}
completion:^(BOOL finished) {
if (finished && self.alpha == 0.0) {
self.hidden = YES;
}
}];
} else {
[self.textBadge setText:[NSString stringWithFormat:@"%" PRIdNS, number]];
self.hidden = (number > 0 ? NO : YES);
self.alpha = (number > 0 ? 1 : 0);
}
}
}
- (void)setBackgroundColor:(UIColor*)backgroundColor animated:(BOOL)animated {
if (animated) {
[UIView animateWithDuration:kAnimationDuration
animations:^{
[self.textBadge setBackgroundColor:backgroundColor];
}];
} else {
[self.textBadge setBackgroundColor:backgroundColor];
}
}
#pragma mark - UIView overrides
// Override |willMoveToSuperview| to add |textBadge| to the view hierarchy and
// perform additional setup operations.
- (void)willMoveToSuperview:(UIView*)newSuperview {
if (!self.didAddSubviews) {
[self addSubview:self.textBadge];
self.didAddSubviews = YES;
[self activateConstraints];
self.backgroundColor = UIColor.clearColor;
// Start hidden.
self.alpha = 0.0;
self.hidden = YES;
}
[super willMoveToSuperview:newSuperview];
}
#pragma mark - Private properties
// Lazily load |textBadge|.
- (TextBadgeView*)textBadge {
if (!_textBadge) {
_textBadge = [[TextBadgeView alloc] initWithText:@"0"
labelHorizontalMargin:kLabelMargin];
[_textBadge setTranslatesAutoresizingMaskIntoConstraints:NO];
}
return _textBadge;
}
#pragma mark - Private methods
// Activate constraints to properly position |textBadge| within NumberBadgeView.
- (void)activateConstraints {
[NSLayoutConstraint activateConstraints:@[
[self.textBadge.trailingAnchor constraintEqualToAnchor:self.trailingAnchor],
[self.textBadge.centerYAnchor constraintEqualToAnchor:self.centerYAnchor],
[self.widthAnchor constraintEqualToAnchor:self.textBadge.widthAnchor],
[self.heightAnchor constraintEqualToAnchor:self.textBadge.heightAnchor]
]];
}
@end