有弹性,可伸展的UICollectionView 头部视图

本文翻译自:Stretchy UICollectionView headers

滚动视图的反弹效果可能是iOS中最具特色的效果之一。虽然最初只是华而不实,但随着时间的推移,实际上它已经发挥了一些功能用途,像下拉刷新。另一个很好地应用滚动视图的反弹效果的,就是我最近看到的弹性头部视图。

这个效果非常好,当你向下拉动滚动视图时,可以在顶部和底部查看更多的图片内容。你可能已经在Twitter的iOS应用和Airbnb清单中看到类似的效果了。这种效果很容易实现,今天我将向您展示如何去做。

让我们开始吧。首先,创建一个[UICollectionViewFlowLayout][3]的子类。

import <UIKit/UIKit.h>

@interface StretchyHeaderCollectionViewLayout : UICollectionViewFlowLayout
@end

如果你曾经摆弄自定义过UICollectionView的布局,那么你可能已经意识到它们非常强大的。这是我们的实现:

import "StretchyHeaderCollectionViewLayout.h"

@implementation StretchyHeaderCollectionViewLayout

- (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds {
// This will schedule calls to layoutAttributesForElementsInRect: as the
// collectionView is scrolling.
return YES;
}

- (UICollectionViewScrollDirection)scrollDirection {
// This subclass only supports vertical scrolling.
return UICollectionViewScrollDirectionVertical;
}

- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect {

UICollectionView *collectionView = [self collectionView];
UIEdgeInsets insets = [collectionView contentInset];
CGPoint offset = [collectionView contentOffset];
CGFloat minY = -insets.top;

// First get the superclass attributes.
NSArray *attributes = [super layoutAttributesForElementsInRect:rect];

// Check if we've pulled below past the lowest position
if (offset.y < minY) {

// Figure out how much we've pulled down
CGFloat deltaY = fabsf(offset.y - minY);

for (UICollectionViewLayoutAttributes *attrs in attributes) {

// Locate the header attributes
NSString *kind = [attrs representedElementKind];
if (kind == UICollectionElementKindSectionHeader) {

// Adjust the header's height and y based on how much the user
// has pulled down.
CGSize headerSize = [self headerReferenceSize];
CGRect headerRect = [attrs frame];
headerRect.size.height = MAX(minY, headerSize.height + deltaY);
headerRect.origin.y = headerRect.origin.y - deltaY;
[attrs setFrame:headerRect];
break;
}
}
}
return attributes;
}

@end

这个自定义的布局会检查我们是否下拉到最低的偏移量,这意味着我们将开始拉伸头部视图。如果真的是,我们找到这个头部元素,根据拉伸的值来增加它的高度和y轴上的偏移量。

接下来,在你配置你的集合视图的地方,添加下面的代码:

// Create a new instance of our stretchy layout and set the
// default size for our header (for when it's not stretched)
StretchyHeaderCollectionViewLayout *stretchyLayout;
stretchyLayout = [[StretchyHeaderCollectionViewLayout alloc] init];
[stretchyLayout setHeaderReferenceSize:CGSizeMake(320.0, 160.0)];

// Set our custom layout
[collectionView setCollectionViewLayout:stretchyLayout];
// and tell our collection view to always bounce.
[collectionView setAlwaysBounceVertical:YES];

// Then register a class to use for the header.
[collectionView registerClass:[UICollectionReusableView class]
forSupplementaryViewOfKind:UICollectionElementKindSectionHeader
withReuseIdentifier:@"ident"];

最后一件需要做的事情就是创建我们的头部视图,而且应该是UICollectionReusableView的子类。我们可以添加一个UIImageView作为它的子视图,然后使用autoresizing来让它大小合适:

- (UICollectionReusableView *)collectionView:(UICollectionView *)collectionView
viewForSupplementaryElementOfKind:(NSString *)kind
atIndexPath:(NSIndexPath *)indexPath {
// You can make header an ivar so we only ever create one
if (!header) {

header = [collectionView dequeueReusableSupplementaryViewOfKind:kind
withReuseIdentifier:@"ident"
forIndexPath:indexPath];
CGRect bounds;
bounds = [header bounds];

UIImageView *imageView;
imageView = [[UIImageView alloc] initWithFrame:bounds];
[imageView setImage:[UIImage imageNamed:@"header-background"]];

// Make sure the contentMode is set to scale proportionally
[imageView setContentMode:UIViewContentModeScaleAspectFill];
// Clip the parts of the image that are not in frame
[imageView setClipsToBounds:YES];
// Set the autoresizingMask to always be the same height as the header
[imageView setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
// Add the image to our header
[header addSubview:imageView];
}
return header;
}

这个autoresizingMask 会保持图片的高度与头部视图的一致。这个contentMode和clipsToBounds属性将居中图片以及裁切多余的内容。

你可以在Github上找到完整的代码。

posted @ 2014-12-05 17:33  剑尖  阅读(1268)  评论(0)    收藏  举报