// Copyright 2022 The Chromium Authors
// 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/tab_switcher/tab_grid/pinned_tabs/pinned_cell.h"
#import <MaterialComponents/MaterialActivityIndicator.h>
#import <ostream>
#import "base/check.h"
#import "base/notreached.h"
#import "base/strings/sys_string_conversions.h"
#import "ios/chrome/browser/shared/ui/elements/top_aligned_image_view.h"
#import "ios/chrome/browser/shared/ui/symbols/symbols.h"
#import "ios/chrome/browser/shared/ui/util/rtl_geometry.h"
#import "ios/chrome/browser/ui/tab_switcher/tab_grid/grid/grid_item_identifier.h"
#import "ios/chrome/browser/ui/tab_switcher/tab_grid/pinned_tabs/pinned_tabs_constants.h"
#import "ios/chrome/browser/ui/tab_switcher/tab_grid/transitions/legacy_grid_transition_animation.h"
#import "ios/chrome/browser/ui/tab_switcher/tab_switcher_item.h"
#import "ios/chrome/common/ui/colors/semantic_color_names.h"
#import "ios/chrome/common/ui/elements/gradient_view.h"
#import "ios/chrome/grit/ios_strings.h"
#import "ios/web/public/web_state_id.h"
#import "ui/base/l10n/l10n_util_mac.h"
#import "ui/gfx/ios/uikit_util.h"
namespace {
// TODO(crbug.com/40890700): Refactor this method.
// Frame-based layout utilities for GridTransitionCell.
// Scales the size of `view`'s frame by `factor` in both height and width. This
// scaling is done by changing the frame size without changing its origin,
// unlike a scale transform which scales around the view's center.
void ScaleView(UIView* view, CGFloat factor) {
if (!view) {
return;
}
CGRect frame = view.frame;
frame.size.width *= factor;
frame.size.height *= factor;
view.frame = frame;
}
// TODO(crbug.com/40890700): Refactor this method.
// Positions `view` by setting its frame's origin to `point`.
void PositionView(UIView* view, CGPoint point) {
if (!view) {
return;
}
CGRect frame = view.frame;
frame.origin = point;
view.frame = frame;
}
// Returns "dark" non-dynamic color from provided `dynamicColor`.
//
// Note: Pinned cell doesn't change it's colors in the Light/Dark modes.
// When dynamic colors are used it might create discreptancies during the
// transtion animation. The cell could be copied with system interface traits
// applied. Therefore, the Light colors could show up during the animation.
//
// In order to match the appearance of the other UI-elements on the tab grid
// it is worth using the same color names, but using "dark" non-dynamic part
// of them.
UIColor* GetInterfaceStyleDarkColor(UIColor* dynamicColor) {
UITraitCollection* interfaceStyleDarkTraitCollection = [UITraitCollection
traitCollectionWithUserInterfaceStyle:UIUserInterfaceStyleDark];
return [dynamicColor
resolvedColorWithTraitCollection:interfaceStyleDarkTraitCollection];
}
} // namespace
@interface PinnedCell ()
// Title is displayed by this label.
@property(nonatomic, weak) UILabel* titleLabel;
// View that holds the WebState's snapshot image.
@property(nonatomic, weak) TopAlignedImageView* snapshotView;
// Container view that holds favicon and title.
//
// Note: This view is a counterpart of the GridCell's `topBar`. It is named as
// a `header` since there are no top and bottom parts (bars) in the pinned
// cell.
@property(nonatomic, weak) UIView* headerView;
// Gradient view that fades out the trailing part of the title label.
@property(nonatomic, weak) UIView* titleLabelFader;
// Snapshot view's top constraint.
@property(nonatomic, strong) NSLayoutConstraint* snapshotViewTopConstraint;
// Header view's height constraint.
@property(nonatomic, strong) NSLayoutConstraint* headerViewHeightConstraint;
// Favicon container view's center Y constraint.
@property(nonatomic, strong)
NSLayoutConstraint* faviconContainerViewCenterYConstraint;
@end
@implementation PinnedCell {
// Container for the title label.
UIView* _titleLabelContainer;
// Container for the `_faviconView`.
UIView* _faviconContainerView;
// View for displaying the favicon.
UIImageView* _faviconView;
// Activity Indicator view that animates while WebState is loading.
MDCActivityIndicator* _activityIndicator;
// Title label's leading constraint.
NSLayoutConstraint* _titleLabelLeadingConstraint;
// Title label's trailing constraint.
NSLayoutConstraint* _titleLabelTrailingConstraint;
// Title label's fader leading constraint.
NSLayoutConstraint* _titleLabelFaderLeadingConstraint;
// Title label's fader trailing constraint.
NSLayoutConstraint* _titleLabelFaderTrailingConstraint;
}
+ (instancetype)transitionSelectionCellFromCell:(PinnedCell*)cell {
PinnedCell* transitionSelectionCell =
[[self alloc] initWithFrame:cell.bounds];
transitionSelectionCell.selected = YES;
transitionSelectionCell.contentView.hidden = YES;
return transitionSelectionCell;
}
- (instancetype)initWithFrame:(CGRect)frame {
if ((self = [super initWithFrame:frame])) {
self.backgroundColor = GetInterfaceStyleDarkColor(
[UIColor colorNamed:kSecondaryBackgroundColor]);
self.layer.cornerRadius = kPinnedCellCornerRadius;
self.layer.masksToBounds = NO;
self.contentView.layer.cornerRadius = kPinnedCellCornerRadius;
self.contentView.layer.masksToBounds = YES;
self.contentView.backgroundColor = GetInterfaceStyleDarkColor(
[UIColor colorNamed:kSecondaryBackgroundColor]);
[self setupSelectedBackgroundView];
[self setupSnapshotView];
[self setupHeaderView];
[self setupFaviconContainerView];
[self setupFaviconView];
[self setupActivityIndicator];
[self setupTitleLabel];
[self setupTitleLabelFader];
}
return self;
}
- (void)prepareForReuse {
[super prepareForReuse];
self.icon = nil;
self.title = nil;
self.snapshot = nil;
self.pinnedItemIdentifier = web::WebStateID();
}
#pragma mark - Public
- (void)setHighlighted:(BOOL)highlighted {
// NO-OP to disable highlighting and only allow selection.
}
- (UIImage*)icon {
return _faviconView.image;
}
- (void)setIcon:(UIImage*)icon {
_faviconView.image = icon;
}
- (UIImage*)snapshot {
return self.snapshotView.image;
}
- (void)setSnapshot:(UIImage*)snapshot {
self.snapshotView.image = snapshot;
}
- (NSString*)title {
return _titleLabel.text;
}
- (void)setTitle:(NSString*)title {
NSTextAlignment titleTextAligment = DetermineBestAlignmentForText(title);
_titleLabel.text = [title copy];
_titleLabel.textAlignment = titleTextAligment;
self.accessibilityLabel = [self accessibilityLabelWithTitle:title];
[self updateTitleLabelAppearance];
[self updateTitleLabelFaderAppearance];
}
- (UIDragPreviewParameters*)dragPreviewParameters {
UIBezierPath* visiblePath = [UIBezierPath
bezierPathWithRoundedRect:self.bounds
cornerRadius:self.contentView.layer.cornerRadius];
UIDragPreviewParameters* params = [[UIDragPreviewParameters alloc] init];
params.visiblePath = visiblePath;
return params;
}
- (void)showActivityIndicator {
[_activityIndicator startAnimating];
[_activityIndicator setHidden:NO];
[_faviconContainerView setHidden:YES];
}
- (void)hideActivityIndicator {
[_activityIndicator stopAnimating];
[_activityIndicator setHidden:YES];
[_faviconContainerView setHidden:NO];
}
- (void)setPinnedItemIdentifier:(web::WebStateID)pinnedItemIdentifier {
_pinnedItemIdentifier = pinnedItemIdentifier;
if (pinnedItemIdentifier.valid()) {
TabSwitcherItem* item =
[[TabSwitcherItem alloc] initWithIdentifier:pinnedItemIdentifier];
self.itemIdentifier = [[GridItemIdentifier alloc] initWithTabItem:item];
} else {
self.itemIdentifier = nil;
}
}
#pragma mark - UIAccessibility
- (BOOL)isAccessibilityElement {
// This makes the whole cell tappable in VoiceOver.
return YES;
}
#pragma mark - Private
// Sets up the selection border.
- (void)setupSelectedBackgroundView {
UIView* selectedBackgroundBorderView = [[UIView alloc] init];
selectedBackgroundBorderView.translatesAutoresizingMaskIntoConstraints = NO;
selectedBackgroundBorderView.layer.cornerRadius =
kPinnedCellCornerRadius + kPinnedCellSelectionRingGapWidth +
kPinnedCellSelectionRingTintWidth;
selectedBackgroundBorderView.layer.borderWidth =
kPinnedCellSelectionRingTintWidth;
selectedBackgroundBorderView.layer.borderColor =
[UIColor colorNamed:kStaticBlue400Color].CGColor;
UIView* selectedBackgroundView = [[UIView alloc] init];
[selectedBackgroundView addSubview:selectedBackgroundBorderView];
[NSLayoutConstraint activateConstraints:@[
[selectedBackgroundBorderView.topAnchor
constraintEqualToAnchor:selectedBackgroundView.topAnchor
constant:-kPinnedCellSelectionRingPadding],
[selectedBackgroundBorderView.leadingAnchor
constraintEqualToAnchor:selectedBackgroundView.leadingAnchor
constant:-kPinnedCellSelectionRingPadding],
[selectedBackgroundBorderView.trailingAnchor
constraintEqualToAnchor:selectedBackgroundView.trailingAnchor
constant:kPinnedCellSelectionRingPadding],
[selectedBackgroundBorderView.bottomAnchor
constraintEqualToAnchor:selectedBackgroundView.bottomAnchor
constant:kPinnedCellSelectionRingPadding]
]];
self.selectedBackgroundView = selectedBackgroundView;
}
- (void)setupSnapshotView {
TopAlignedImageView* snapshotView = [[TopAlignedImageView alloc] init];
snapshotView.translatesAutoresizingMaskIntoConstraints = NO;
// Snapshot view is shown only during the animation transition to the Tab
// view. The Tab view uses not static, but dynamic colors. Therefore, it is
// safe to apply dynamic color here.
snapshotView.backgroundColor = [UIColor colorNamed:kSecondaryBackgroundColor];
snapshotView.hidden = YES;
_snapshotView = snapshotView;
UIView* contentView = self.contentView;
[contentView addSubview:snapshotView];
_snapshotViewTopConstraint = [snapshotView.topAnchor
constraintEqualToAnchor:contentView.topAnchor
constant:kPinnedCellSnapshotTopPadding];
NSArray* constraints = @[
_snapshotViewTopConstraint,
[snapshotView.leadingAnchor
constraintEqualToAnchor:contentView.leadingAnchor],
[snapshotView.trailingAnchor
constraintEqualToAnchor:contentView.trailingAnchor],
[snapshotView.bottomAnchor
constraintGreaterThanOrEqualToAnchor:contentView.bottomAnchor],
];
[NSLayoutConstraint activateConstraints:constraints];
}
- (void)setupHeaderView {
UIView* headerView = [[UIView alloc] init];
headerView.translatesAutoresizingMaskIntoConstraints = NO;
headerView.clipsToBounds = YES;
headerView.backgroundColor = GetInterfaceStyleDarkColor(
[UIColor colorNamed:kSecondaryBackgroundColor]);
headerView.layer.cornerRadius = kPinnedCellCornerRadius;
_headerView = headerView;
UIView* contentView = self.contentView;
[contentView addSubview:headerView];
_headerViewHeightConstraint =
[headerView.heightAnchor constraintEqualToConstant:kPinnedCellHeight];
NSArray* constraints = @[
_headerViewHeightConstraint,
[headerView.topAnchor constraintEqualToAnchor:contentView.topAnchor],
[headerView.leadingAnchor
constraintEqualToAnchor:contentView.leadingAnchor],
[headerView.trailingAnchor
constraintEqualToAnchor:contentView.trailingAnchor],
];
[NSLayoutConstraint activateConstraints:constraints];
}
// Sets up the `_faviconContainerView` view.
- (void)setupFaviconContainerView {
UIView* faviconContainerView = [[UIView alloc] init];
faviconContainerView.translatesAutoresizingMaskIntoConstraints = NO;
[_headerView addSubview:faviconContainerView];
_faviconContainerViewCenterYConstraint = [faviconContainerView.centerYAnchor
constraintEqualToAnchor:_headerView.topAnchor
constant:kPinnedCellHeight / 2];
[NSLayoutConstraint activateConstraints:@[
_faviconContainerViewCenterYConstraint,
[faviconContainerView.leadingAnchor
constraintEqualToAnchor:_headerView.leadingAnchor
constant:kPinnedCellHorizontalPadding],
[faviconContainerView.widthAnchor
constraintEqualToConstant:kPinnedCellFaviconContainerWidth],
[faviconContainerView.heightAnchor
constraintEqualToAnchor:faviconContainerView.widthAnchor],
]];
_faviconContainerView = faviconContainerView;
}
// Sets up the `_faviconView` view.
- (void)setupFaviconView {
UIImageView* faviconView = [[UIImageView alloc] init];
[_faviconContainerView addSubview:faviconView];
faviconView.translatesAutoresizingMaskIntoConstraints = NO;
faviconView.contentMode = UIViewContentModeScaleAspectFill;
faviconView.clipsToBounds = YES;
faviconView.layer.cornerRadius = kPinnedCellFaviconCornerRadius;
faviconView.layer.masksToBounds = YES;
faviconView.tintColor =
GetInterfaceStyleDarkColor([UIColor colorNamed:kTextPrimaryColor]);
[NSLayoutConstraint activateConstraints:@[
[faviconView.widthAnchor constraintEqualToConstant:kPinnedCellFaviconWidth],
[faviconView.heightAnchor constraintEqualToAnchor:faviconView.widthAnchor],
[faviconView.centerYAnchor
constraintEqualToAnchor:_faviconContainerView.centerYAnchor],
[faviconView.centerXAnchor
constraintEqualToAnchor:_faviconContainerView.centerXAnchor],
]];
_faviconView = faviconView;
// Set the default icon.
self.icon = nil;
}
- (void)setupActivityIndicator {
CGRect indicatorFrame =
CGRectMake(0, 0, kPinnedCellFaviconWidth, kPinnedCellFaviconWidth);
MDCActivityIndicator* activityIndicator =
[[MDCActivityIndicator alloc] initWithFrame:indicatorFrame];
activityIndicator.translatesAutoresizingMaskIntoConstraints = NO;
activityIndicator.cycleColors = @[ [UIColor colorNamed:kBlueColor] ];
activityIndicator.radius =
ui::AlignValueToUpperPixel(kPinnedCellFaviconWidth / 2);
[_headerView addSubview:activityIndicator];
[NSLayoutConstraint activateConstraints:@[
[activityIndicator.centerYAnchor
constraintEqualToAnchor:_faviconView.centerYAnchor],
[activityIndicator.centerXAnchor
constraintEqualToAnchor:_faviconView.centerXAnchor],
]];
_activityIndicator = activityIndicator;
}
// Sets up the title label.
- (void)setupTitleLabel {
UILabel* titleLabel = [[UILabel alloc] init];
titleLabel.translatesAutoresizingMaskIntoConstraints = NO;
titleLabel.adjustsFontForContentSizeCategory = YES;
titleLabel.font = [UIFont preferredFontForTextStyle:UIFontTextStyleFootnote];
titleLabel.textColor =
GetInterfaceStyleDarkColor([UIColor colorNamed:kTextPrimaryColor]);
UIView* titleLabelContainer = [[UIView alloc] init];
titleLabelContainer.translatesAutoresizingMaskIntoConstraints = NO;
titleLabelContainer.clipsToBounds = YES;
[titleLabelContainer addSubview:titleLabel];
[_headerView addSubview:titleLabelContainer];
[NSLayoutConstraint activateConstraints:@[
[titleLabelContainer.heightAnchor
constraintLessThanOrEqualToAnchor:_headerView.heightAnchor],
[titleLabelContainer.centerYAnchor
constraintEqualToAnchor:_faviconContainerView.centerYAnchor],
[titleLabelContainer.leadingAnchor
constraintEqualToAnchor:_faviconContainerView.trailingAnchor
constant:kPinnedCellTitleLeadingPadding],
[titleLabelContainer.trailingAnchor
constraintEqualToAnchor:_headerView.trailingAnchor
constant:-kPinnedCellHorizontalPadding],
[titleLabelContainer.heightAnchor
constraintEqualToAnchor:titleLabel.heightAnchor],
[titleLabelContainer.centerYAnchor
constraintEqualToAnchor:titleLabel.centerYAnchor]
]];
_titleLabelLeadingConstraint = [titleLabelContainer.leadingAnchor
constraintEqualToAnchor:titleLabel.leadingAnchor];
_titleLabelTrailingConstraint = [titleLabelContainer.trailingAnchor
constraintEqualToAnchor:titleLabel.trailingAnchor];
_titleLabelContainer = titleLabelContainer;
_titleLabel = titleLabel;
[self updateTitleLabelAppearance];
}
// Sets up the gradient view that fades out the title label.
- (void)setupTitleLabelFader {
UIColor* backgroundColor = GetInterfaceStyleDarkColor(
[UIColor colorNamed:kSecondaryBackgroundColor]);
UIColor* transparentColor = [backgroundColor colorWithAlphaComponent:0.0f];
UIColor* opaqueColor = [backgroundColor colorWithAlphaComponent:1.0f];
GradientView* gradientView =
[[GradientView alloc] initWithStartColor:transparentColor
endColor:opaqueColor
startPoint:CGPointMake(0.0f, 0.5f)
endPoint:CGPointMake(1.0f, 0.5f)];
gradientView.translatesAutoresizingMaskIntoConstraints = NO;
[_headerView addSubview:gradientView];
[NSLayoutConstraint activateConstraints:@[
[gradientView.widthAnchor
constraintEqualToConstant:kPinnedCellFaderGradientWidth],
[gradientView.heightAnchor
constraintEqualToAnchor:_titleLabelContainer.heightAnchor],
[gradientView.centerYAnchor
constraintEqualToAnchor:_titleLabelContainer.centerYAnchor],
]];
_titleLabelFaderLeadingConstraint = [gradientView.leadingAnchor
constraintEqualToAnchor:_titleLabelContainer.leadingAnchor];
_titleLabelFaderTrailingConstraint = [gradientView.trailingAnchor
constraintEqualToAnchor:_titleLabelContainer.trailingAnchor];
_titleLabelFader = gradientView;
[self updateTitleLabelFaderAppearance];
}
- (void)updateTitleLabelAppearance {
NSTextAlignment titleTextAligment = _titleLabel.textAlignment;
if (UseRTLLayout()) {
if (titleTextAligment == NSTextAlignmentLeft) {
_titleLabelLeadingConstraint.active = NO;
_titleLabelTrailingConstraint.active = YES;
} else {
_titleLabelTrailingConstraint.active = NO;
_titleLabelLeadingConstraint.active = YES;
}
} else {
if (titleTextAligment == NSTextAlignmentLeft) {
_titleLabelTrailingConstraint.active = NO;
_titleLabelLeadingConstraint.active = YES;
} else {
_titleLabelLeadingConstraint.active = NO;
_titleLabelTrailingConstraint.active = YES;
}
}
}
// Updates the position and direction of the gradient view that fades out the
// title label.
- (void)updateTitleLabelFaderAppearance {
NSTextAlignment titleTextAligment = _titleLabel.textAlignment;
if (UseRTLLayout()) {
if (titleTextAligment == NSTextAlignmentLeft) {
[_titleLabelFader setTransform:CGAffineTransformMakeScale(1, 1)];
_titleLabelFaderTrailingConstraint.active = NO;
_titleLabelFaderLeadingConstraint.active = YES;
} else {
[_titleLabelFader setTransform:CGAffineTransformMakeScale(-1, 1)];
_titleLabelFaderLeadingConstraint.active = NO;
_titleLabelFaderTrailingConstraint.active = YES;
}
} else {
if (titleTextAligment == NSTextAlignmentLeft) {
[_titleLabelFader setTransform:CGAffineTransformMakeScale(1, 1)];
_titleLabelFaderLeadingConstraint.active = NO;
_titleLabelFaderTrailingConstraint.active = YES;
} else {
[_titleLabelFader setTransform:CGAffineTransformMakeScale(-1, 1)];
_titleLabelFaderTrailingConstraint.active = NO;
_titleLabelFaderLeadingConstraint.active = YES;
}
}
}
- (NSString*)accessibilityLabelWithTitle:(NSString*)title {
return l10n_util::GetNSStringF(IDS_IOS_PINNED_TAB_ACCESSIBILITY_LABEL,
base::SysNSStringToUTF16(title));
}
@end
// TODO(crbug.com/40890700): Refacor PinnedTransitionCell.
@implementation PinnedTransitionCell {
// Previous tab view width, used to scale the tab views.
CGFloat _previousTabViewWidth;
}
// Synthesis of GridToTabTransitionView properties.
@synthesize topTabView = _topTabView;
@synthesize mainTabView = _mainTabView;
@synthesize bottomTabView = _bottomTabView;
+ (instancetype)transitionCellFromCell:(PinnedCell*)cell {
PinnedTransitionCell* proxy = [[self alloc] initWithFrame:cell.bounds];
proxy.selected = NO;
proxy.icon = cell.icon;
proxy.title = cell.title;
proxy.snapshot = cell.snapshot;
return proxy;
}
#pragma mark - GridToTabTransitionView properties.
- (void)setTopCellView:(UIView*)topCellView {
// The top cell view is `topBar` and can't be changed.
NOTREACHED_IN_MIGRATION();
}
- (UIView*)topCellView {
return self.headerView;
}
- (void)setTopTabView:(UIView*)topTabView {
DCHECK(!_topTabView) << "topTabView should only be set once.";
if (!topTabView.superview) {
[self.contentView addSubview:topTabView];
}
_topTabView = topTabView;
}
- (void)setMainCellView:(UIView*)mainCellView {
// The main cell view is the snapshot view and can't be changed.
NOTREACHED_IN_MIGRATION();
}
- (UIView*)mainCellView {
return self.snapshotView;
}
- (void)setMainTabView:(UIView*)mainTabView {
DCHECK(!_mainTabView) << "mainTabView should only be set once.";
if (!mainTabView.superview) {
[self.contentView addSubview:mainTabView];
}
_previousTabViewWidth = mainTabView.frame.size.width;
_mainTabView = mainTabView;
}
- (void)setBottomTabView:(UIView*)bottomTabView {
DCHECK(!_bottomTabView) << "bottomTabView should only be set once.";
if (!bottomTabView.superview) {
[self.contentView addSubview:bottomTabView];
}
_bottomTabView = bottomTabView;
}
- (CGFloat)cornerRadius {
return self.contentView.layer.cornerRadius;
}
- (void)setCornerRadius:(CGFloat)radius {
self.contentView.layer.cornerRadius = radius;
}
#pragma mark - GridToTabTransitionView methods
- (void)prepareForTransitionWithAnimationDirection:
(GridAnimationDirection)animationDirection {
switch (animationDirection) {
case GridAnimationDirectionContracting:
[self prepareForContractingAnimation];
break;
case GridAnimationDirectionExpanding:
[self prepareForExpandingAnimation];
break;
}
}
- (void)positionTabViews {
[self scaleTabViews];
self.snapshotViewTopConstraint.constant = self.topTabView.frame.size.height;
self.headerViewHeightConstraint.constant = self.topTabView.frame.size.height;
self.faviconContainerViewCenterYConstraint.constant =
self.topTabView.frame.size.height / 2;
[self setNeedsUpdateConstraints];
[self layoutIfNeeded];
PositionView(self.topTabView, CGPointMake(0, 0));
// Position the main view so it's top-aligned with the main cell view.
PositionView(self.mainTabView, self.mainCellView.frame.origin);
if (!self.bottomTabView) {
return;
}
// Position the bottom tab view at the bottom.
CGFloat yPosition = CGRectGetMaxY(self.contentView.bounds) -
self.bottomTabView.frame.size.height;
PositionView(self.bottomTabView, CGPointMake(0, yPosition));
}
- (void)positionCellViews {
[self scaleTabViews];
self.snapshotViewTopConstraint.constant = kPinnedCellSnapshotTopPadding;
self.faviconContainerViewCenterYConstraint.constant = kPinnedCellHeight / 2;
[self setNeedsUpdateConstraints];
[self layoutIfNeeded];
PositionView(self.topTabView, CGPointMake(0, 0));
// Position the main view so it's top-aligned with the main cell view.
PositionView(self.mainTabView, self.mainCellView.frame.origin);
if (!self.bottomTabView) {
return;
}
if (self.bottomTabView.frame.origin.y > 0) {
// Position the bottom tab so it's equivalently located.
PositionView(self.bottomTabView,
CGPointMake(0, CGRectGetMaxY(self.mainCellView.frame) +
CGRectGetHeight(self.mainCellView.frame)));
} else {
// Position the bottom tab view below the main content view.
CGFloat bottomYOffset = CGRectGetMaxY(self.mainCellView.frame);
PositionView(self.bottomTabView, CGPointMake(0, bottomYOffset));
}
}
#pragma mark - Private helper methods
// Prepares the cell for contracting animation.
- (void)prepareForContractingAnimation {
[self preapreForAnimation];
}
// Prepares the cell for expanding animation.
- (void)prepareForExpandingAnimation {
[self preapreForAnimation];
self.headerView.backgroundColor =
[UIColor colorNamed:kSecondaryBackgroundColor];
self.titleLabel.textColor = [UIColor colorNamed:kTextPrimaryColor];
self.titleLabelFader.hidden = YES;
}
// Common logic for the cell animation preparation.
- (void)preapreForAnimation {
// Remove dark corners from the transition animtation cell.
self.backgroundColor = [UIColor clearColor];
// Pinned transition cell does the transition to the Tab view, which uses
// not static but dynamic colors. Therefore, in order to match the background
// colors of the Tab view, all pinned transtion cell colors must be dynamic.
self.contentView.backgroundColor =
[UIColor colorNamed:kSecondaryBackgroundColor];
self.snapshotView.hidden = NO;
}
// Scales the tab views relative to the current width of the cell.
- (void)scaleTabViews {
DUMP_WILL_BE_CHECK_NE(_previousTabViewWidth, 0);
CGFloat scale = self.bounds.size.width / _previousTabViewWidth;
ScaleView(self.topTabView, scale);
ScaleView(self.mainTabView, scale);
ScaleView(self.bottomTabView, scale);
_previousTabViewWidth = self.mainTabView.frame.size.width;
}
@end