blob: 5a0bf21bcba2d1e6ba923025ec96b71cbc719660 [file] [log] [blame]
// Copyright 2017 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/content_suggestions/content_suggestions_layout.h"
#import "ios/chrome/browser/ui/content_suggestions/content_suggestions_collection_utils.h"
#import "ios/chrome/browser/ui/ntp/new_tab_page_feature.h"
#import "ios/chrome/browser/ui/ntp/new_tab_page_header_constants.h"
#import "ios/chrome/browser/ui/toolbar/public/toolbar_utils.h"
#import "ios/chrome/browser/ui/util/ui_util.h"
#import "ios/chrome/browser/ui/util/uikit_ui_util.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
@implementation ContentSuggestionsLayout
- (instancetype)initWithOffset:(CGFloat)offset {
if (self = [super init]) {
_offset = offset;
}
return self;
}
- (CGSize)collectionViewContentSize {
if (IsRefactoredNTP()) {
// In the refactored NTP, we don't want to extend the view height beyond its
// content.
return [super collectionViewContentSize];
}
DCHECK(!IsRefactoredNTP());
CGFloat collectionViewHeight = self.collectionView.bounds.size.height;
CGFloat headerHeight = [self firstHeaderHeight];
// The minimum height for the collection view content should be the height of
// the header plus the height of the collection view minus the height of the
// NTP bottom bar. This allows the Most Visited cells to be scrolled up to the
// top of the screen. Also computes the total NTP scrolling height for
// Discover infinite feed.
self.ntpHeight = collectionViewHeight + headerHeight;
CGFloat minimumHeight =
self.ntpHeight - ntp_header::kScrolledToTopOmniboxBottomMargin;
CGFloat topSafeArea = self.collectionView.safeAreaInsets.top;
if (!IsRegularXRegularSizeClass(self.collectionView)) {
CGFloat toolbarHeight =
IsSplitToolbarMode(self.collectionView)
? ToolbarExpandedHeight([UIApplication sharedApplication]
.preferredContentSizeCategory)
: 0;
CGFloat additionalHeight =
toolbarHeight + topSafeArea + self.collectionView.contentInset.bottom;
minimumHeight -= additionalHeight;
self.ntpHeight += additionalHeight;
}
CGSize contentSize = [super collectionViewContentSize];
if (contentSize.height < minimumHeight) {
contentSize.height = minimumHeight;
// Increases the minimum height to allow the page to scroll to the cached
// position.
if (self.offset > 0) {
contentSize.height += self.offset;
}
}
return contentSize;
}
- (NSArray*)layoutAttributesForElementsInRect:(CGRect)rect {
if (IsRegularXRegularSizeClass(self.collectionView))
return [super layoutAttributesForElementsInRect:rect];
NSMutableArray* layoutAttributes =
[[super layoutAttributesForElementsInRect:rect] mutableCopy];
UICollectionViewLayoutAttributes* fixedHeaderAttributes = nil;
NSIndexPath* fixedHeaderIndexPath =
[NSIndexPath indexPathForItem:0 inSection:0];
for (UICollectionViewLayoutAttributes* attributes in layoutAttributes) {
if ([attributes.representedElementKind
isEqualToString:UICollectionElementKindSectionHeader] &&
attributes.indexPath.section == fixedHeaderIndexPath.section) {
fixedHeaderAttributes = [self
layoutAttributesForSupplementaryViewOfKind:
UICollectionElementKindSectionHeader
atIndexPath:fixedHeaderIndexPath];
attributes.zIndex = fixedHeaderAttributes.zIndex;
attributes.frame = fixedHeaderAttributes.frame;
}
}
// The fixed header's attributes are not updated if the header's default frame
// is far enough away from |rect|, which can occur when the NTP is scrolled
// up.
if (!fixedHeaderAttributes) {
UICollectionViewLayoutAttributes* fixedHeaderAttributes =
[self layoutAttributesForSupplementaryViewOfKind:
UICollectionElementKindSectionHeader
atIndexPath:fixedHeaderIndexPath];
[layoutAttributes addObject:fixedHeaderAttributes];
}
return layoutAttributes;
}
- (UICollectionViewLayoutAttributes*)
layoutAttributesForSupplementaryViewOfKind:(NSString*)kind
atIndexPath:(NSIndexPath*)indexPath {
UICollectionViewLayoutAttributes* attributes =
[super layoutAttributesForSupplementaryViewOfKind:kind
atIndexPath:indexPath];
if (!IsSplitToolbarMode(self.collectionView))
return attributes;
if ([kind isEqualToString:UICollectionElementKindSectionHeader] &&
indexPath.section == 0) {
UICollectionView* collectionView = self.collectionView;
CGPoint contentOffset = collectionView.contentOffset;
CGFloat headerHeight = CGRectGetHeight(attributes.frame);
CGPoint origin = attributes.frame.origin;
// Keep the header in front of all other views.
attributes.zIndex = NSIntegerMax;
// Prevent the fake omnibox from scrolling up off of the screen.
CGFloat topSafeArea = self.collectionView.safeAreaInsets.top;
CGFloat minY =
headerHeight - ntp_header::kFakeOmniboxScrolledToTopMargin -
ToolbarExpandedHeight(
[UIApplication sharedApplication].preferredContentSizeCategory) -
topSafeArea;
if (contentOffset.y > minY)
origin.y = contentOffset.y - minY;
attributes.frame = {origin, attributes.frame.size};
}
return attributes;
}
- (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBound {
if (IsRegularXRegularSizeClass(self.collectionView))
return [super shouldInvalidateLayoutForBoundsChange:newBound];
return YES;
}
#pragma mark - MDCCollectionViewFlowLayout overrides
// This section contains overrides of methods to avoid ugly effects during
// rotation due to the default behavior of the MDCCollectionViewFlowLayout. See
// http://crbug.com/949659 .
- (UICollectionViewLayoutAttributes*)
initialLayoutAttributesForAppearingItemAtIndexPath:
(NSIndexPath*)itemIndexPath {
UICollectionViewLayoutAttributes* attribute =
[super initialLayoutAttributesForAppearingItemAtIndexPath:itemIndexPath];
attribute.alpha = 0;
return attribute;
}
- (UICollectionViewLayoutAttributes*)
initialLayoutAttributesForAppearingSupplementaryElementOfKind:
(NSString*)elementKind
atIndexPath:
(NSIndexPath*)
elementIndexPath {
UICollectionViewLayoutAttributes* attribute = [super
initialLayoutAttributesForAppearingSupplementaryElementOfKind:elementKind
atIndexPath:
elementIndexPath];
attribute.alpha = 0;
return attribute;
}
- (UICollectionViewLayoutAttributes*)
finalLayoutAttributesForDisappearingItemAtIndexPath:
(NSIndexPath*)itemIndexPath {
UICollectionViewLayoutAttributes* attribute =
[super finalLayoutAttributesForDisappearingItemAtIndexPath:itemIndexPath];
attribute.alpha = 0;
return attribute;
}
- (UICollectionViewLayoutAttributes*)
finalLayoutAttributesForDisappearingSupplementaryElementOfKind:
(NSString*)elementKind
atIndexPath:
(NSIndexPath*)
indexPath {
UICollectionViewLayoutAttributes* attribute = [super
finalLayoutAttributesForDisappearingSupplementaryElementOfKind:elementKind
atIndexPath:indexPath];
attribute.alpha = 0;
return attribute;
}
#pragma mark - Private.
// Returns the height of the header of the first section.
- (CGFloat)firstHeaderHeight {
id<UICollectionViewDelegateFlowLayout> delegate =
static_cast<id<UICollectionViewDelegateFlowLayout>>(
self.collectionView.delegate);
return [delegate collectionView:self.collectionView
layout:self
referenceSizeForHeaderInSection:0]
.height;
}
@end