Cell spacing in UICollectionView
如何在
1 2 3 4 5 | - (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout minimumInteritemSpacingForSectionAtIndex:(NSInteger)section { return 5.0; } |
但我还是没有得到想要的结果。我认为这是最小间距。没有什么方法可以设置最大间距吗?
支持最初的问题。我试着在
在UITableView上,我可以通过直接指定一行中的x,y坐标来实现这一点…
以下是我的uiCollectionView代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | #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; } |
更新:用以下代码解决了我的问题。
视图控制器.m1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 | #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 |
我知道这个话题已经过时了,但是如果有人仍然需要正确的答案,你需要的是:
像这样添加实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | - (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; } |
其中maximumspacking可以设置为您喜欢的任何值。这个技巧保证了细胞间的空间完全等于最大间距!!
使用水平流布局,我还得到了单元格之间的10点间距。为了消除间距,我需要将
1 2 3 4 5 | UICollectionViewFlowLayout *flow = [[UICollectionViewFlowLayout alloc] init]; flow.itemSize = CGSizeMake(cellWidth, cellHeight); flow.scrollDirection = UICollectionViewScrollDirectionHorizontal; flow.minimumInteritemSpacing = 0; flow.minimumLineSpacing = 0; |
此外,如果所有单元格的大小相同,则直接在流布局上设置属性而不是使用委托方法更简单、更有效。
记住,它是最小的行间距,而不是最小的项目间间距或单元格空间。因为CollectionView的滚动方向是水平的。
如果是垂直的,则需要为单元格之间的水平空间设置单元格空间或项目间空间,为单元格之间的垂直空间设置行距。
目标C版
1 2 3 4 5 6 | - (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout minimumLineSpacingForSectionAtIndex:(NSInteger)section { return 20; } |
SWIFT版:
1 2 3 | func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat { return 20 } |
尝试此代码以确保每个项目之间的间距为5px:
1 2 3 4 5 6 7 8 9 10 11 12 | - (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; } |
最流行的答案的快速版本。细胞间的空间等于
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | 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 } } |
我发现了一种非常简单的方法,通过使用ib来配置单元格或行之间的间距。
只需从Storyboard/xib文件中选择uiCollectionView,然后单击下图中指定的"大小检查器"。
要以编程方式配置空间,请使用以下属性。
1)用于设置行间距。
1 | [self.collectionView setMinimumLineSpacing:5]; |
2)用于设置项目/单元格之间的间距。
1 | [self.collectionView setMinimumInteritemSpacing:5]; |
请注意属性名minimumInteritemspacing。这将是项目之间的最小间距,而不是确切的间距。如果将MinimumInteritemspacking设置为某个值,则可以确保间距不会小于该值。但也有可能获得更高的价值。
实际上,项目之间的间距取决于几个因素:itemsize和sectioninset。集合视图根据这些值动态放置内容。所以你不能保证精确的间距。您应该对sectioninset和minimumInteritemspacing进行一些尝试和错误。
应答swift 3.0,xcode 8
1.确保设置集合视图委托
1 2 3 4 5 6 7 8 | class DashboardViewController: UIViewController { @IBOutlet weak var dashboardCollectionView: UICollectionView! override func viewDidLoad() { super.viewDidLoad() dashboardCollectionView.delegate = self } } |
2.实现uiCollectionViewDelegateFlowLayout协议,而不是uiCollectionViewDelegate。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | 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 } } |
在头文件中定义
这样实现
1 2 3 4 | - (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout insetForSectionAtIndex:(NSInteger)section { return UIEdgeInsetsMake(5, 5, 5, 5); } |
单击此处查看Apple有关
投票的答案(以及快速版本)有一个小问题:右边会有很大的间隔。
这是因为流布局是定制的,以使单元间距精确,并具有float-left行为。
我的解决方案是操纵截面插入,使截面居中对齐,但间距正好与指定的一样。
在下面的屏幕截图中,项目/行间距正好为8磅,而左&右插入部分将大于8磅(使其居中对齐):
银行代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | 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 } |
间距的简单代码
1 2 | let layout = collectionView.collectionViewLayout as! UICollectionViewFlowLayout layout.minimumInteritemSpacing = 10 // Some float value |
我使用的是MonoTouch,所以名称和代码会有点不同,但您可以通过确保CollectionView的宽度等于(x*单元格宽度)+(x-1)*最小间距,x=每行单元格的数量来实现这一点。
只需根据您的最小interitemspacking和单元格宽度执行以下步骤
1)我们根据单元格大小+当前插入+最小间距计算每行的项目数量。
1 2 | float currentTotalWidth = CollectionView.Frame.Width - Layout.SectionInset.Left - Layout.SectionInset.Right (Layout = flowlayout) int amountOfCellsPerRow = (currentTotalWidth + MinimumSpacing) / (cell width + MinimumSpacing) |
2)现在您有了所有信息来计算集合视图的预期宽度。
1 | float totalWidth =(amountOfCellsPerRow * cell width) + (amountOfCellsPerRow-1) * MinimumSpacing |
3)所以当前宽度和预期宽度之间的差异是
1 | float difference = currentTotalWidth - totalWidth; |
4)现在调整插入(在本例中,我们将其添加到右侧,这样CollectionView的左侧位置保持不变。
1 | Layout.SectionInset.Right = Layout.SectionInset.Right + difference; |
Vojtech VRBKA的上述解决方案是正确的,但会触发警告:
warning:UICollectionViewFlowLayout has cached frame mismatch for index path - cached value: This is likely occurring because the flow layout subclass Layout is modify attributes returned by UICollectionViewFlowLayout without copying them
以下代码应修复此问题:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | 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 } } |
我偶然发现了一个与op类似的问题,不幸的是,由于
1 2 3 4 5 6 7 8 9 10 | #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); } |
该代码将确保
斯威夫特3版
只需创建一个uiCollectionViewFlowLayout子类并粘贴此方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | 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 } |
如果你想在不触及实际单元格大小的情况下调整间距,这是最适合我的解决方案。#xcode 9 tvos11 ios11 swift
所以在uicollectionviewdelegateflowlayout中,change实现下一个方法,诀窍是你必须同时使用这两个方法,而文档并没有真正指向我思考的方向。D
1 2 3 4 5 6 7 | 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 } |
我试过伊阿古849的答案,但效果很好。
斯威夫特4
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | 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
故事板方法
在故事板中选择CollectionView,转到"大小检查器",并将单元格和行的最小间距设置为5
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | 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) }() |
以前的版本并不适用于>1的部分。所以我的解决方案就在这里找到了:https://codentrick.com/create-a-tag-flow-layout-with-uicollectionview/。对于懒惰的人:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | 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 } |
我对接受的答案有疑问,因此我更新了它,这对我很有用:
h
1 2 3 | @interface MaxSpacingCollectionViewFlowLayout : UICollectionViewFlowLayout @property (nonatomic,assign) CGFloat maxCellSpacing; @end |
m
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | @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 |
我在
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | 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
我有一个水平的
我试过了伊阿古849的答案,但后来我发现我甚至不需要他的答案。出于某种原因,设置
不知道为什么它是这样工作的,但它是工作的。
试着用这种方法玩:
1 2 3 4 5 6 7 8 | - (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout insetForSectionAtIndex:(NSInteger)section { UIEdgeInsets insets = UIEdgeInsetsMake(?, ?, ?, ?); return insets; } |