使用嵌套滚动视图实现 iOS 7 跳板多任务 UI

Posted

技术标签:

【中文标题】使用嵌套滚动视图实现 iOS 7 跳板多任务 UI【英文标题】:Implementing the iOS 7 springboard multitasking UI with nested scrollviews 【发布时间】:2014-02-09 23:56:23 【问题描述】:

我正在尝试在我的应用程序中创建一个卡片布局,非常类似于 ios 7 中的多任务 UI。工程 WWDC 视频 Exploring Scroll Views on iOS 7,指出他们在 iOS 7 中使用嵌套滚动视图来实现该效果,但没有详细说明。我想他们会使用 UICollectionView,因为这似乎是最直观的方式。

到目前为止,我已经尝试过制作自定义 UICollectionViewFlowLayout,但这非常困难,并且没有提供我正在寻找的功能。我还尝试使用带有 UICollectionViewFlowLayout 的 UICollectionView,其中每个自定义 UICollectionViewCells 单元格都有一个滚动视图,其中有一张卡片作为其子视图。我正在使用自动布局,所以我添加了卡片底部与滚动视图顶部连续的约束。然后,我在滚动视图的内容大小中添加了额外的空间,以便在用户向上滑动时卡片看起来会滚动到屏幕外。像这样,

[self.contentView addConstraints: [NSLayoutConstraint constraintsWithVisualFormat:@"V:|-70-[cardView(400)]-650-|"
                                                                          options:0
                                                                          metrics:nil
                                                                            views:views]];

| 指定的超级视图是填充单元格的滚动视图。

而且我无法使sizeForItemAtIndexPath 大到足以覆盖整个屏幕,因为我收到错误消息:

the behavior of the UICollectionViewFlowLayout is not defined because: the item height must be less that the height of the UICollectionView minus the section insets top and bottom values.

所以问题是,我应该制作自定义 UICollectionViewFlowLayout 而不是尝试利用嵌套的滚动视图吗?

【问题讨论】:

我问了一个类似的问题,但细节少得多,我删除了。我可能应该刚刚编辑了上一个问题。 【参考方案1】:

如果其他人偶然发现这个问题,答案是否定的,我不应该制作自定义 UICollectionViewFlowLayout 来处理滑出的手势。使用 UIScrollViews 比实现自定义布局更自然。它提供了与整个 iOS 中滚动视图相同的滚动和动量。

我设法通过嵌套 UIScrollView 以非常简单的方式解决了我遇到的问题。我似乎收到的警告实际上只是将集合视图放在选项卡视图控制器的第一个选项卡位置并让单元格占据整个屏幕的错误。通过在集合视图后面添加一个额外的视图解决了这个错误。

- (void)viewDidLoad

    [super viewDidLoad];
    [self.cardCollectionView registerClass:[SwipeCardCollectionViewCell class] forCellWithReuseIdentifier:kCollectionViewCellIdentifier];
    self.cardCollectionView.alwaysBounceVertical = NO;
    self.cardCollectionView.alwaysBounceHorizontal = YES;


- (void)loadView 
    self.view = [[UIView alloc] init];

    self.layout = [[UICollectionViewFlowLayout alloc] init]; 
    self.layout.scrollDirection = UICollectionViewScrollDirectionHorizontal
    self.cardCollectionView = [[UICollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:self.layout];
    self.cardCollectionView.translatesAutoresizingMaskIntoConstraints = NO;
    self.cardCollectionView.backgroundColor = [UIColor clearColor];
    self.cardCollectionView.delegate = self;
    self.cardCollectionView.dataSource = self;
    [self.view addSubview:self.cardCollectionView];

    NSDictionary *views = @@"cardCollectionView": self.cardCollectionView

    [self.view addConstraints: [NSLayoutConstraint constraintsWithVisualFormat:@"V:|[cardCollectionView]|"
                                                                       options:0
                                                                       metrics:nil
                                                                         views:views]];

    [self.view addConstraints: [NSLayoutConstraint constraintsWithVisualFormat:@"H:|[cardCollectionView]|"
                                                                       options:0
                                                                       metrics:nil
                                                                         views:views]];

                                                              views:views]];


-(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath  
    SwipeCardCollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:kCollectionViewCellIdentifier forIndexPath:indexPath];
    cell.delegate = self;
    return cell;

我使集合视图的sizeForItemAtIndexPath: 等于屏幕的大小

- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath 
    return CGSizeMake(self.cardCollectionView.frame.size.width - CARD_WIDTH_INSET, self.cardCollectionView.frame.size.height);


-(CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout minimumInteritemSpacingForSectionAtIndex:(NSInteger)section
    return 0.0;


-(CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout minimumLineSpacingForSectionAtIndex:(NSInteger)section 
    return CARD_SPACING;

我通过实现scrollViewWillEndDragging:withVelocity:targetContentOffset: 并在集合视图的任一侧添加内容插入,让卡片按卡片的宽度而不是集合视图边界的宽度进行分页。

- (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout insetForSectionAtIndex:(NSInteger)section
    CGFloat inset = CARD_WIDTH_INSET/2.0;
    return UIEdgeInsetsMake(0.0, inset, 0.0, inset);


- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset 
    CGFloat cardWidth = self.cardCollectionView.frame.size.width - CARD_WIDTH_INSET;
    NSInteger cardNumber = round(targetContentOffset->x/(cardWidth + CARD_SPACING));
    CGFloat finalOffset = cardNumber*(cardWidth + CARD_SPACING);

   targetContentOffset->x = finalOffset;

最后,通过向我制作的自定义滑动单元格添加滚动视图,我能够让卡片从屏幕上滑出。启用分页允许卡片直接滑出屏幕,就像在跳板多任务 UI 中一样。像这样,

- (id)initWithFrame:(CGRect)frame

    self = [super initWithFrame:frame];
    if (self) 
        self.cardContainerScrollView = [[UIScrollView alloc] init];

        self.cardContainerScrollView.delegate = self;
        self.cardContainerScrollView.pagingEnabled = YES;
        self.cardContainerScrollView.translatesAutoresizingMaskIntoConstraints = NO;
        self.cardContainerScrollView.alwaysBounceHorizontal = NO;
        self.cardContainerScrollView.alwaysBounceVertical = YES;
        self.cardContainerScrollView.showsHorizontalScrollIndicator = NO;
        self.cardContainerScrollView.showsVerticalScrollIndicator = NO;

        self.cardView = [[UIView alloc] init];
        self.cardView.translatesAutoresizingMaskIntoConstraints = NO;
        self.cardView.backgroundColor = [UIColor whiteColor];

        [self.cardContainerScrollView addSubview:self.cardView];

        [self.contentView addSubview:self.cardContainerScrollView];

        NSDictionary *views = @@"cardView": self.cardView,
                                @"cardContainerScrollView": self.cardContainerScrollView;

        [self.contentView addConstraints: [NSLayoutConstraint constraintsWithVisualFormat:@"V:|[cardContainerScrollView]|"
                                                                                  options:0
                                                                                  metrics:nil
                                                                                    views:views]];

        [self.contentView addConstraints: [NSLayoutConstraint constraintsWithVisualFormat:@"H:|[cardContainerScrollView]|"
                                                                                  options:0
                                                                                  metrics:nil
                                                                                    views:views]];

        [self.contentView addConstraints: [NSLayoutConstraint constraintsWithVisualFormat:@"V:|-70-[cardView(400)]-650-|"
                                                                                  options:0
                                                                                  metrics:nil
                                                                                    views:views]];

        [self.contentView addConstraints: [NSLayoutConstraint constraintsWithVisualFormat:@"H:|[cardView(260)]|"
                                                                                  options:0
                                                                                  metrics:nil
                                                                                    views:views]];

    
    return self;

【讨论】:

以上是关于使用嵌套滚动视图实现 iOS 7 跳板多任务 UI的主要内容,如果未能解决你的问题,请参考以下文章

iOS 嵌套滚动视图——一直滚动到超级视图?

ios中带有表格视图的嵌套滚动视图

单视图 IOS 7 内存泄漏

如何在 Twitter iOS 7 应用程序中实现可滑动的时间线

ios上嵌套滚动视图的可用性问题

iOS嵌套的UIScrollViews没有响应