首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >UICollectionView中的单元格左对齐

UICollectionView中的单元格左对齐
EN

Stack Overflow用户
提问于 2014-03-21 00:55:36
回答 17查看 73.9K关注 0票数 114

我在我的项目中使用了UICollectionView,其中一行上有多个不同宽度的单元格。根据:https://developer.apple.com/library/content/documentation/WindowsViews/Conceptual/CollectionViewPGforIOS/UsingtheFlowLayout/UsingtheFlowLayout.html

它以相等的填充将单元格分布到整个行中。这是预期的,除非我想左对齐它们,并硬编码一个填充宽度。

我认为我需要对UICollectionViewFlowLayout进行子类化,然而,在阅读了一些在线教程后,我似乎就是不明白它是如何工作的。

EN

回答 17

Stack Overflow用户

回答已采纳

发布于 2016-03-16 00:23:16

当行仅由1个项目组成或过于复杂时,此线程中的其他解决方案无法正常工作。

根据Ryan给出的示例,我修改了代码,通过检查新元素的Y位置来检测新行。在性能上非常简单和快速。

Swift:

代码语言:javascript
复制
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调用中的闭包顶部添加以下内容:

代码语言:javascript
复制
guard layoutAttribute.representedElementCategory == .cell else {
    return
}

Objective-C:

代码语言:javascript
复制
- (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;
}
票数 210
EN

Stack Overflow用户

发布于 2019-10-18 00:03:43

2019年的简单解决方案

这是这些年来事情发生了很大变化的那些令人沮丧的问题之一。现在很容易了。

基本上,您只需这样做:

代码语言:javascript
复制
    // as you move across one row ...
    a.frame.origin.x = x
    x += a.frame.width + minimumInteritemSpacing
    // and, obviously start fresh again each row

您现在所需要的就是样板代码:

代码语言:javascript
复制
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中-就完成了。

要复制和粘贴的完整工作解决方案:

这就是整件事:

代码语言:javascript
复制
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,是他首先澄清了这一点!

票数 36
EN

Stack Overflow用户

发布于 2015-02-07 04:30:04

这个问题已经提出了一段时间,但没有答案,这是一个很好的问题。答案是覆盖UICollectionViewFlowLayout子类中的一个方法:

代码语言:javascript
复制
@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设置为运行计算的边际,并将新元素添加到数组中。

票数 23
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/22539979

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档