问题 iOS UICollectionView - 默认流,从右到左填充行


我在我的一个应用程序UICollectionView中使用,具有自定义单元格但具有默认流程。 目前,该视图连续显示3个单元格(所有单元格大小相同)。单元格从左到右填充行,但我需要从右到左。

可能吗?我是否需要创建自定义流程布局?如果是这样,任何人都可以举个简单的例子?

任何帮助将不胜感激。


8355
2017-10-31 16:44


起源



答案:


假设您只期望三个单元格,并使用此集合视图的标题视图,则需要执行以下步骤:

覆盖UICollectionViewFlowLayout

@interface GSRightToLeftCollectionViewFlowLayout : UICollectionViewFlowLayout

(GS代表Groboot SmarTech :))

.m应该如下所示:

#import "GSRightToLeftCollectionViewFlowLayout.h"

typedef enum {
    // enum for comfortibility.
    CellXPositionLeft = 1,
    CellXpositionRight = 2,
    CellXpositionCenter = 3,
    CellXPositionNone = 4
} CellXPosition;

@interface GSRightToLeftCollectionViewFlowLayout ()

@property (nonatomic) BOOL cellFlag;
@property (nonatomic) float cellLeftX;
@property (nonatomic) float cellMiddleX;
@property (nonatomic) float cellRightX;

@end

@implementation GSRightToLeftCollectionViewFlowLayout

// when ever the bounds change, call layoutAttributesForElementsInRect:
- (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds
{
    return YES;
}

- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect
{
    NSMutableArray *allItems = [[super layoutAttributesForElementsInRect:rect] mutableCopy];

    for (UICollectionViewLayoutAttributes *attribute in allItems) {

        // when we get an item, calculate it's English writing position,
        // so that we can convert it to the Hebrew - Arabic position.
        if (!self.cellFlag) {

            [self calculateLocationsForCellsWithAttribute:attribute];

            self.cellFlag = YES;
        }

        // if it's a header, do not change it's place.
        if (attribute.representedElementKind == UICollectionElementKindSectionHeader) {

            continue;
        }

        // check where the item should be placed.
        CellXPosition position = [self attributeForPosition:attribute];

        switch (position) {
            case CellXPositionLeft:
                attribute.frame = CGRectMake(self.cellLeftX, attribute.frame.origin.y, attribute.frame.size.width, attribute.frame.size.height);
                break;
            case CellXpositionRight:
                attribute.frame = CGRectMake(self.cellRightX, attribute.frame.origin.y, attribute.frame.size.width, attribute.frame.size.height);
                break;
            case CellXpositionCenter:
                attribute.frame = CGRectMake(self.cellMiddleX, attribute.frame.origin.y, attribute.frame.size.width, attribute.frame.size.height);
                break;
            case CellXPositionNone:
                NSLog(@"warning");
                break;
        }
    }

    return allItems;
}


- (CellXPosition)attributeForPosition:(UICollectionViewLayoutAttributes *)attribute
{
    CellXPosition cellXposition = CellXPositionNone;

    // we will return an opposite answer
    if (attribute.indexPath.row % 3 == 0) {

        // if it's in the left side, move it to the right
        cellXposition = CellXpositionRight;

    } else if (attribute.indexPath.row % 3 == 1) {

        cellXposition = CellXpositionCenter;

    } else if (attribute.indexPath.row % 3) {

        // if it's in the right side, move it to the left
        cellXposition = CellXPositionLeft;
    }

    return cellXposition;
}

- (void)calculateLocationsForCellsWithAttribute:(UICollectionViewLayoutAttributes *)attribute
{
    float cellWidth = self.itemSize.width;
    float minimumX = self.sectionInset.left;
    float maximumX = self.sectionInset.right;
    float displayWidth = self.collectionView.contentSize.width - minimumX - maximumX;
    // on iOS6, displayWidth will be 0 (don't know why), so insert an if (displayWidth == 0) and set manually the size.

    self.cellLeftX = minimumX;
    float space = (displayWidth - cellWidth * 3) / 2;
    self.cellMiddleX = self.cellRightX + cellWidth + space;
    self.cellRightX = self.cellMiddleX + cellWidth + space;
}

@end

在显示CollectionView的类中,您需要执行以下操作:

如果您使用的是故事板: 1.将collectionView布局更改为自定义(在属性检查器中) 2.将它的类设置为GSRightToLeftCollectionViewFlowLayout。 3.在ViewDidLoad中(或者每当执行初始化时)

- (void)viewDidLoad
{
    [super viewDidLoad];

    GSRightToLeftCollectionViewFlowLayout *layout = [[GSRightToLeftCollectionViewFlowLayout alloc] init];
    layout.itemSize = CGSizeMake(98, 138); // set the item size.
    layout.minimumLineSpacing = 1; // other layout properties.
    layout.minimumInteritemSpacing = 1; // other layout properties.
    layout.headerReferenceSize = CGSizeMake(50, 18);
    layout.sectionInset = UIEdgeInsetsMake(1.0f, 0.0f, 1.0f, 0.0f);
    layout.scrollDirection = UICollectionViewScrollDirectionVertical;
    // in case you use headers / footers. this is also where you would register a Cell if you don't use storyboards.
    [self.collectionView registerClass:[GSReusableView class] forSupplementaryViewOfKind:UICollectionElementKindSectionHeader withReuseIdentifier:@"HeaderView"];
    self.collectionView.collectionViewLayout = layout;
}

10
2017-11-12 08:03



如果我必须使用这个代码的无限数量的单元格太水平,我必须在代码中做什么改变.. - Leena
你可以更具体@Leena吗? collectionView是水平还是垂直滚动?该 码 不限制单元格/项目的数量,它只更改项目的属性。你的意思是你在一行中有无限数量的项目?你可以上传你想要创建的集合视图的直观表示吗? - Raz
让我应用替代解决方案布局感谢回复。 - Leena
非常有用的答案 - 谢谢!我能解决问题的唯一方法.. stackoverflow.com/questions/24916006  ...正在使用你的示例代码layoutAttributesForElementsInRect并将其向下移动35,为了善良! :o - Fattie
@Leena你可以分享你的替代解决方案吗? - iosMentalist


答案:


假设您只期望三个单元格,并使用此集合视图的标题视图,则需要执行以下步骤:

覆盖UICollectionViewFlowLayout

@interface GSRightToLeftCollectionViewFlowLayout : UICollectionViewFlowLayout

(GS代表Groboot SmarTech :))

.m应该如下所示:

#import "GSRightToLeftCollectionViewFlowLayout.h"

typedef enum {
    // enum for comfortibility.
    CellXPositionLeft = 1,
    CellXpositionRight = 2,
    CellXpositionCenter = 3,
    CellXPositionNone = 4
} CellXPosition;

@interface GSRightToLeftCollectionViewFlowLayout ()

@property (nonatomic) BOOL cellFlag;
@property (nonatomic) float cellLeftX;
@property (nonatomic) float cellMiddleX;
@property (nonatomic) float cellRightX;

@end

@implementation GSRightToLeftCollectionViewFlowLayout

// when ever the bounds change, call layoutAttributesForElementsInRect:
- (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds
{
    return YES;
}

- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect
{
    NSMutableArray *allItems = [[super layoutAttributesForElementsInRect:rect] mutableCopy];

    for (UICollectionViewLayoutAttributes *attribute in allItems) {

        // when we get an item, calculate it's English writing position,
        // so that we can convert it to the Hebrew - Arabic position.
        if (!self.cellFlag) {

            [self calculateLocationsForCellsWithAttribute:attribute];

            self.cellFlag = YES;
        }

        // if it's a header, do not change it's place.
        if (attribute.representedElementKind == UICollectionElementKindSectionHeader) {

            continue;
        }

        // check where the item should be placed.
        CellXPosition position = [self attributeForPosition:attribute];

        switch (position) {
            case CellXPositionLeft:
                attribute.frame = CGRectMake(self.cellLeftX, attribute.frame.origin.y, attribute.frame.size.width, attribute.frame.size.height);
                break;
            case CellXpositionRight:
                attribute.frame = CGRectMake(self.cellRightX, attribute.frame.origin.y, attribute.frame.size.width, attribute.frame.size.height);
                break;
            case CellXpositionCenter:
                attribute.frame = CGRectMake(self.cellMiddleX, attribute.frame.origin.y, attribute.frame.size.width, attribute.frame.size.height);
                break;
            case CellXPositionNone:
                NSLog(@"warning");
                break;
        }
    }

    return allItems;
}


- (CellXPosition)attributeForPosition:(UICollectionViewLayoutAttributes *)attribute
{
    CellXPosition cellXposition = CellXPositionNone;

    // we will return an opposite answer
    if (attribute.indexPath.row % 3 == 0) {

        // if it's in the left side, move it to the right
        cellXposition = CellXpositionRight;

    } else if (attribute.indexPath.row % 3 == 1) {

        cellXposition = CellXpositionCenter;

    } else if (attribute.indexPath.row % 3) {

        // if it's in the right side, move it to the left
        cellXposition = CellXPositionLeft;
    }

    return cellXposition;
}

- (void)calculateLocationsForCellsWithAttribute:(UICollectionViewLayoutAttributes *)attribute
{
    float cellWidth = self.itemSize.width;
    float minimumX = self.sectionInset.left;
    float maximumX = self.sectionInset.right;
    float displayWidth = self.collectionView.contentSize.width - minimumX - maximumX;
    // on iOS6, displayWidth will be 0 (don't know why), so insert an if (displayWidth == 0) and set manually the size.

    self.cellLeftX = minimumX;
    float space = (displayWidth - cellWidth * 3) / 2;
    self.cellMiddleX = self.cellRightX + cellWidth + space;
    self.cellRightX = self.cellMiddleX + cellWidth + space;
}

@end

在显示CollectionView的类中,您需要执行以下操作:

如果您使用的是故事板: 1.将collectionView布局更改为自定义(在属性检查器中) 2.将它的类设置为GSRightToLeftCollectionViewFlowLayout。 3.在ViewDidLoad中(或者每当执行初始化时)

- (void)viewDidLoad
{
    [super viewDidLoad];

    GSRightToLeftCollectionViewFlowLayout *layout = [[GSRightToLeftCollectionViewFlowLayout alloc] init];
    layout.itemSize = CGSizeMake(98, 138); // set the item size.
    layout.minimumLineSpacing = 1; // other layout properties.
    layout.minimumInteritemSpacing = 1; // other layout properties.
    layout.headerReferenceSize = CGSizeMake(50, 18);
    layout.sectionInset = UIEdgeInsetsMake(1.0f, 0.0f, 1.0f, 0.0f);
    layout.scrollDirection = UICollectionViewScrollDirectionVertical;
    // in case you use headers / footers. this is also where you would register a Cell if you don't use storyboards.
    [self.collectionView registerClass:[GSReusableView class] forSupplementaryViewOfKind:UICollectionElementKindSectionHeader withReuseIdentifier:@"HeaderView"];
    self.collectionView.collectionViewLayout = layout;
}

10
2017-11-12 08:03



如果我必须使用这个代码的无限数量的单元格太水平,我必须在代码中做什么改变.. - Leena
你可以更具体@Leena吗? collectionView是水平还是垂直滚动?该 码 不限制单元格/项目的数量,它只更改项目的属性。你的意思是你在一行中有无限数量的项目?你可以上传你想要创建的集合视图的直观表示吗? - Raz
让我应用替代解决方案布局感谢回复。 - Leena
非常有用的答案 - 谢谢!我能解决问题的唯一方法.. stackoverflow.com/questions/24916006  ...正在使用你的示例代码layoutAttributesForElementsInRect并将其向下移动35,为了善良! :o - Fattie
@Leena你可以分享你的替代解决方案吗? - iosMentalist


我有同样的问题,因为阿拉伯语翻译。

这是我的解决方案: 创建UICollectionViewFlowLayout的子类并覆盖

- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect:

我使用NSLocales字符方向作为RTL转换的触发器。如果它是LTR语言,它只返回默认属性。

希望它有用。

- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect {
    NSArray *supersAttributes = [super layoutAttributesForElementsInRect:rect];
    NSLocale *currentLocale = ((NSLocale *) [NSLocale currentLocale]);
    NSLocaleLanguageDirection direction = [NSLocale characterDirectionForLanguage:currentLocale.localeIdentifier];

    // if it's an RTL language: change the frame
    if (direction == NSLocaleLanguageDirectionRightToLeft) {
        for (UICollectionViewLayoutAttributes *attributes in supersAttributes) {
            CGRect frame = attributes.frame;
            frame.origin.x = rect.size.width - attributes.frame.size.width - attributes.frame.origin.x;
            attributes.frame = frame;
        }
    }
    return supersAttributes;
}

5
2017-12-27 15:26



谢谢你,先生!我目前遇到的问题是它不再保留单行,而是尝试在新行中添加项目。如果你有任何指针,他们会很感激,而我自己研究这个问题。:)这在你可能想要将元素“推”到UICollectionview上的情况下很有用。 - Jon Madison
小点,但是 supersAttributes 不需要是可变的。 - bcattle
谢谢你指出这一点。我编辑了我的答案。
当我使用它时,项目就会消失。我需要做些什么特别的工作来完成这项工作?是因为我的观点足够宽,可以滚动吗? - uliwitness


@implementation CollectionReversedLayout
- (void)reverseLayoutAttributes:(UICollectionViewLayoutAttributes *)attributes {
     CGPoint newCenter = attributes.center;
     newCenter.x = self.collectionViewContentSize.width - newCenter.x;
     attributes.center = newCenter;
     return attributes;
}

- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect {
    NSArray *s = [super layoutAttributesForElementsInRect:rect];
    for (UICollectionViewLayoutAttributes *attributes in s) {
        [self reverseLayoutAttributes:attributes];
    }
    return s;
}

- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath {
    UICollectionViewLayoutAttributes *s = [super layoutAttributesForItemAtIndexPath:indexPath];
    [self reverseLayoutAttributes:s];
    return s;
}

0
2017-12-05 06:53