Commit 9427879e authored by Mark Cogan's avatar Mark Cogan Committed by Commit Bot

[iOS] Further refactor tab grid transition animation.

This CL further refactors the grid animation in preparation for
supporting a reversable version. Each of the four primary steps in the
animation (positioning and scaling the selected and unselected cells in
both the regular and expanded positions) is extracted into a separate
method, and common code in those methods is further extracted to reduce
repitition.

Bug: 804539
Cq-Include-Trybots: master.tryserver.chromium.mac:ios-simulator-cronet;master.tryserver.chromium.mac:ios-simulator-full-configs
Change-Id: Ibabd01e9f01fb58198bcd14f39536a77dcdf2c39
Reviewed-on: https://chromium-review.googlesource.com/975462
Commit-Queue: Mark Cogan <marq@chromium.org>
Reviewed-by: default avataredchin <edchin@chromium.org>
Cr-Commit-Position: refs/heads/master@{#545088}
parent d3711b44
......@@ -13,12 +13,18 @@
@interface GridTransitionAnimation ()
@property(nonatomic, strong) GridTransitionLayout* layout;
@property(nonatomic, weak) id<GridTransitionAnimationDelegate> delegate;
@property(nonatomic, assign) CGFloat xScale;
@property(nonatomic, assign) CGFloat yScale;
@property(nonatomic, readonly) CGSize selectedSize;
@property(nonatomic, readonly) CGPoint selectedCenter;
@property(nonatomic, assign) CGFloat finalSelectedCellCornerRadius;
@end
@implementation GridTransitionAnimation
@synthesize delegate = _delegate;
@synthesize layout = _layout;
@synthesize xScale = _xScale;
@synthesize yScale = _yScale;
@synthesize finalSelectedCellCornerRadius = _finalSelectedCellCornerRadius;
- (instancetype)initWithLayout:(GridTransitionLayout*)layout
......@@ -26,10 +32,14 @@
if (self = [super initWithFrame:CGRectZero]) {
_layout = layout;
_delegate = delegate;
_finalSelectedCellCornerRadius =
_layout.selectedItem.cell.contentView.layer.cornerRadius;
}
return self;
}
#pragma mark - UIView
- (void)willMoveToSuperview:(UIView*)newSuperview {
self.frame = newSuperview.bounds;
if (newSuperview && self.subviews.count == 0) {
......@@ -37,44 +47,25 @@
}
}
- (void)prepareForAnimationInSuperview:(UIView*)newSuperview {
// Extract some useful metrics from the animation superview.
CGSize animationSize = newSuperview.bounds.size;
CGPoint animationCenter = newSuperview.center;
- (void)didMoveToSuperview {
// Positioning the animating items depends on converting points to this
// view's coordinate system, so wait until it's in a view hierarchy.
[self positionSelectedItemInExpandedGrid];
[self positionUnselectedItemsInExpandedGrid];
}
// Shorthand for selected item attributes.
CGSize selectedSize = self.layout.selectedItem.attributes.size;
CGPoint selectedCenter = self.layout.selectedItem.attributes.center;
#pragma mark - Private Properties
// Compute the scale of the transition grid (which is at the propotional size
// of the superview).
CGFloat xScale = animationSize.width / selectedSize.width;
CGFloat yScale = animationSize.height / selectedSize.height;
- (CGSize)selectedSize {
return self.layout.selectedItem.attributes.size;
}
// Lay out the transition grid add it as subviews.
for (GridTransitionLayoutItem* item in self.layout.items) {
// The state provider vends attributes in UIWindow coordinates.
// Find where this item is located in |newSuperview|'s coordinates.
CGPoint gridCenter =
[newSuperview convertPoint:item.attributes.center fromView:nil];
// Map that to the scale and position of the transition grid.
CGPoint center = CGPointMake(
animationCenter.x + ((gridCenter.x - selectedCenter.x) * xScale),
animationCenter.y + ((gridCenter.y - selectedCenter.y) * yScale));
UICollectionViewCell* cell = item.cell;
cell.bounds = item.attributes.bounds;
// Add a scale transform to the cell so it matches the x-scale of the
// open tab.
cell.transform = CGAffineTransformScale(cell.transform, xScale, xScale);
cell.center = center;
if (item == self.layout.selectedItem) {
self.finalSelectedCellCornerRadius = cell.contentView.layer.cornerRadius;
cell.contentView.layer.cornerRadius = 0.0;
}
[self addSubview:cell];
}
- (CGPoint)selectedCenter {
return self.layout.selectedItem.attributes.center;
}
#pragma mark - Public methods
- (void)animateWithDuration:(NSTimeInterval)duration {
// The transition is structured as two or three separate animations. They are
// timed based on |staggeredDuration|, which is a configurable fraction
......@@ -119,17 +110,6 @@
}
completion:nil];
// Animation for (B): zoom the selected cell into place and round its corners.
auto selectedCellAnimation = ^{
selectedCell.center =
[self.superview convertPoint:self.layout.selectedItem.attributes.center
fromView:nil];
selectedCell.bounds = self.layout.selectedItem.attributes.bounds;
selectedCell.transform = CGAffineTransformIdentity;
selectedCell.contentView.layer.cornerRadius =
self.finalSelectedCellCornerRadius;
};
// Completion block to be run when the transition completes.
auto completion = ^(BOOL finished) {
// Tell the delegate the animation has completed.
......@@ -142,7 +122,9 @@
[UIView animateWithDuration:duration
delay:0
options:UIViewAnimationOptionCurveEaseOut
animations:selectedCellAnimation
animations:^{
[self positionSelectedItemInRegularGrid];
}
completion:completion];
} else {
// Multiple cell case.
......@@ -150,31 +132,107 @@
[UIView animateWithDuration:staggeredDuration
delay:0.0
options:UIViewAnimationOptionCurveEaseOut
animations:selectedCellAnimation
animations:^{
[self positionSelectedItemInRegularGrid];
}
completion:nil];
// Animation for (C): zoom other cells into place.
auto unselectedCellsAnimation = ^{
for (GridTransitionLayoutItem* item in self.layout.items) {
if (item == self.layout.selectedItem)
continue;
UIView* cell = item.cell;
// The state provider vends attributes in UIWindow coordinates.
// Find where this item is located in |newSuperview|'s coordinates.
cell.center =
[self.superview convertPoint:item.attributes.center fromView:nil];
cell.transform = CGAffineTransformIdentity;
}
};
// Run animation (C) for |staggeredDuration| up to the end of the
// transition.
[UIView animateWithDuration:staggeredDuration
delay:duration - staggeredDuration
options:UIViewAnimationOptionCurveEaseOut
animations:unselectedCellsAnimation
animations:^{
[self positionUnselectedItemsInRegularGrid];
}
completion:completion];
}
}
#pragma mark - Private methods
// Perfrom the initial setup for the animation, computing scale based on the
// superview size and adding the transition cells to the view hierarchy.
- (void)prepareForAnimationInSuperview:(UIView*)newSuperview {
// Extract some useful metrics from the animation superview.
CGSize animationSize = newSuperview.bounds.size;
// Compute the scale of the transition grid (which is at the proportional size
// of the superview).
self.xScale = animationSize.width / self.selectedSize.width;
self.yScale = animationSize.height / self.selectedSize.height;
for (GridTransitionLayoutItem* item in self.layout.items) {
[self addSubview:item.cell];
}
}
// Positions the selected item in the expanded grid position with a zero corner
// radius.
- (void)positionSelectedItemInExpandedGrid {
[self positionAndScaleItemInExpandedGrid:self.layout.selectedItem];
UICollectionViewCell* cell = self.layout.selectedItem.cell;
cell.contentView.layer.cornerRadius = 0.0;
}
// Positions all of the non-selected items in their expanded grid positions.
- (void)positionUnselectedItemsInExpandedGrid {
// Lay out the transition grid add it as subviews.
for (GridTransitionLayoutItem* item in self.layout.items) {
if (item == self.layout.selectedItem)
continue;
[self positionAndScaleItemInExpandedGrid:item];
}
}
// Positions the selected item in the regular grid position with its final
// corner radius.
- (void)positionSelectedItemInRegularGrid {
[self positionAndScaleItemInRegularGrid:self.layout.selectedItem];
UICollectionViewCell* cell = self.layout.selectedItem.cell;
cell.contentView.layer.cornerRadius = self.finalSelectedCellCornerRadius;
}
// Positions all of the non-selected items in their regular grid positions.
- (void)positionUnselectedItemsInRegularGrid {
for (GridTransitionLayoutItem* item in self.layout.items) {
if (item == self.layout.selectedItem)
continue;
[self positionAndScaleItemInRegularGrid:item];
}
}
// Positions |item| in its regular grid position.
- (void)positionAndScaleItemInRegularGrid:(GridTransitionLayoutItem*)item {
UIView* cell = item.cell;
cell.center =
[self.superview convertPoint:item.attributes.center fromView:nil];
cell.transform = CGAffineTransformIdentity;
}
// Positions |item| in its expanded grid position.
- (void)positionAndScaleItemInExpandedGrid:(GridTransitionLayoutItem*)item {
UICollectionViewCell* cell = item.cell;
cell.bounds = item.attributes.bounds;
// Add a scale transform to the cell so it matches the x-scale of the
// open tab. Scaling is only based on the x-scale so that the aspect ratio of
// the cell will be preserved.
cell.transform =
CGAffineTransformScale(cell.transform, self.xScale, self.xScale);
cell.center = [self expandedCenterForItem:item];
}
// Returns the center point for an item in the expanded grid position. This is
// computed by scaling its center point relative to the selected item's center
// point. The scaling factors are the ratios of the animation view's height and
// width to the selected cell's height and width.
- (CGPoint)expandedCenterForItem:(GridTransitionLayoutItem*)item {
// Convert item center from window coordinates.
CGPoint gridCenter = [self convertPoint:item.attributes.center fromView:nil];
// Map that to the scale and position of the transition grid.
return CGPointMake(
self.center.x + ((gridCenter.x - self.selectedCenter.x) * self.xScale),
self.center.y + ((gridCenter.y - self.selectedCenter.y) * self.yScale));
}
@end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment