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



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

到目前为止,我已经尝试过制作一个自定义的UICollectionViewFlowLayout,但它非常困难,并且不能提供我想要的功能。我还尝试将UICollectionView与UICollectionViewFlowLayout一起使用,其中每个自定义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,而不是试图利用嵌套的滚动视图吗?

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

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

- (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;
}

最新更新