我在我的项目中使用了UICollectionView,其中一行上有多个不同宽度的单元格。根据:https://developer.apple.com/library/content/documentation/WindowsViews/Conceptual/CollectionViewPGforIOS/UsingtheFlowLayout/UsingtheFlowLayout.html
它以相等的填充将单元格分布到整个行中。这是预期的,除非我想左对齐它们,并硬编码一个填充宽度。
我认为我需要对UICollectionViewFlowLayout进行子类化,然而,在阅读了一些在线教程后,我似乎就是不明白它是如何工作的。
发布于 2016-03-16 00:23:16
当行仅由1个项目组成或过于复杂时,此线程中的其他解决方案无法正常工作。
根据Ryan给出的示例,我修改了代码,通过检查新元素的Y位置来检测新行。在性能上非常简单和快速。
Swift:
class LeftAlignedCollectionViewFlowLayout: UICollectionViewFlowLayout {
override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
let attributes = super.layoutAttributesForElements(in: rect)
var leftMargin = sectionInset.left
var maxY: CGFloat = -1.0
attributes?.forEach { layoutAttribute in
if layoutAttribute.frame.origin.y >= maxY {
leftMargin = sectionInset.left
}
layoutAttribute.frame.origin.x = leftMargin
leftMargin += layoutAttribute.frame.width + minimumInteritemSpacing
maxY = max(layoutAttribute.frame.maxY , maxY)
}
return attributes
}
}
如果您希望补充视图保持其大小不变,请在forEach
调用中的闭包顶部添加以下内容:
guard layoutAttribute.representedElementCategory == .cell else {
return
}
Objective-C:
- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect {
NSArray *attributes = [super layoutAttributesForElementsInRect:rect];
CGFloat leftMargin = self.sectionInset.left; //initalized to silence compiler, and actaully safer, but not planning to use.
CGFloat maxY = -1.0f;
//this loop assumes attributes are in IndexPath order
for (UICollectionViewLayoutAttributes *attribute in attributes) {
if (attribute.frame.origin.y >= maxY) {
leftMargin = self.sectionInset.left;
}
attribute.frame = CGRectMake(leftMargin, attribute.frame.origin.y, attribute.frame.size.width, attribute.frame.size.height);
leftMargin += attribute.frame.size.width + self.minimumInteritemSpacing;
maxY = MAX(CGRectGetMaxY(attribute.frame), maxY);
}
return attributes;
}
发布于 2019-10-18 00:03:43
2019年的简单解决方案
这是这些年来事情发生了很大变化的那些令人沮丧的问题之一。现在很容易了。
基本上,您只需这样做:
// as you move across one row ...
a.frame.origin.x = x
x += a.frame.width + minimumInteritemSpacing
// and, obviously start fresh again each row
您现在所需要的就是样板代码:
override func layoutAttributesForElements(
in rect: CGRect)->[UICollectionViewLayoutAttributes]? {
guard let att = super.layoutAttributesForElements(in: rect) else { return [] }
var x: CGFloat = sectionInset.left
var y: CGFloat = -1.0
for a in att {
if a.representedElementCategory != .cell { continue }
if a.frame.origin.y >= y { x = sectionInset.left }
a.frame.origin.x = x
x += a.frame.width + minimumInteritemSpacing
y = a.frame.maxY
}
return att
}
只需将其复制并粘贴到UICollectionViewFlowLayout
中-就完成了。
要复制和粘贴的完整工作解决方案:
这就是整件事:
class TagsLayout: UICollectionViewFlowLayout {
required override init() {super.init(); common()}
required init?(coder aDecoder: NSCoder) {super.init(coder: aDecoder); common()}
private func common() {
estimatedItemSize = UICollectionViewFlowLayout.automaticSize
minimumLineSpacing = 10
minimumInteritemSpacing = 10
}
override func layoutAttributesForElements(
in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
guard let att = super.layoutAttributesForElements(in:rect) else {return []}
var x: CGFloat = sectionInset.left
var y: CGFloat = -1.0
for a in att {
if a.representedElementCategory != .cell { continue }
if a.frame.origin.y >= y { x = sectionInset.left }
a.frame.origin.x = x
x += a.frame.width + minimumInteritemSpacing
y = a.frame.maxY
}
return att
}
}
最后..。
感谢上面的@AlexShubin,是他首先澄清了这一点!
发布于 2015-02-07 04:30:04
这个问题已经提出了一段时间,但没有答案,这是一个很好的问题。答案是覆盖UICollectionViewFlowLayout子类中的一个方法:
@implementation MYFlowLayoutSubclass
//Note, the layout's minimumInteritemSpacing (default 10.0) should not be less than this.
#define ITEM_SPACING 10.0f
- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect {
NSArray *attributesForElementsInRect = [super layoutAttributesForElementsInRect:rect];
NSMutableArray *newAttributesForElementsInRect = [[NSMutableArray alloc] initWithCapacity:attributesForElementsInRect.count];
CGFloat leftMargin = self.sectionInset.left; //initalized to silence compiler, and actaully safer, but not planning to use.
//this loop assumes attributes are in IndexPath order
for (UICollectionViewLayoutAttributes *attributes in attributesForElementsInRect) {
if (attributes.frame.origin.x == self.sectionInset.left) {
leftMargin = self.sectionInset.left; //will add outside loop
} else {
CGRect newLeftAlignedFrame = attributes.frame;
newLeftAlignedFrame.origin.x = leftMargin;
attributes.frame = newLeftAlignedFrame;
}
leftMargin += attributes.frame.size.width + ITEM_SPACING;
[newAttributesForElementsInRect addObject:attributes];
}
return newAttributesForElementsInRect;
}
@end
按照Apple的建议,您可以从super获取布局属性并对其进行迭代。如果它是行中的第一个(由它的原始.x在左边距处定义),则不去管它,并将x重置为零。然后,对于第一个单元格和每个单元格,添加该单元格的宽度和一些边距。这将传递给循环中的下一项。如果它不是第一项,则将它的origin.x设置为运行计算的边际,并将新元素添加到数组中。
https://stackoverflow.com/questions/22539979
复制相似问题