如何创build一个居中的UICollectionView像Spotify的播放器

我在尝试像Spotify播放器中那样创buildUICollectionView时遇到了很多困难:

一只繁忙的猫

对我来说这个问题是双重的。

1)我如何居中细胞,以便您可以看到中间细胞以及左边和右边的一个。

  • 如果我创build正方形的单元格并在每个单元格之间添加间距,单元格将正确显示,但不居中。

2)使用pagingEnabled = YES,collectionview正确地从一个页面滑动到另一个页面。 但是,如果没有将单元格居中,只会将收集视图移动到屏幕宽度的页面上。 所以问题是你如何让页面移动,以获得上述效果。

3)你如何在移动的时候使细胞的大小animation化

  • 我不想太担心这个。 如果我能做到这一点,这将是伟大的,但更难的问题是1和2。

我目前的代码是一个简单的UICollectionView与正常的委托设置和正方形的自定义UICollectionview单元格。 也许我需要子类UICollectionViewFlowLayout? 或者,也许我需要将pagingEnabled为NO,然后使用自定义刷卡事件? 会爱任何帮助!

正如你在评论中所说的那样,在Objective-c代码中,有一个非常有名的库叫iCarousel,它可以帮助完成你的需求。链接: https : //github.com/nicklockwood/iCarousel

您可以使用“旋转”或“线性”或其他一些样式,只需很less或不需要修改即可实现自定义视图

为了实现它,你只实现了它的一些委托方法,它的工作如下:

//specify the type you want to use in viewDidLoad _carousel.type = iCarouselTypeRotary; //Set the following delegate methods - (NSInteger)numberOfItemsInCarousel:(iCarousel *)carousel { //return the total number of items in the carousel return [_items count]; } - (UIView *)carousel:(iCarousel *)carousel viewForItemAtIndex:(NSInteger)index reusingView:(UIView *)view { UILabel *label = nil; //create new view if no view is available for recycling if (view == nil) { //don't do anything specific to the index within //this `if (view == nil) {...}` statement because the view will be //recycled and used with other index values later view = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 200.0f, 200.0f)]; ((UIImageView *)view).image = [UIImage imageNamed:@"page.png"]; view.contentMode = UIViewContentModeCenter; label = [[UILabel alloc] initWithFrame:view.bounds]; label.backgroundColor = [UIColor clearColor]; label.textAlignment = NSTextAlignmentCenter; label.font = [label.font fontWithSize:50]; label.tag = 1; [view addSubview:label]; } else { //get a reference to the label in the recycled view label = (UILabel *)[view viewWithTag:1]; } //set item label label.text = [_items[index] stringValue]; return view; } - (CGFloat)carousel:(iCarousel *)carousel valueForOption:(iCarouselOption)option withDefault:(CGFloat)value { if (option == iCarouselOptionSpacing) { return value * 1.1; } return value; } 

您可以从Github存储库链接中包含的“ Examples / Basic iOS示例 ”中检查完整的工作演示

由于它是老的和stream行的,你可以find一些相关的教程,它也将比定制代码实现更稳定

那么,昨天我让UICollectionview像这样移动。

我可以与你分享我的代码:)

这是我的故事板

确保取消选中“启用分页”

这是我的代码。

 @interface FavoriteViewController () <UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout> { NSMutableArray * mList; CGSize cellSize; } @property (weak, nonatomic) IBOutlet UICollectionView *cv; @end @implementation FavoriteViewController - (void) viewWillAppear:(BOOL)animated { [super viewWillAppear:animated]; // to get a size. [self.view setNeedsLayout]; [self.view layoutIfNeeded]; CGRect screenFrame = [[UIScreen mainScreen] bounds]; CGFloat width = screenFrame.size.width*self.cv.frame.size.height/screenFrame.size.height; cellSize = CGSizeMake(width, self.cv.frame.size.height); // if cell's height is exactly same with collection view's height, you get an warning message. cellSize.height -= 1; [self.cv reloadData]; // setAlpha is for hiding looking-weird at first load [self.cv setAlpha:0]; } - (void) viewDidAppear:(BOOL)animated { [super viewDidAppear:animated]; [self scrollViewDidScroll:self.cv]; [self.cv setAlpha:1]; } #pragma mark - scrollview delegate - (void) scrollViewDidScroll:(UIScrollView *)scrollView { if(mList.count > 0) { const CGFloat centerX = self.cv.center.x; for(UICollectionViewCell * cell in [self.cv visibleCells]) { CGPoint pos = [cell convertPoint:CGPointZero toView:self.view]; pos.x += cellSize.width/2.0f; CGFloat distance = fabs(centerX - pos.x); // If you want to make side-cell's scale bigger or smaller, // change the value of '0.1f' CGFloat scale = 1.0f - (distance/centerX)*0.1f; [cell setTransform:CGAffineTransformMakeScale(scale, scale)]; } } } - (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset { // for custom paging CGFloat movingX = velocity.x * scrollView.frame.size.width; CGFloat newOffsetX = scrollView.contentOffset.x + movingX; if(newOffsetX < 0) { newOffsetX = 0; } else if(newOffsetX > cellSize.width * (mList.count-1)) { newOffsetX = cellSize.width * (mList.count-1); } else { NSUInteger newPage = newOffsetX/cellSize.width + ((int)newOffsetX%(int)cellSize.width > cellSize.width/2.0f ? 1 : 0); newOffsetX = newPage*cellSize.width; } targetContentOffset->x = newOffsetX; } #pragma mark - collectionview delegate - (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section { return mList.count; } - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath { UICollectionViewCell * cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"list" forIndexPath:indexPath]; NSDictionary * dic = mList[indexPath.row]; UIImageView * iv = (UIImageView *)[cell.contentView viewWithTag:1]; UIImage * img = [UIImage imageWithData:[dic objectForKey:kKeyImg]]; [iv setImage:img]; return cell; } - (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath { return cellSize; } - (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout insetForSectionAtIndex:(NSInteger)section { CGFloat gap = (self.cv.frame.size.width - cellSize.width)/2.0f; return UIEdgeInsetsMake(0, gap, 0, gap); } - (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout minimumLineSpacingForSectionAtIndex:(NSInteger)section { return 0; } - (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout minimumInteritemSpacingForSectionAtIndex:(NSInteger)section { return 0; } 

make中心的关键代码是

  1. scrollViewWillEndDragging

  2. insetForSectionAtIndex

animation大小的关键代码是

  1. scrollviewDidScroll

我希望这可以帮助你

PS如果您想要像上传的图像一样更改Alpha,请在scrollViewDidScroll中添加[cell setalpha]

我想稍微想要类似的行为,在@Mike_M的帮助下,我能弄明白。 虽然有很多很多方法可以做到这一点,但这个特定的实现是创build一个自定义的UICollectionViewLayout。

代码如下(要点可以在这里find: https : //gist.github.com/mmick66/9812223 )

现在设置下面的内容很重要: *yourCollectionView*.decelerationRate = UIScrollViewDecelerationRateFast ,这可以防止单元格被快速滑动所跳过。

这应该包括第1部分和第2部分。现在,第3部分,您可以通过不断的无效化和更新,将它合并到自定义的collectionView中,但是如果你问我,这有点麻烦。 因此,另一种方法是在UIScrollViewDidScroll中设置一个CGAffineTransformMakeScale( , ) ,根据它离屏幕中心的距离dynamic更新单元格的大小。

您可以使用[*youCollectionView indexPathsForVisibleItems]获取collectionView的可见单元格的[*youCollectionView indexPathsForVisibleItems] ,然后获取这些indexPaths的单元格。 对于每个单元格,计算其中心到yourCollectionView中心的距离

可以使用这个漂亮的方法findcollectionView的中心: CGPoint point = [self.view convertPoint:*yourCollectionView*.center toView:*yourCollectionView];

现在build立一个规则,如果单元格的中心距离x远,单元格的大小例如是“正常大小”,则称它为1,越靠近中心越接近两倍正常大小2。

那么你可以使用下面的if / else的想法:

  if (distance > x) { cell.transform = CGAffineTransformMakeScale(1.0f, 1.0f); } else if (distance <= x) { float scale = MIN(distance/x) * 2.0f; cell.transform = CGAffineTransformMakeScale(scale, scale); } 

发生什么事情是,细胞的大小将完全按照你的触摸。 让我知道如果你有任何更多的问题,因为我写的大部分是我头顶的)。

 - (CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)offset withScrollingVelocity:(CGPoint)velocity { CGRect cvBounds = self.collectionView.bounds; CGFloat halfWidth = cvBounds.size.width * 0.5f; CGFloat proposedContentOffsetCenterX = offset.x + halfWidth; NSArray* attributesArray = [self layoutAttributesForElementsInRect:cvBounds]; UICollectionViewLayoutAttributes* candidateAttributes; for (UICollectionViewLayoutAttributes* attributes in attributesArray) { // == Skip comparison with non-cell items (headers and footers) == // if (attributes.representedElementCategory != UICollectionElementCategoryCell) { continue; } // == First time in the loop == // if(!candidateAttributes) { candidateAttributes = attributes; continue; } if (fabsf(attributes.center.x - proposedContentOffsetCenterX) < fabsf(candidateAttributes.center.x - proposedContentOffsetCenterX)) { candidateAttributes = attributes; } } return CGPointMake(candidateAttributes.center.x - halfWidth, offset.y); } 

pagingEnabled不应该被启用,因为它需要每个单元格是你查看的宽度,这将不起作用,因为你需要看到其他单元格的边缘。 对于你的第一点和第二点,我想你会从另外一个问题的迟到答案中find你需要的。

单元格大小的animation可以通过inheritanceUIcollectionviewFlowLayout并重写layoutAttributesForItemAtIndexPath:来实现layoutAttributesForItemAtIndexPath:在其中修改首先调用super提供的布局属性,然后基于与窗口中心相关的位置修改布局属性大小。

希望这有助于。