【问题标题】:Cell spacing in UICollectionViewUICollectionView 中的单元格间距
【发布时间】:2013-06-18 05:41:33
【问题描述】:

如何在UICollectionView 的部分中设置单元格间距?我知道有一个属性minimumInteritemSpacing 我已将其设置为 5.0,但间距未显示为 5.0。我已经实现了flowout委托方法。

- (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout minimumInteritemSpacingForSectionAtIndex:(NSInteger)section
{

    return 5.0;
}

我仍然没有得到想要的结果。我认为它的最小间距。有没有什么办法可以设置最大间距?

【问题讨论】:

  • 你是否检查过它是否进入了委托方法..?通过放置断点..
  • 是的,它正在进入委托,在 IB 中,我也将最小单元格间距设置为 5。但我不认为最小空间是可以帮助我的属性,因为它只会确保最小单元格之间的空间应该是 5,但是如果集合视图有额外的空间,它将使用额外的空间。应该有像最大单元格间距这样的东西
  • 我也有同样的问题。 5px 似乎不起作用。有趣的是,我可以用 UITableView 做到这一点,但不能用 UICollectionView...
  • 我已经修复了它并进行了一些计算。我将在明天发布它。:)
  • 2016 年注意事项就这么简单:stackoverflow.com/a/28327193/294884

标签: ios objective-c uicollectionview


【解决方案1】:

支持最初的问题。我试图在UICollectionView 上将间距设置为 5px,但这不起作用,UIEdgeInsetsMake(0,0,0,0)...

在 UITableView 上,我可以通过直接连续指定 x,y 坐标来做到这一点...

这是我的 UICollectionView 代码:

#pragma mark collection view cell layout / size
- (CGSize)collectionView:(UICollectionView*)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath {
    return [self getCellSize:indexPath];  // will be w120xh100 or w190x100
    // if the width is higher, only one image will be shown in a line
}

#pragma mark collection view cell paddings
- (UIEdgeInsets)collectionView:(UICollectionView*)collectionView layout:(UICollectionViewLayout *)collectionViewLayout insetForSectionAtIndex:(NSInteger)section {
    return UIEdgeInsetsMake(0, 0, 0, 0); // top, left, bottom, right
}

- (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout minimumInteritemSpacingForSectionAtIndex:(NSInteger)section {

    return 5.0;
}

更新:使用以下代码解决了我的问题。

ViewController.m

#import "ViewController.h"
#import "MagazineCell.h" // created just the default class. 

static NSString * const cellID = @"cellID";

@interface ViewController ()

@end

@implementation ViewController

#pragma mark - Collection view
-(NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView
{
    return 1;
}
-(NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
{
    return 30;
}

-(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
    MagazineCell *mCell = (MagazineCell *)[collectionView dequeueReusableCellWithReuseIdentifier:cellID forIndexPath:indexPath];

    mCell.backgroundColor = [UIColor lightGrayColor];

    return mCell;
}

#pragma mark Collection view layout things
// Layout: Set cell size
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath {

    NSLog(@"SETTING SIZE FOR ITEM AT INDEX %d", indexPath.row);
    CGSize mElementSize = CGSizeMake(104, 104);
    return mElementSize;
}
- (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout minimumInteritemSpacingForSectionAtIndex:(NSInteger)section {
    return 2.0;
}

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

// Layout: Set Edges
- (UIEdgeInsets)collectionView:
(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout insetForSectionAtIndex:(NSInteger)section {
   // return UIEdgeInsetsMake(0,8,0,8);  // top, left, bottom, right
    return UIEdgeInsetsMake(0,0,0,0);  // top, left, bottom, right
}

@end

【讨论】:

  • 这是一个更好的答案。看看你可以在 UICollectionViewDelegateFlowLayout 中调整的回调,你可以看到如何调整它。
  • 我认为这只有在item的宽度+间距等于uicollectionview的宽度时才有效。
  • @JayprakashDubey 目前我不适合 swift。你必须尝试一下。祝你好运
  • 你在哪里指定5px的单元格之间的宽度?
  • 比公认的答案要好得多,主要是因为子类化 UICollectionViewLayout 只是为了覆盖一种方法似乎有点矫枉过正,而且考虑到所有其他需要被覆盖的方法,这并不是一件小事。
【解决方案2】:

我知道这个话题很老了,但如果有人仍然需要正确的答案,你需要什么:

  1. 覆盖标准流布局。
  2. 添加这样的实现:

    - (NSArray *) layoutAttributesForElementsInRect:(CGRect)rect {
        NSArray *answer = [super layoutAttributesForElementsInRect:rect];
    
        for(int i = 1; i < [answer count]; ++i) {
            UICollectionViewLayoutAttributes *currentLayoutAttributes = answer[i];
            UICollectionViewLayoutAttributes *prevLayoutAttributes = answer[i - 1];
            NSInteger maximumSpacing = 4;
            NSInteger origin = CGRectGetMaxX(prevLayoutAttributes.frame);
    
            if(origin + maximumSpacing + currentLayoutAttributes.frame.size.width < self.collectionViewContentSize.width) {
                CGRect frame = currentLayoutAttributes.frame;
                frame.origin.x = origin + maximumSpacing;
                currentLayoutAttributes.frame = frame;
            }
        }
        return answer;
    }
    

maximumSpacing 可以设置为您喜欢的任何值。这个技巧保证单元格之间的空间将完全等于maximumSpacing!

【讨论】:

  • 那么,我们把这个放在哪里?
  • 嗨,Jonny,继承自标准流布局,只需将此方法复制并粘贴到您的类中。
  • 请注意,您不需要创建mutableCopy:您不是在改变数组的内容,而是改变其中的对象。
  • 也许我做错了什么或者这个想法不能 100% 起作用,因为当我滚动到最后时,我看到一些单元格相互重叠:(
  • 很棒的帖子。在某些情况下,我确实遇到了一个错误,它扰乱了布局最后一行的格式。为了解决这个问题,我将 if 更改为 AND 附加条件:if(origin + maximumSpacing + currentLayoutAttributes.frame.size.width &lt; self.collectionViewContentSize.width &amp;&amp; currentLayoutAttributes.frame.origin.x &gt; prevLayoutAttributes.frame.origin.x) {
【解决方案3】:

使用水平流布局,我还得到了单元格之间的 10 点间距。要删除间距,我需要将 minimumLineSpacingminimumInterItemSpacing 设置为零。

UICollectionViewFlowLayout *flow = [[UICollectionViewFlowLayout alloc] init];
flow.itemSize = CGSizeMake(cellWidth, cellHeight);
flow.scrollDirection = UICollectionViewScrollDirectionHorizontal;
flow.minimumInteritemSpacing = 0;
flow.minimumLineSpacing = 0;

此外,如果所有单元格的大小相同,则直接在流布局上设置属性而不是使用委托方法更简单、更有效。

【讨论】:

  • 令人惊讶的是,minimumLineSpacing 会影响单元格之间的水平间距。
  • 我也是,我需要水平滚动和单元格之间的零间距,并且 minimumLineSpacing = 0 是关键。
  • 使用minimumInteritemSpacing无需就能获得这种效果。只需在我的水平布局上设置 minimumLineSpacing = 0 即可删除项目之间的水平间距。
  • 如果你想一想,项目之间的水平距离就是水平流动布局的行距。行距是典型的垂直流动布局中项目之间的垂直距离。
【解决方案4】:

请记住,这是最小行间距,而不是最小项目间距或单元格间距。因为你的collectionView的滚动方向是HORIZONTAL

如果是垂直的,那么你需要为单元格之间的水平空间设置单元格间距或项间空间,为单元格之间的垂直间距设置行间距。

Objective-C 版本

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

Swift 版本:

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
    return 20
}

【讨论】:

  • 现在当我返回 0 时底部空间消失了;如何删除右侧空间?
  • 嘿,我不太明白你的问题。但我认为 insetForSectionAtIndex 是您正在寻找的。 - (UIEdgeInsets)collectionView:(UICollectionView*)collectionView 布局:(UICollectionViewLayout *)collectionViewLayout insetForSectionAtIndex:(NSInteger)section { return UIEdgeInsetsMake(5, 10, 5, 10); }
  • 水平和垂直的最小行距。
  • 你拯救了我的一天(Y)。
  • 我的话!我无法相信这一点。谢谢
【解决方案5】:

试试这个代码,确保每个项目之间有 5px 的间距:

- (UIEdgeInsets)collectionView:(UICollectionView *) collectionView 
                        layout:(UICollectionViewLayout *) collectionViewLayout 
        insetForSectionAtIndex:(NSInteger) section {

    return UIEdgeInsetsMake(0, 0, 0, 5); // top, left, bottom, right
}

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

【讨论】:

  • 请将您的代码格式化为代码块,以便在 SO 上很好地显示。
【解决方案6】:

Swift 版本的最受欢迎答案。单元格之间的空间将等于cellSpacing

class CustomViewFlowLayout : UICollectionViewFlowLayout {

    let cellSpacing:CGFloat = 4

    override func layoutAttributesForElementsInRect(rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
        if let attributes = super.layoutAttributesForElementsInRect(rect) {
        for (index, attribute) in attributes.enumerate() {
                if index == 0 { continue }
                let prevLayoutAttributes = attributes[index - 1]
                let origin = CGRectGetMaxX(prevLayoutAttributes.frame)
                if(origin + cellSpacing + attribute.frame.size.width < self.collectionViewContentSize().width) {
                    attribute.frame.origin.x = origin + cellSpacing
                }
            }
            return attributes
        }
        return nil
    }
}

【讨论】:

  • super.layoutAttributesForElements(in: rect) 返回 nil 有什么想法吗?
  • 与所有其他使用此方法的方法一样,这不适用于多行。即使将适当的委托方法和属性设置为使用 0,行之间仍会存在间歇性舍入错误样式的间隙。这可能是 UICollectionView 呈现问题。
【解决方案7】:

我发现使用 IB 配置单元格或行之间的间距非常简单。

只需从 storyboard/Xib 文件中选择 UICollectionView 并点击 Size Inspector,如下图所示。

要以编程方式配置空间,请使用以下属性。

1) 用于设置行之间的空间。

[self.collectionView setMinimumLineSpacing:5];

2) 用于设置项目/单元格之间的空间。

[self.collectionView setMinimumInteritemSpacing:5];

【讨论】:

  • 那是最小空间,会大于指定值,并不总是指定值。
  • minimumLineSpacingminimumInteritemSpacing 属于布局而不是 collectionView 本身。
【解决方案8】:

请注意属性名称 minimumInterItemSpacing 。这将是项目之间的最小间距不是精确的间距。如果将 minimumInterItemSpacing 设置为某个值,则可以确保间距不会小于该值。但是有机会获得更高的价值。

实际上,项目之间的间距取决于 itemSizesectionInset 几个因素。集合视图根据这些值动态放置内容。所以你不能保证确切的间距。您应该对 sectionInsetminimumInterItemSpacing 进行反复试验。

【讨论】:

  • @Anil 边虫? :D
  • 您的昆虫部分可能有错误。
  • 昆虫 == 虫子 && 昆虫 != inset :)
【解决方案9】:

Swift 3.0、Xcode 8 的答案

1.确保你设置了集合视图委托

class DashboardViewController: UIViewController {
    @IBOutlet weak var dashboardCollectionView: UICollectionView!

    override func viewDidLoad() {
        super.viewDidLoad()
        dashboardCollectionView.delegate = self
    }
}

2.实现UICollectionViewDelegateFlowLayout协议,而不是UICollectionViewDelegate。

extension DashboardViewController: UICollectionViewDelegateFlowLayout {
    fileprivate var sectionInsets: UIEdgeInsets {
        return .zero
    }

    fileprivate var itemsPerRow: CGFloat {
        return 2
    }

    fileprivate var interitemSpace: CGFloat {
        return 5.0
    }

    func collectionView(_ collectionView: UICollectionView,
                        layout collectionViewLayout: UICollectionViewLayout,
                        sizeForItemAt indexPath: IndexPath) -> CGSize {
        let sectionPadding = sectionInsets.left * (itemsPerRow + 1)
        let interitemPadding = max(0.0, itemsPerRow - 1) * interitemSpace
        let availableWidth = collectionView.bounds.width - sectionPadding - interitemPadding
        let widthPerItem = availableWidth / itemsPerRow

        return CGSize(width: widthPerItem, height: widthPerItem)
    }

    func collectionView(_ collectionView: UICollectionView,
                        layout collectionViewLayout: UICollectionViewLayout,
                        insetForSectionAt section: Int) -> UIEdgeInsets {
        return sectionInsets
    }

    func collectionView(_ collectionView: UICollectionView,
                        layout collectionViewLayout: UICollectionViewLayout,
                        minimumLineSpacingForSectionAt section: Int) -> CGFloat {
        return 0.0
    }

    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
        return interitemSpace
    }
}

【讨论】:

  • 认为您可以删除那里的公众。应该可以正常工作,并且与所有其他功能一致。
  • 这适用于垂直滚动集合视图吗?我有一个集合视图,它应该在 iPhone 6s 和 6s Plus 等设备上水平显示 2 个单元格,但对于低于该值的任何设备,即 5s、5 和 SE,应该只显示一个单元格。我当前的代码正在运行,但在 6s Plus 设备中,单元之间的差距太大而且看起来很难看,我正在努力缩小差距,这是唯一的要求,因为所有其他设备都按照我的设计工作。
【解决方案10】:

间距的简单代码

let layout = collectionView.collectionViewLayout as! UICollectionViewFlowLayout
layout.minimumInteritemSpacing = 10 // Some float value

【讨论】:

  • 对于水平单元格间距应使用: minimumLineSpacing
【解决方案11】:

voted answer(还有swift version)有个小问题:右边会有很大的间距。

这是因为流布局经过自定义以使单元格间距精确,具有向左浮动行为。

我的解决方案是操作部分插图,使部分居中对齐,但间距与指定的完全一致。

在下面的截图中,项目/行间距正好是 8pt,而左右插入部分将大于 8pt(使其居中对齐):

这样的 Swift 代码:

private let minItemSpacing: CGFloat = 8
private let itemWidth: CGFloat      = 100
private let headerHeight: CGFloat   = 32

override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()

    // Create our custom flow layout that evenly space out the items, and have them in the center
    let layout = UICollectionViewFlowLayout()
    layout.itemSize = CGSize(width: itemWidth, height: itemWidth)
    layout.minimumInteritemSpacing = minItemSpacing
    layout.minimumLineSpacing = minItemSpacing
    layout.headerReferenceSize = CGSize(width: 0, height: headerHeight)

    // Find n, where n is the number of item that can fit into the collection view
    var n: CGFloat = 1
    let containerWidth = collectionView.bounds.width
    while true {
        let nextN = n + 1
        let totalWidth = (nextN*itemWidth) + (nextN-1)*minItemSpacing
        if totalWidth > containerWidth {
            break
        } else {
            n = nextN
        }
    }

    // Calculate the section inset for left and right. 
    // Setting this section inset will manipulate the items such that they will all be aligned horizontally center.
    let inset = max(minItemSpacing, floor( (containerWidth - (n*itemWidth) - (n-1)*minItemSpacing) / 2 ) )
    layout.sectionInset = UIEdgeInsets(top: minItemSpacing, left: inset, bottom: minItemSpacing, right: inset)

    collectionView.collectionViewLayout = layout
}

【讨论】:

  • 我得到一个 'NSInvalidArgumentException',原因:'*** setObjectForKey: object cannot be nil (key: {length = 2, path = 0 - 0})' 未捕获的异常.
  • ...如果我没记错的话,你的while循环相当于:let n = Int((containerWidth + minItemSpacing)/((itemWidth+minItemSpacing)))
  • 没有按原样使用代码,但这为我实现集合视图的流出布局提供了最佳参考。欢呼
【解决方案12】:

在你的头文件中定义UICollectionViewDelegateFlowLayout协议。

像这样实现UICollectionViewDelegateFlowLayout协议的以下方法:

- (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout insetForSectionAtIndex:(NSInteger)section
{
    return UIEdgeInsetsMake(5, 5, 5, 5);
}

Click Here 查看UIEdgeInsetMake 方法的 Apple 文档。

【讨论】:

    【解决方案13】:

    故事板方法

    在情节提要中选择 CollectionView 并转到大小检查器并将单元格和行的最小间距设置为 5

    Swift 5 编程

    lazy var collectionView: UICollectionView = {
            let layout = UICollectionViewFlowLayout()
            layout.scrollDirection = .horizontal
    
            //Provide Width and Height According to your need
            let width = UIScreen.main.bounds.width / 4
            let height = UIScreen.main.bounds.height / 10
            layout.itemSize = CGSize(width: width, height: height)
    
            //For Adjusting the cells spacing
            layout.minimumInteritemSpacing = 5
            layout.minimumLineSpacing = 5
    
            return UICollectionView(frame: self.view.frame, collectionViewLayout: layout)
        }()
    

    【讨论】:

      【解决方案14】:

      如果您想在不触及实际单元格大小的情况下调整间距,这是最适合我的解决方案。 #xcode 9 #tvOS11 #iOS11 #swift

      所以在 UICollectionViewDelegateFlowLayout 中,更改实现下一个方法,诀窍是你必须同时使用它们,而文档并没有真正指出我要朝那个方向思考。 :D

      open func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
          return cellSpacing
      }
      
      public func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
          return cellSpacing
      }
      

      【讨论】:

        【解决方案15】:

        好吧,如果您要创建一个水平集合视图,然后在单元格之间留出空间,您需要设置属性 minimumLineSpacing

        【讨论】:

          【解决方案16】:

          我使用的是 monotouch,所以名称和代码会有些不同,但您可以通过确保 collectionview 的宽度等于 (x * cell width) + (x-1) * MinimumSpacing 来做到这一点x = 每行的单元格数量。

          只需根据您的 MinimumInteritemSpacing 和单元格的宽度执行以下步骤

          1) 我们根据单元格大小 + 当前插图 + 最小间距计算每行的项目数量

          float currentTotalWidth = CollectionView.Frame.Width - Layout.SectionInset.Left - Layout.SectionInset.Right (Layout = flowlayout)
          int amountOfCellsPerRow = (currentTotalWidth + MinimumSpacing) / (cell width + MinimumSpacing)
          

          2) 现在你有了所有信息来计算集合视图的预期宽度

          float totalWidth =(amountOfCellsPerRow * cell width) + (amountOfCellsPerRow-1) * MinimumSpacing
          

          3) 所以当前宽度和预期宽度的差是

          float difference = currentTotalWidth - totalWidth;
          

          4) 现在调整插图(在本例中,我们将其添加到右侧,因此 collectionview 的左侧位置保持不变

          Layout.SectionInset.Right = Layout.SectionInset.Right + difference;
          

          【讨论】:

            【解决方案17】:

            我有一个水平的UICollectionView 和子类UICollectionViewFlowLayout。集合视图的单元格很大,一次只显示一行,集合视图适合屏幕的宽度。

            我尝试了 iago849 的回答,它奏效了,但后来我发现我什至不需要他的回答。出于某种原因,设置 minimumInterItemSpacing 没有任何作用。我的项目/单元格之间的间距可以完全由minimumLineSpacing 控制。

            不知道为什么会这样,但确实有效。

            【讨论】:

            • 这似乎是真的 - 奇怪的是,“lines”值实际上是水平布局中项目之间的分隔符。
            • 嘿@user3344977 - 很好的发现。我决定将这个问题分成一个单独的问题,直接解决水平滚动集合视图,以便某些人更容易找到它。我在那里参考你的答案。我的问题在这里:stackoverflow.com/q/61774415/826946
            【解决方案18】:

            我在Swift 3 单元格行间距中的解决方案,就像在 Instagram 中一样:

            lazy var collectionView: UICollectionView = {
                let layout = UICollectionViewFlowLayout()
                let cv = UICollectionView(frame: .zero, collectionViewLayout: layout)
                cv.backgroundColor = UIColor.rgb(red: 227, green: 227, blue: 227)
                cv.showsVerticalScrollIndicator = false
                layout.scrollDirection = .vertical
                layout.minimumLineSpacing = 1
                layout.minimumInteritemSpacing = 1
                return cv
            }()
            
            func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
            
                switch UIDevice.current.modelName {
                case "iPhone 4":
                    return CGSize(width: 106, height: 106)
                case "iPhone 5":
                    return CGSize(width: 106, height: 106)
                case "iPhone 6,7":
                    return CGSize(width: 124, height: 124)
                case "iPhone Plus":
                    return CGSize(width: 137, height: 137)
                default:
                    return CGSize(width: frame.width / 3, height: frame.width / 3)
                }
            }
            

            如何以编程方式检测设备: https://stackoverflow.com/a/26962452/6013170

            【讨论】:

              【解决方案19】:

              我偶然发现了与 OP 类似的问题。不幸的是,接受的答案对我不起作用,因为collectionView 的内容不会正确居中。因此我想出了一个不同的解决方案,它只要求collectionView 中的所有项目都具有相同的宽度,这似乎是问题中的情况:

              #define cellSize 90
              
              - (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout insetForSectionAtIndex:(NSInteger)section {
                  float width = collectionView.frame.size.width;
                  float spacing = [self collectionView:collectionView layout:collectionViewLayout minimumInteritemSpacingForSectionAtIndex:section];
                  int numberOfCells = (width + spacing) / (cellSize + spacing);
                  int inset = (width + spacing - numberOfCells * (cellSize + spacing) ) / 2;
              
                  return UIEdgeInsetsMake(0, inset, 0, inset);
              }
              

              该代码将确保...minimumInteritemSpacing... 返回的值将是每个collectionViewCell 之间的确切间距,并进一步保证所有单元格将集中在collectionView

              【讨论】:

              • 这很酷。但我想知道是否可以将类似的解决方案应用于元素之间的垂直间距
              • 如果你有水平滚动,垂直间距?
              【解决方案20】:

              vojtech-vrbkaabove solution 是正确的,但会触发警告:

              警告:UICollectionViewFlowLayout 缓存了索引路径的帧不匹配 - 缓存值:这可能是因为流布局子类 Layout 是修改 UICollectionViewFlowLayout 返回的属性而不复制它们

              下面的代码应该可以修复它:

              class CustomViewFlowLayout : UICollectionViewFlowLayout {
              
                 let cellSpacing:CGFloat = 4
              
                 override func layoutAttributesForElementsInRect(rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
                     let original = super.layoutAttributesForElementsInRect(rect)
              
                     if let original = original {
                         let attributes = NSArray.init(array: original, copyItems: true) as! [UICollectionViewLayoutAttributes]
              
                         for (index, attribute) in attributes.enumerate() {
                              if index == 0 { continue }
                              let prevLayoutAttributes = attributes[index - 1]
                              let origin = CGRectGetMaxX(prevLayoutAttributes.frame)
                              if(origin + cellSpacing + attribute.frame.size.width < self.collectionViewContentSize().width) {
                                  attribute.frame.origin.x = origin + cellSpacing
                              }
                          }
                          return attributes
                     }
                     return nil
                 }
              }
              

              【讨论】:

              • 与使用此方法的 SO 上的其他答案一样,这确实适用于在 collectionView 的宽度上安装单元格,但行之间仍然存在间歇性、可变高度的间隙。这可能是 CollectionView 实际呈现方式的限制。
              【解决方案21】:

              我对接受的答案有疑问,所以我更新了它,这对我有用:

              .h

              @interface MaxSpacingCollectionViewFlowLayout : UICollectionViewFlowLayout
              @property (nonatomic,assign) CGFloat maxCellSpacing;
              @end
              

              .m

              @implementation MaxSpacingCollectionViewFlowLayout
              
              - (NSArray *) layoutAttributesForElementsInRect:(CGRect)rect {
                  NSArray *attributes = [super layoutAttributesForElementsInRect:rect];
              
                  if (attributes.count <= 0) return attributes;
              
                  CGFloat firstCellOriginX = ((UICollectionViewLayoutAttributes *)attributes[0]).frame.origin.x;
              
                  for(int i = 1; i < attributes.count; i++) {
                      UICollectionViewLayoutAttributes *currentLayoutAttributes = attributes[i];
                      if (currentLayoutAttributes.frame.origin.x == firstCellOriginX) { // The first cell of a new row
                          continue;
                      }
              
                      UICollectionViewLayoutAttributes *prevLayoutAttributes = attributes[i - 1];
                      CGFloat prevOriginMaxX = CGRectGetMaxX(prevLayoutAttributes.frame);
                      if ((currentLayoutAttributes.frame.origin.x - prevOriginMaxX) > self.maxCellSpacing) {
                          CGRect frame = currentLayoutAttributes.frame;
                          frame.origin.x = prevOriginMaxX + self.maxCellSpacing;
                          currentLayoutAttributes.frame = frame;
                      }
                  }
                  return attributes;
              }
              
              @end
              

              【讨论】:

                【解决方案22】:

                Swift 3 版本

                只需创建一个 UICollectionViewFlowLayout 子类并粘贴此方法即可。

                override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
                        guard let answer = super.layoutAttributesForElements(in: rect) else { return nil }
                
                        for i in 1..<answer.count {
                            let currentAttributes = answer[i]
                            let previousAttributes = answer[i - 1]
                
                            let maximumSpacing: CGFloat = 8
                            let origin = previousAttributes.frame.maxX
                
                            if (origin + maximumSpacing + currentAttributes.frame.size.width < self.collectionViewContentSize.width && currentAttributes.frame.origin.x > previousAttributes.frame.origin.x) {
                                var frame = currentAttributes.frame
                                frame.origin.x = origin + maximumSpacing
                                currentAttributes.frame = frame
                            }
                        }
                
                        return answer
                }
                

                【讨论】:

                • 这适用于水平拟合,但即使将 minimumLineSpacingForSectionAt 设置为返回 0,并且 layout.minimumLineSpacing 设置为 0,行之间仍会存在间隙。此外,在 collectionView 中触摸会使应用程序崩溃,由于第一个循环超出范围。
                【解决方案23】:

                我已经尝试了 iago849 的答案,并且成功了。

                斯威夫特 4

                open override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
                    guard let answer = super.layoutAttributesForElements(in: rect) else {
                        return nil
                    }
                    let count = answer.count
                    for i in 1..<count {
                        let currentLayoutAttributes = answer[i]
                        let prevLayoutAttributes = answer[i-1]
                        let origin = prevLayoutAttributes.frame.maxX
                        if (origin + CGFloat(spacing) + currentLayoutAttributes.frame.size.width) < self.collectionViewContentSize.width && currentLayoutAttributes.frame.origin.x > prevLayoutAttributes.frame.origin.x {
                            var frame = currentLayoutAttributes.frame
                            frame.origin.x = origin + CGFloat(spacing)
                            currentLayoutAttributes.frame = frame
                        }
                    }
                    return answer
                }
                

                这里是 github 项目的链接。 https://github.com/vishalwaka/MultiTags

                【讨论】:

                  【解决方案24】:

                  以前的版本并不真正适用于部分 > 1。所以我的解决方案在这里找到了 https://codentrick.com/create-a-tag-flow-layout-with-uicollectionview/。对于懒惰的人:

                  override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
                      let attributesForElementsInRect = super.layoutAttributesForElements(in: rect)
                      var newAttributesForElementsInRect = [UICollectionViewLayoutAttributes]()
                      // use a value to keep track of left margin
                      var leftMargin: CGFloat = 0.0;
                      for attributes in attributesForElementsInRect! {
                          let refAttributes = attributes 
                          // assign value if next row
                          if (refAttributes.frame.origin.x == self.sectionInset.left) {
                              leftMargin = self.sectionInset.left
                          } else {
                              // set x position of attributes to current margin
                              var newLeftAlignedFrame = refAttributes.frame
                              newLeftAlignedFrame.origin.x = leftMargin
                              refAttributes.frame = newLeftAlignedFrame
                          }
                          // calculate new value for current margin
                          leftMargin += refAttributes.frame.size.width + 10
                          newAttributesForElementsInRect.append(refAttributes)
                      }
                  
                      return newAttributesForElementsInRect
                  }
                  

                  【讨论】:

                    【解决方案25】:

                    试试这个方法:

                    - (UIEdgeInsets)collectionView:(UICollectionView *)collectionView
                                        layout:(UICollectionViewLayout*)collectionViewLayout
                        insetForSectionAtIndex:(NSInteger)section {
                    
                        UIEdgeInsets insets = UIEdgeInsetsMake(?, ?, ?, ?);
                    
                        return insets;
                    }
                    

                    【讨论】:

                    • 这仅适用于部分之间的间距,不适用于单元格
                    猜你喜欢
                    • 2014-09-24
                    • 2015-05-01
                    • 1970-01-01
                    • 2013-03-09
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 2014-06-06
                    相关资源
                    最近更新 更多