UICollectionView - 水平滚动,水平布局?

Posted

技术标签:

【中文标题】UICollectionView - 水平滚动,水平布局?【英文标题】:UICollectionView - Horizontal scroll, horizontal layout? 【发布时间】:2013-10-18 13:36:34 【问题描述】:

我有一个UIScrollView,它显示了一个图标网格。如果您想象 ios Springboard 的布局,您将非常接近正确。它有一个水平的分页滚动(就像跳板一样)。但是,布局似乎不太正确。看起来好像它正在从上到下布置项目。结果,由于要显示的项目数量,我的最后一列只有 2 行。我宁愿在最后一页的最后一行有 2 个项目,就像您在跳板中看到的那样。

如何使用UICollectionView 及其相关类来实现这一点?我必须写一个自定义的UICollectionViewFlowLayout吗?

【问题讨论】:

您想出的解决方案是什么?我也在尝试水平布局。 【参考方案1】:

第一种方法

UIPageViewControllerUICollectionViewControllers 的数组一起使用怎么样?您必须在每个UICollectionViewController 中获取适当数量的项目,但这应该不难。您将获得与 Springboard 完全相同的外观。

第二种方法

我已经考虑过了,我认为你必须设置:

self.collectionView.pagingEnabled = YES;

并通过子类化UICollectionViewLayout 创建您自己的集合视图布局。您可以从自定义布局对象访问self.collectionView,因此您将知道集合视图的framenumberOfSectionsnumberOfItemsInSection: 的大小。使用该信息,您可以计算单元格的frames(在prepareLayout 中)和collectionViewContentSize。以下是一些关于创建自定义布局的文章:

https://developer.apple.com/library/content/documentation/WindowsViews/Conceptual/CollectionViewPGforIOS/CreatingCustomLayouts/CreatingCustomLayouts.html http://www.objc.io/issue-3/collection-view-layouts.html

第三种方法

您可以在不创建自定义布局的情况下执行此操作(或其近似值)。在空白视图中添加UIScrollView,在其中设置分页。在滚动视图中添加一个集合视图。然后添加一个宽度约束,在代码中检查你有多少项目并将其constant 设置为正确的值,例如(self.view.frame.size.width * numOfScreens)。这是它的外观(单元格上的数字显示indexPath.row):https://www.dropbox.com/s/ss4jdbvr511azxz/collection_view.mov 如果您对单元格的排序方式不满意,那么恐怕您必须选择 1. 或 2。

【讨论】:

Adopting the 1st approach, I have problems displaying the view controller via push, when a cell is selected.如何解决? @Scott:什么样的问题? 我已经发布了question earlier here。我能够在每个页面中加载不同的 UICollectionViewController,但是一旦我选择单元格就会出现问题,错误指出 Push segues 只能在源控制器由 UINavigationController 的实例管理时使用 如果有人想要第二种方法的工作示例***.com/questions/25963987/…【参考方案2】:

您是否尝试将 UICollectionViewFlowLayout 的滚动方向设置为水平?

[yourFlowLayout setScrollDirection:UICollectionViewScrollDirectionHorizontal];

如果你想让它像跳板一样分页,你需要像这样在你的集合视图上启用分页:

[yourCollectionView setPagingEnabled:YES];

【讨论】:

是的,但是正如我在问题中提到的,这会在每个页面上从上到下填充集合视图。结果,最后一列仅被部分填充。我宁愿它从左到右填充集合视图,使最后一行部分填充。【参考方案3】:

只是为了好玩,另一种方法是只保留分页和水平滚动集,添加一个方法来更改数组项的顺序以从“从上到下,从左到右”转换为视觉上 '从左到右,从上到下' 并用空的隐藏单元格填充中间单元格以使间距正确。如果 9 个网格中有 7 个项目,则如下所示:

[1][4][7]
[2][5][ ]
[3][6][ ]

应该变成

[1][2][3]
[4][5][6]
[7][ ][ ]

所以 1=1、2=4、3=7 等等,6=空。您可以通过计算总行数和总列数对它们重新排序,然后计算每个单元格的行数和列数,更改列的行数,反之亦然,然后您就有了新的索引。当单元格没有与图像对应的值时,您可以返回一个空单元格并将cell.hidden = YES; 设置为它。

它在我构建的音板应用程序中运行良好,所以如果有人想要工作代码,我会添加它。只需要很少的代码就可以使这个技巧发挥作用,听起来比它更难!

更新

我怀疑这是最好的解决方案,但根据要求,这里的工作代码:

- (void)viewDidLoad 
    // Fill an `NSArray` with items in normal order
    items = [NSMutableArray arrayWithObjects:
             [NSDictionary dictionaryWithObjectsAndKeys:@"Some label 1", @"label", @"Some value 1", @"value", nil],
             [NSDictionary dictionaryWithObjectsAndKeys:@"Some label 2", @"label", @"Some value 2", @"value", nil],
             [NSDictionary dictionaryWithObjectsAndKeys:@"Some label 3", @"label", @"Some value 3", @"value", nil],
             [NSDictionary dictionaryWithObjectsAndKeys:@"Some label 4", @"label", @"Some value 4", @"value", nil],
             [NSDictionary dictionaryWithObjectsAndKeys:@"Some label 5", @"label", @"Some value 5", @"value", nil],
              nil
              ];

    // Calculate number of rows and columns based on width and height of the `UICollectionView` and individual cells (you might have to add margins to the equation based on your setup!)
    CGFloat w = myCollectionView.frame.size.width;
    CGFloat h = myCollectionView.frame.size.height;
    rows = floor(h / cellHeight);
    columns = floor(w / cellWidth);


// Calculate number of sections
- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView 
    return ceil((float)items.count / (float)(rows * columns));


// Every section has to have every cell filled, as we need to add empty cells as well to correct the spacing
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section 
    return rows*columns;


// And now the most important one
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath 
    UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier@"myIdentifier" forIndexPath:indexPath];

    // Convert rows and columns
    int row = indexPath.row % rows;
    int col = floor(indexPath.row / rows);
    // Calculate the new index in the `NSArray`
    int newIndex = ((int)indexPath.section * rows * columns) + col + row * columns;

    // If the newIndex is within the range of the items array we show the cell, if not we hide it
    if(newIndex < items.count) 
        NSDictionary *item = [items objectAtIndex:newIndex];
        cell.label.text = [item objectForKey:@"label"];
        cell.hidden = NO;
     else 
        cell.hidden = YES;
    

    return cell;

如果您想使用didSelectItemAtIndexPath 方法,您必须使用与cellForItemAtIndexPath 相同的转换来获得相应的项目。如果您有单元格边距,则需要将它们添加到行和列计算中,因为这些必须正确才能使其起作用。

【讨论】:

你能分享你的代码吗?这听起来像是最好的解决方案! 添加了工作代码。如果您对此有任何疑问,请告诉我! 答案很好,但没有指定行、列、单元格高度、单元格宽度的数据类型。 if(newIndex &lt; items.count) 由于这条线,应用程序正在崩溃。所以我将它更新为if(newIndex &lt; items.count-1) 所以如果我的数组有 4 个对象,它在集合视图中只显示 3 个项目。有什么帮助吗? 这是一个非常好的想法并且易于实施。【参考方案4】:

您需要将UICollectionView 的高度降低到其单元格/项目高度,然后从“Scroll Direction”中选择“Horizontal”,如下面的屏幕截图所示。然后它将根据您在其数据源实现中返回的numberOfItems 水平滚动。

【讨论】:

这并不能回答问题。 感谢“您需要将 UICollectionView 的高度降低到它的单元格/项目高度”这个技巧对我非常有用。【参考方案5】:

我正在开发 Xcode 6.2,对于水平滚动,我在属性检查器中更改了滚动方向。

点击collectionView->属性检查器->滚动方向->改为水平

希望对大家有所帮助。

【讨论】:

【参考方案6】:

如果您在代码中定义 UICollectionViewFlowLayout,它将覆盖 Interface Builder 配置。因此,您需要再次重新定义scrollDirection

let layout = UICollectionViewFlowLayout()
...
layout.scrollDirection = .Horizontal
self.awesomeCollectionView.collectionViewLayout = layout

【讨论】:

这是实际答案。【参考方案7】:

来自@Erik Hunter,我发布了 make Horizo​​ntal UICollectionView 的完整代码

UICollectionViewFlowLayout *collectionViewFlowLayout = [[UICollectionViewFlowLayout alloc] init];
[collectionViewFlowLayout setScrollDirection:UICollectionViewScrollDirectionHorizontal];
self.myCollectionView.collectionViewLayout = collectionViewFlowLayout;

在斯威夫特中

let layout = UICollectionViewFlowLayout()
layout.scrollDirection = .Horizontal
self.myCollectionView.collectionViewLayout = layout

在 Swift 3.0 中

let layout = UICollectionViewFlowLayout()
layout.scrollDirection = .horizontal
self.myCollectionView.collectionViewLayout = layout

希望有帮助

【讨论】:

如果您需要水平滚动以默认方向填充单元格,那么它很好(默认:从上到下)但是,如果需要使水平滚动与单元格填充水平(从左到右)然后需要自定义它...(参考:5_Output_H-Scroll_H-Fill.png)【参考方案8】:

我们可以使用 UICollectionView 执行相同的 Springboard 行为,为此我们需要为自定义布局编写代码。

我通过 "SMCollectionViewFillLayout"

的自定义布局类实现实现了它

代码库:

https://github.com/smindia1988/SMCollectionViewFillLayout

输出如下:

1.png

2_Code_H-Scroll_V-Fill.png

3_Output_H-Scroll_V-Fill.png

4_Code_H-Scroll_H-Fill.png

5_Output_H-Scroll_H-Fill.png

【讨论】:

我想像“5_Output_H-Scroll_H-Fill.png”那样实现。请建议我,我怎样才能做到这一点? @iPhoneDev 我已共享代码存储库链接。您可以从那里下载代码。希望我的代码对你有帮助!!! :) @Sandy 我知道 OP 没有要求这个,但是你有没有机会在 Swift 中使用你的代码?【参考方案9】:

对于 xcode 8,我做到了这一点,并且成功了:

【讨论】:

【参考方案10】:

您可以编写一个自定义的UICollectionView 布局来实现这一点,这是我的实现的演示图像:

这里是代码库:KSTCollectionViewPageHorizontalLayout

@iPhoneDev(这也许对你也有帮助)

【讨论】:

【参考方案11】:

此代码在 Swift 3.1Xcode 8.3.2 中运行良好

override func viewDidLayoutSubviews() 
        super.viewDidLayoutSubviews()

        let layout = UICollectionViewFlowLayout()
        layout.scrollDirection = .horizontal
        self.collectionView.collectionViewLayout = layout
        self.collectionView!.contentInset = UIEdgeInsets(top: -10, left: 0, bottom:0, right: 0)

        if let layout = self.collectionView.collectionViewLayout as? UICollectionViewFlowLayout 
            layout.minimumInteritemSpacing = 0
            layout.minimumLineSpacing = 0
            layout.itemSize = CGSize(width: self.view.frame.size.width-40, height: self.collectionView.frame.size.height-10)
            layout.invalidateLayout()
        

    

【讨论】:

为什么不在将布局分配给集合视图之前设置这些属性? - 会干净得多。 这段代码不是产生了无限循环吗?您在viewDidLayoutSubviews 中调用它,然后触发invalidateLayout(),它会一次又一次地调用viewWillLayoutSubviewsviewDidLayoutSubviews【参考方案12】:

如果您需要设置 UICollectionView 滚动方向水平并且您需要将单元格宽度和高度设置为静态。 请将 collectionview 估计大小 Automatic 设置为 None

View The ScreenShot

【讨论】:

以上是关于UICollectionView - 水平滚动,水平布局?的主要内容,如果未能解决你的问题,请参考以下文章

UICollectionView - 垂直滚动,带有自定义布局的水平分页

UICollectionView - 水平滚动,水平布局?

如何检测滚动到水平 UICollectionView 的末尾

在 UICollectionView 中禁用水平滚动

UICollectionView 中的垂直和水平滚动方向

UICollectionView 水平滚动