有弹性,可伸展的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上找到完整的代码。

最近的文章

UICollectionView 实现专辑封面视差滚动

本文翻译自:Parallax scrolling album covers with UICollectionView视差效果现在风靡一时,iOS7上更是使用了很多。在新的音乐App中,在iTunes Radio中,都有一种我非常喜欢的特别的视差效果。滚动的专辑封面栈。实现这个效果似乎是一个非常有趣的挑战,今天我将向你展示如何创建这个效果。当然,我们使用的是UICollectionView。下面是最终的效果。在我开始写代码之前,我想先解释一下我的方法。我们将创建一个UICollecti...…

iOS继续阅读
更早的文章

SSH 管道 - 使用SSH进行端口转发

本文翻译自:SSH Tunnel – Port Forwarding With SSHSSH 有很多功能,SSH 管道就是其中一个。SSH管道是两台机器间的安全连接,经常被称为“SSH隧道”,或者“端口转发”。使用SSH,我们可以绑定本地计算机的指定端口到远程计算机上的指定端口。这将在这些机器之间创建加密的SSH 管道,并允许通过本地主机套接字地址直接与远程主机通信。我们可以使用SSH管道来保护不安全的连接或绕过防火墙的不同限制。在我们创建我们的第一个SSH管道之前,你可以在你机器上的...…

Linux继续阅读