Commit 602d2720 authored by Mark Cogan's avatar Mark Cogan Committed by Commit Bot

[iOS] Merge tab grid transitions with tab views.

This CL updates the tab grid transitions to cleanly match the presented tab views.

The basic abstraction for this transition is the GridToTabTransitionView protocol which defines a set of 'cell' and 'tab' views, and methods to lay them out. The general idea is that a view conforming to this protocol can either be in 'cell' mode (where it exactly matches a tab grid cell), or in 'tab' mode (where it exactly matches an open tab).

Most of the work is done by the GridTransitionCell subclass of GridCell, which positions and scales the "tab" and "cell" views it is supplied with so they animate smoothly. All of this positioning/scaling logic is internal to that class and nothing else depends on it.

Providing the transition cell with the tab views is handled in the transition animator, mostly leveraging the content area layout guide. This doesn't require any direct integration with the tab view (that is, the BVC) beyond that layout guide.

Approaches I abandoned:

- letting -layoutSubviews position the tab/grid views. The problem with this approach (which is much cleaner from an API perspective) is that the timing of the layout passes is unpredictable, it it resulted in changes being made that weren't fully animated (for example, subviews would be scaled and positioned without animation, and then the superview would scale with an animation).

- using layout constraints for GridTransitionCell's tab views. This is probably the correct thing to do long-term; mostly this was a decision made under time constraints, but since ultimately animating constraint changes depends on -layoutSubviews being called at the right times, I did have some concerns given the difficulties described above. It's unfortunate that grid_cell.mm is mixing constraint- and frame-based layout, but it is confined to that single file.

Bug: 851460, 850507, 862343
Cq-Include-Trybots: luci.chromium.try:ios-simulator-full-configs;master.tryserver.chromium.mac:ios-simulator-cronet
Change-Id: If6d55f14aa05e705143547b61773fdb34796ad2d
Reviewed-on: https://chromium-review.googlesource.com/1136647
Commit-Queue: Mark Cogan <marq@chromium.org>
Reviewed-by: default avatarGauthier Ambard <gambard@chromium.org>
Cr-Commit-Position: refs/heads/master@{#575590}
parent 0922d466
......@@ -15,7 +15,33 @@
#error "This file requires ARC support."
#endif
namespace {
// 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;
}
// 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;
}
} // namespace
@interface GridCell ()
// Header height of the cell.
@property(nonatomic, strong) NSLayoutConstraint* topBarHeight;
// Visual components of the cell.
@property(nonatomic, weak) UIView* topBar;
@property(nonatomic, weak) UIImageView* iconView;
......@@ -36,8 +62,9 @@
@synthesize icon = _icon;
@synthesize snapshot = _snapshot;
@synthesize title = _title;
@synthesize topBar = _topBar;
// Private properties.
@synthesize topBarHeight = _topBarHeight;
@synthesize topBar = _topBar;
@synthesize iconView = _iconView;
@synthesize snapshotView = _snapshotView;
@synthesize titleLabel = _titleLabel;
......@@ -74,12 +101,15 @@
_snapshotView = snapshotView;
_closeTapTargetButton = closeTapTargetButton;
_topBarHeight =
[topBar.heightAnchor constraintEqualToConstant:kGridCellHeaderHeight];
NSArray* constraints = @[
[topBar.topAnchor constraintEqualToAnchor:contentView.topAnchor],
[topBar.leadingAnchor constraintEqualToAnchor:contentView.leadingAnchor],
[topBar.trailingAnchor
constraintEqualToAnchor:contentView.trailingAnchor],
[topBar.heightAnchor constraintEqualToConstant:kGridCellHeaderHeight],
_topBarHeight,
[snapshotView.topAnchor constraintEqualToAnchor:topBar.bottomAnchor],
[snapshotView.leadingAnchor
constraintEqualToAnchor:contentView.leadingAnchor],
......@@ -303,7 +333,15 @@
@end
@implementation GridTransitionCell
@implementation GridTransitionCell {
// 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:(GridCell*)cell {
GridTransitionCell* proxy = [[self alloc] initWithFrame:cell.bounds];
......@@ -314,11 +352,10 @@
proxy.title = cell.title;
return proxy;
}
#pragma mark - GridToTabTransitionView
#pragma mark - GridToTabTransitionView properties.
- (void)setTopCellView:(UIView*)topCellView {
// The top cell is the top bar and can't be changed.
// The top cell view is |topBar| and can't be changed.
NOTREACHED();
}
......@@ -326,6 +363,37 @@
return self.topBar;
}
- (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();
}
- (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;
}
......@@ -334,4 +402,58 @@
self.contentView.layer.cornerRadius = radius;
}
#pragma mark - GridToTabTransitionView methods
- (void)positionTabViews {
[self scaleTabViews];
self.topBarHeight.constant = self.topTabView.frame.size.height;
[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.topBarHeight.constant = kGridCellHeaderHeight;
[self setNeedsUpdateConstraints];
[self layoutIfNeeded];
CGFloat yOffset = kGridCellHeaderHeight - self.topTabView.frame.size.height;
PositionView(self.topTabView, CGPointMake(0, yOffset));
// 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.
CGFloat scale = self.bounds.size.width / _previousTabViewWidth;
PositionView(self.bottomTabView,
CGPointMake(0, self.bottomTabView.frame.origin.y * scale));
} else {
// Position the bottom tab view below the main content view.
CGFloat yOffset = CGRectGetMaxY(self.mainCellView.frame);
PositionView(self.bottomTabView, CGPointMake(0, yOffset));
}
}
#pragma mark - Private helper methods
// Scales the tab views relative to the current width of the cell.
- (void)scaleTabViews {
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
......@@ -137,6 +137,10 @@ NSUInteger GetPageIndexFromPage(TabGridPage page) {
[self setupTopToolbar];
[self setupBottomToolbar];
[self setupFloatingButton];
// Hide the toolbars and the floating button, so they can fade in the first
// time there's a transition into this view controller.
[self hideToolbars];
}
- (void)viewWillAppear:(BOOL)animated {
......@@ -702,18 +706,20 @@ NSUInteger GetPageIndexFromPage(TabGridPage page) {
kTabGridCloseAllButtonIdentifier;
}
// Shows (by setting the alpha to 1.0) the two toolbar views. Suitable for use
// in animations.
// Shows (by setting the alpha to 1.0) the two toolbar views and the floating
// button. Suitable for use in animations.
- (void)showToolbars {
self.topToolbar.alpha = 1.0;
self.bottomToolbar.alpha = 1.0;
self.floatingButton.alpha = 1.0;
}
// Hides (by setting the alpha to 0.0) the two toolbar views. Suitable for use
// in animations.
// Hides (by setting the alpha to 0.0) the two toolbar views and the floating
// button. Suitable for use in animations.
- (void)hideToolbars {
self.topToolbar.alpha = 0.0;
self.bottomToolbar.alpha = 0.0;
self.floatingButton.alpha = 0.0;
}
// Translates the toolbar views offscreen and then animates them back in using
......
......@@ -7,18 +7,36 @@
#import <UIKit/UIKit.h>
// A protocol to be adopted by a view that will be provided for the transition
// animation. This view will need to be animated between 'cell' and 'tab'
// states, and will need to have a number of properties available for that
// purpose.
// An collection of properties and methods a view must support in order to be
// used to animate the transition between a grid cell and a browser tab.
@protocol GridToTabTransitionView
// The subview at the top of the view in 'cell' state.
@property(nonatomic, strong) UIView* topCellView;
// The subview at the top of the view in 'tab' state.
@property(nonatomic, strong) UIView* topTabView;
// The subview containing the main content in 'cell' state.
@property(nonatomic, strong) UIView* mainCellView;
// The subview containing the main content in 'tab' state.
@property(nonatomic, strong) UIView* mainTabView;
// The subview at the bottom of the view in 'tab' state.
@property(nonatomic, strong) UIView* bottomTabView;
// The corner radius of the view.
@property(nonatomic) CGFloat cornerRadius;
// Tells the view to scale and position its subviews for the "tab" layout. This
// must be able to be called inside an animation block.
- (void)positionTabViews;
// Tells the view to scale and position its subviews for the "cell" layout. This
// must be able to be called inside an animation block.
- (void)positionCellViews;
@end
#endif // IOS_CHROME_BROWSER_UI_TAB_GRID_TRANSITIONS_GRID_TO_TAB_TRANSITION_VIEW_H_
......@@ -85,8 +85,12 @@
CGRect finalRect =
[NamedGuide guideWithName:kContentAreaGuide view:viewWithNamedGuides]
.layoutFrame;
layout.expandedRect =
[proxyContainer convertRect:finalRect fromView:viewWithNamedGuides];
[layout.activeItem populateWithSnapshotsFromView:viewWithNamedGuides
middleRect:finalRect];
layout.expandedRect = [proxyContainer convertRect:viewWithNamedGuides.frame
fromView:presentedView];
NSTimeInterval duration = [self transitionDuration:transitionContext];
// Create the animation view and insert it.
......
......@@ -90,7 +90,7 @@ const CGFloat kInactiveItemScale = 0.95;
#pragma mark - Private methods
- (void)buildContractingAnimations {
// The transition is structured as two or four separate animations. They are
// The transition is structured as three or five separate animations. They are
// timed based on various sub-durations and delays which are expressed as
// fractions of the overall animation duration.
CGFloat partialDuration = 0.6;
......@@ -100,11 +100,11 @@ const CGFloat kInactiveItemScale = 0.95;
// If there's only one cell, the animation has two parts.
// (A) Zooming the active cell into position.
// (B) Fading in the active cell's auxilliary view.
// (B) Crossfading from the tab to cell top view.
// (C) Rounding the corners of the active cell.
//
// {0%}------------------[A]----------{60%}
// {40%}--[B]---------------{70%}
// {50%}---[B]---{80%}
// {0%}---[C]---{30%}
// If there's more than once cell, the animation adds two more parts:
......@@ -113,16 +113,24 @@ const CGFloat kInactiveItemScale = 0.95;
// The overall timing is as follows:
//
// {0%}------------------[A]----------{60%}
// {40%}--[B]---------------{70%}
// {50%}---[B]---{80%}
// {0%}---[C]---{30%}
// {20%}--[D]-------------------------{100%}
// {20%}--[E]----------------------{80%}
// {20%}--[D]-----------------------------{100%}
// {20%}--[E]-------------------------{80%}
//
// All animations are timed ease-in (so more motion happens later), except
// for C which is relatively small in space and short in duration; it has
// linear timing so it doesn't seem instantaneous.
// for B and C. B is a crossfade timed ease in/out, and C is relatively small
// in space and short in duration; it has linear timing so it doesn't seem
// instantaneous.
// (Changing the timing constants above will change the timing % values)
UIView<GridToTabTransitionView>* activeCell = self.layout.activeItem.cell;
// The final cell snapshot exactly matches the main tab view of the cell, so
// it can have an alpha of 0 for the whole animation.
activeCell.mainTabView.alpha = 0.0;
// The final cell header starts at 0 alpha and is cross-faded in.
activeCell.topCellView.alpha = 0.0;
// A: Zoom the active cell into position.
auto zoomActiveCellAnimation = ^{
[self positionAndScaleActiveItemInGrid];
......@@ -137,17 +145,17 @@ const CGFloat kInactiveItemScale = 0.95;
animations:zoomActiveCellKeyframeAnimation];
[self.animations addAnimator:zoomActiveCell];
// B: Fade in the active cell's auxillary view
UIView* auxillaryView = self.layout.activeItem.cell.topCellView;
// B: Fade in the active cell top cell view, fade out the active cell's
// top tab view.
auto fadeInAuxillaryKeyframeAnimation =
[self keyframeAnimationWithRelativeStart:delay
relativeDuration:briefDuration
animations:^{
auxillaryView.alpha = 1.0;
}];
[self keyframeAnimationFadingView:activeCell.topTabView
throughToView:activeCell.topCellView
relativeStart:0.5
relativeDuration:briefDuration];
UIViewPropertyAnimator* fadeInAuxillary = [[UIViewPropertyAnimator alloc]
initWithDuration:self.duration
curve:UIViewAnimationCurveEaseIn
curve:UIViewAnimationCurveEaseInOut
animations:fadeInAuxillaryKeyframeAnimation];
[self.animations addAnimator:fadeInAuxillary];
......@@ -207,36 +215,53 @@ const CGFloat kInactiveItemScale = 0.95;
}
- (void)buildExpandingAnimations {
// The transition is structured as two or four separate animations. They are
// The transition is structured as four or six separate animations. They are
// timed based on two sub-durations which are expressed as fractions of the
// overall animation duration.
CGFloat partialDuration = 0.66;
CGFloat briefDuration = 0.23;
CGFloat briefDuration = 0.3;
CGFloat veryBriefDuration = 0.2;
CGFloat delay = (1.0 - veryBriefDuration) / 2.0;
// If there's only one cell, the animation has three parts:
// (A) Zooming the active cell out into the expanded position.
// (B) Fading out the active cell's auxilliary view.
// (B) Crossfading the active cell's top views.
// (C) Squaring the corners of the active cell.
// (D) Fading out the main cell view and fading in the main tab view.
// These parts are timed over |duration| like this:
//
// {0%}--[A]-----------------------------------{100%}
// {0%}--[B]--{23%}
// {77%}---[C]---{100%}
// {0%}--[B]---{30%}
// {70%}----[C]---{100%}
// {40%}-[D]-{60%}
// If there's more than once cell, the animation adds:
// (C) Scaling the inactive cells to 95%
// (D) Fading out the inactive cells.
// (E) Scaling the inactive cells to 95%
// (F) Fading out the inactive cells.
// The overall timing is as follows:
//
// {0%}--[A]-----------------------------------{100%}
// {0%}--[B]--{23%}
// {77%}---[C]---{100%}
// {0%}--[D]-----------------------------------{100%}
// {0%}--[E]-----------------{66%}
// {0%}--[B]---{30%}
// {70%}----[C]---{100%}
// {40%}-[D]-{60%}
// {0%}--[E]-----------------------------------{100%}
// {0%}--[F]-------------------{66%}
//
// All animations are timed ease-out (so more motion happens sooner), except
// for C which is relatively small in space and short in duration; it has
// linear timing so it doesn't seem instantaneous.
// for B, C and D. B is a crossfade and eases in/out. C and D are relatively
// short in duration; they have linear timing so they doesn't seem
// instantaneous, and D is also linear so that identical views animate
// smoothly.
//
// Animation D is necessary because the cell content and the tab content may
// no longer match in aspect ratio; a quick cross-fade in mid-transition
// prevents an abrupt jump when the transition ends and the "real" tab content
// is shown.
UIView<GridToTabTransitionView>* activeCell = self.layout.activeItem.cell;
// The top and main tab views start at zero alpha but are crossfaded in.
activeCell.mainTabView.alpha = 0.0;
activeCell.topTabView.alpha = 0.0;
// A: Zoom the active cell into position.
UIViewPropertyAnimator* zoomActiveCell = [[UIViewPropertyAnimator alloc]
......@@ -247,17 +272,15 @@ const CGFloat kInactiveItemScale = 0.95;
}];
[self.animations addAnimator:zoomActiveCell];
// B: Fade out the active cell's auxillary view.
UIView* auxillaryView = self.layout.activeItem.cell.topCellView;
// B: Crossfade the top views.
auto fadeOutAuxilliaryAnimation =
[self keyframeAnimationWithRelativeStart:0
relativeDuration:briefDuration
animations:^{
auxillaryView.alpha = 0.0;
}];
[self keyframeAnimationFadingView:activeCell.topCellView
throughToView:activeCell.topTabView
relativeStart:0
relativeDuration:briefDuration];
UIViewPropertyAnimator* fadeOutAuxilliary = [[UIViewPropertyAnimator alloc]
initWithDuration:self.duration
curve:UIViewAnimationCurveEaseOut
curve:UIViewAnimationCurveEaseInOut
animations:fadeOutAuxilliaryAnimation];
[self.animations addAnimator:fadeOutAuxilliary];
......@@ -276,12 +299,31 @@ const CGFloat kInactiveItemScale = 0.95;
animations:squareCornersKeyframeAnimation];
[self.animations addAnimator:squareCorners];
// D: crossfade the main cell content.
// Two notes on this transition. (1) In cases where the cell and tab views are
// the same, having both alphas change at the same time means the overall
// transition is seamless. (2) using a linear animation curve means that the
// sum of the opacities is contstant though the animation, which will help it
// seem less abrupt by keeping a relatively constant brightness.
auto crossfadeContentAnimation =
[self keyframeAnimationWithRelativeStart:delay
relativeDuration:veryBriefDuration
animations:^{
activeCell.mainCellView.alpha = 0;
activeCell.mainTabView.alpha = 1.0;
}];
UIViewPropertyAnimator* crossfadeContent = [[UIViewPropertyAnimator alloc]
initWithDuration:self.duration
curve:UIViewAnimationCurveLinear
animations:crossfadeContentAnimation];
[self.animations addAnimator:crossfadeContent];
// If there's only a single cell, that's all.
if (self.layout.inactiveItems.count == 0)
return;
// Additional animations for multiple cells.
// D: Scale down inactive cells.
// E: Scale down inactive cells.
auto scaleDownCellsAnimation = ^{
for (GridTransitionItem* item in self.layout.inactiveItems) {
item.cell.transform = CGAffineTransformScale(
......@@ -294,7 +336,7 @@ const CGFloat kInactiveItemScale = 0.95;
animations:scaleDownCellsAnimation];
[self.animations addAnimator:scaleDownCells];
// E: Fade out inactive cells.
// F: Fade out inactive cells.
auto fadeOutCellsAnimation = ^{
for (GridTransitionItem* item in self.layout.inactiveItems) {
item.cell.alpha = 0.0;
......@@ -329,18 +371,8 @@ const CGFloat kInactiveItemScale = 0.95;
// radius and a 0% opacity auxilliary view.
- (void)positionExpandedActiveItem {
UIView<GridToTabTransitionView>* cell = self.layout.activeItem.cell;
// Ensure that the cell's subviews are correctly positioned.
[cell layoutIfNeeded];
// Position the cell frame so so that the area below the aux view matches the
// expanded rect.
// Easiest way to do this is to set the frame to the expanded rect and then
// add height to it to include the aux view height.
CGFloat auxHeight = cell.topCellView.frame.size.height;
CGRect cellFrame = self.layout.expandedRect;
cellFrame.size.height += auxHeight;
cellFrame.origin.y -= auxHeight;
cell.frame = cellFrame;
cell.topCellView.alpha = 0.0;
cell.frame = self.layout.expandedRect;
[cell positionTabViews];
}
// Positions all of the inactive items in their grid positions.
......@@ -358,12 +390,13 @@ const CGFloat kInactiveItemScale = 0.95;
// Positions the active item in the regular grid position with its final
// corner radius.
- (void)positionAndScaleActiveItemInGrid {
UIView* cell = self.layout.activeItem.cell;
UIView<GridToTabTransitionView>* cell = self.layout.activeItem.cell;
cell.transform = CGAffineTransformIdentity;
CGRect frame = cell.frame;
frame.size = self.layout.activeItem.size;
cell.frame = frame;
[self positionItemInGrid:self.layout.activeItem];
[cell positionCellViews];
}
// Prepares all of the items for an expansion anumation.
......@@ -372,6 +405,7 @@ const CGFloat kInactiveItemScale = 0.95;
[self positionItemInGrid:item];
}
[self positionItemInGrid:self.layout.activeItem];
[self.layout.activeItem.cell positionCellViews];
[self positionItemInGrid:self.layout.selectionItem];
}
......@@ -404,4 +438,38 @@ const CGFloat kInactiveItemScale = 0.95;
};
}
// Returns a cross-fade keyframe animation between two views.
// |startView| should have an alpha of 1; |endView| should have an alpha of 0.
// |start| and |duration| are in the [0.0]-[1.0] interval and represent timing
// relative to |self.duration|.
// The animation returned by this method will fade |startView| to 0 over the
// first half of |duration|, and then fade |endView| to 1.0 over the second
// half, preventing any blurred frames showing both views. For best results, the
// animation curev should be EaseInEaseOut.
- (void (^)(void))keyframeAnimationFadingView:(UIView*)startView
throughToView:(UIView*)endView
relativeStart:(double)start
relativeDuration:(double)duration {
CGFloat halfDuration = duration / 2;
auto keyframes = ^{
[UIView addKeyframeWithRelativeStartTime:start
relativeDuration:halfDuration
animations:^{
startView.alpha = 0.0;
}];
[UIView addKeyframeWithRelativeStartTime:start + halfDuration
relativeDuration:halfDuration
animations:^{
endView.alpha = 1.0;
}];
};
return ^{
[UIView animateKeyframesWithDuration:self.duration
delay:0
options:UIViewAnimationOptionLayoutSubviews
animations:keyframes
completion:nil];
};
}
@end
......@@ -76,6 +76,11 @@
center:(CGPoint)center
size:(CGSize)size;
// Populate the |cell| view of the reciever by extracting snapshots from |view|,
// using |rect| to define (in |view|'s coordinates) the main tab view, with any
// space above and below |rect| being the top and bottom tab views.
- (void)populateWithSnapshotsFromView:(UIView*)view middleRect:(CGRect)rect;
@end
#endif // IOS_CHROME_BROWSER_UI_TAB_GRID_TRANSITIONS_GRID_TRANSITION_LAYOUT_H_
......@@ -4,6 +4,8 @@
#import "ios/chrome/browser/ui/tab_grid/transitions/grid_transition_layout.h"
#import "ios/chrome/browser/ui/tab_grid/transitions/grid_to_tab_transition_view.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
......@@ -71,4 +73,32 @@
return item;
}
- (void)populateWithSnapshotsFromView:(UIView*)view middleRect:(CGRect)rect {
self.cell.mainTabView = [view resizableSnapshotViewFromRect:rect
afterScreenUpdates:YES
withCapInsets:UIEdgeInsetsZero];
CGSize viewSize = view.bounds.size;
if (rect.origin.y > 0) {
// |rect| starts below the top of |view|, so section off the top part of
// |view|.
CGRect topRect = CGRectMake(0, 0, viewSize.width, rect.origin.y);
self.cell.topTabView =
[view resizableSnapshotViewFromRect:topRect
afterScreenUpdates:YES
withCapInsets:UIEdgeInsetsZero];
}
CGFloat middleRectBottom = CGRectGetMaxY(rect);
if (middleRectBottom < viewSize.height) {
// |rect| ends above the bottom of |view|, so section off the bottom part of
// |view|.
CGFloat bottomHeight = viewSize.height - middleRectBottom;
CGRect bottomRect =
CGRectMake(0, middleRectBottom, viewSize.width, bottomHeight);
self.cell.bottomTabView =
[view resizableSnapshotViewFromRect:bottomRect
afterScreenUpdates:YES
withCapInsets:UIEdgeInsetsZero];
}
}
@end
......@@ -87,8 +87,12 @@
CGRect initialRect =
[NamedGuide guideWithName:kContentAreaGuide view:viewWithNamedGuides]
.layoutFrame;
layout.expandedRect =
[proxyContainer convertRect:initialRect fromView:viewWithNamedGuides];
[layout.activeItem populateWithSnapshotsFromView:viewWithNamedGuides
middleRect:initialRect];
layout.expandedRect = [proxyContainer convertRect:viewWithNamedGuides.frame
fromView:dismissingView];
NSTimeInterval duration = [self transitionDuration:transitionContext];
// Create the animation view and insert it.
......
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