Перекрытие ячейки UICollectionView

У меня есть горизонтальный UICollectionView. Я хочу, чтобы ячейки перекрывались друг с другом на определенное количество 60 пикселей, так что вторая ячейка перекрывает первый на 60 пикселей, а третий перекрывает вторую на ту же сумму и т.д.

Я попробовал подкласс UICollectionViewFlowLayout. Но я не могу разместить UICollectionviewCell поверх друг друга.

Ответ 1

Начиная с этого урока: https://web-beta.archive.org/web/20151116053251/http://blog.tobiaswiedenmann.com/post/35135290759/stacklayout

Я изменил stacklayout специально для горизонтального UICollectionView

UICollectionViewStackLayout.h

#import <UIKit/UIKit.h>

@interface UICollectionViewStackLayout : UICollectionViewFlowLayout

#define STACK_OVERLAP 60 //this corresponds to 60 points which is probably what you want, not pixels
#define ITEM_SIZE CGSizeMake(190,210)

@end

UICollectionViewStackLayout.m

#import "UICollectionViewStackLayout.h"

@implementation UICollectionViewStackLayout

-(id)init{

    self = [super init];

    if (self) {
        [self commonInit];
    }

    return self;
}

-(id)initWithCoder:(NSCoder *)aDecoder
{
    if (self = [super init]) {
        [self commonInit];
    }

    return self;
}

-(void)commonInit
{
    self.itemSize = ITEM_SIZE;

    //set minimum layout requirements
    self.scrollDirection = UICollectionViewScrollDirectionHorizontal;
    self.minimumInteritemSpacing = 0;
}

-(CGSize)collectionViewContentSize
{
    NSInteger xSize = [self.collectionView numberOfItemsInSection:0]
    * self.itemSize.width;
    NSInteger ySize = [self.collectionView numberOfSections]
    * (self.itemSize.height);

    CGSize contentSize = CGSizeMake(xSize, ySize);

    if (self.collectionView.bounds.size.width > contentSize.width)
        contentSize.width = self.collectionView.bounds.size.width;

    if (self.collectionView.bounds.size.height > contentSize.height)
        contentSize.height = self.collectionView.bounds.size.height;

    return contentSize;
}

-(NSArray*)layoutAttributesForElementsInRect:(CGRect)rect
{
    NSArray* attributesArray = [super layoutAttributesForElementsInRect:rect];
    int numberOfItems = [self.collectionView numberOfItemsInSection:0];

    for (UICollectionViewLayoutAttributes *attributes in attributesArray) {
        CGFloat xPosition = attributes.center.x;
        CGFloat yPosition = attributes.center.y;

        if (attributes.indexPath.row == 0) {
            attributes.zIndex = INT_MAX; // Put the first cell on top of the stack
        } else {
            xPosition -= STACK_OVERLAP * attributes.indexPath.row;
            attributes.zIndex = numberOfItems - attributes.indexPath.row; //Other cells below the first one
        }

        attributes.center = CGPointMake(xPosition, yPosition);
    }

    return attributesArray;
}

- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)path {
    UICollectionViewLayoutAttributes* attributes = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:path];
    return attributes;
}

@end

Ответ 2

Я использовал mdewitt ответ, преобразовал его в swift 3, и я отлично работаю! Для всех, кого это интересует:

class CollectionViewOverlappingLayout: UICollectionViewFlowLayout {

var overlap: CGFloat = 30

required init?(coder aDecoder: NSCoder) {
    super.init(coder: aDecoder)
    self.scrollDirection = .horizontal
    self.minimumInteritemSpacing = 0
}

override var collectionViewContentSize: CGSize{
    let xSize = CGFloat(self.collectionView!.numberOfItems(inSection: 0)) * self.itemSize.width
    let ySize = CGFloat(self.collectionView!.numberOfSections) * self.itemSize.height

    var contentSize = CGSize(width: xSize, height: ySize)

    if self.collectionView!.bounds.size.width > contentSize.width {
        contentSize.width = self.collectionView!.bounds.size.width
    }

    if self.collectionView!.bounds.size.height > contentSize.height {
        contentSize.height = self.collectionView!.bounds.size.height
    }

    return contentSize
}

override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {

    let attributesArray = super.layoutAttributesForElements(in: rect)
    let numberOfItems = self.collectionView!.numberOfItems(inSection: 0)

    for attributes in attributesArray! {
        var xPosition = attributes.center.x
        let yPosition = attributes.center.y

        if attributes.indexPath.row == 0 {
            attributes.zIndex = Int(INT_MAX) // Put the first cell on top of the stack
        } else {
            xPosition -= self.overlap * CGFloat(attributes.indexPath.row)
            attributes.zIndex = numberOfItems - attributes.indexPath.row //Other cells below the first one
        }

        attributes.center = CGPoint(x: xPosition, y: yPosition)
    }

    return attributesArray
}

override func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
    return UICollectionViewLayoutAttributes(forCellWith: indexPath)
}
}

Ответ 3

Я использовал оба ответа сверху, чтобы создать пример для Xamarin.iOS:

public class CustomFlowLayout : UICollectionViewFlowLayout
{
    public CustomFlowLayout(nfloat width, nfloat height)
    {
        ItemSize = new CGSize(width, height);
        ScrollDirection = UICollectionViewScrollDirection.Horizontal;
    } 

    public override CGSize CollectionViewContentSize
    {
        get
        {
            var xSize = CollectionView.NumberOfItemsInSection(0) * ItemSize.Width;
            var ySize = CollectionView.NumberOfSections() * ItemSize.Height;

            var contentSize = new CGSize(xSize, ySize);

            if (CollectionView.Bounds.Size.Width > contentSize.Width)
                contentSize.Width = CollectionView.Bounds.Size.Width;

            if (CollectionView.Bounds.Size.Height > contentSize.Height)
                contentSize.Height = CollectionView.Bounds.Size.Height;

            return contentSize;
        }
    }

    public override UICollectionViewLayoutAttributes[] LayoutAttributesForElementsInRect(CGRect rect)
    {
        var attributesArray = base.LayoutAttributesForElementsInRect(rect);
        var numberOfItems = CollectionView.NumberOfItemsInSection(0);

        foreach (var attributes in attributesArray)
        {
            var xPosition = attributes.Center.X;
            var yPosition = attributes.Center.Y;

            if (attributes.IndexPath.Row == 0)
                attributes.ZIndex = int.MaxValue; // Put the first cell on top of the stack
            else
            {
                xPosition -= 20 * attributes.IndexPath.Row; //20 - value based on your cell width
                attributes.ZIndex = numberOfItems - attributes.IndexPath.Row; //Other cells below the first one
            }

            attributes.Center = new CGPoint(xPosition, yPosition);
        }

        return attributesArray;
    }

    public override UICollectionViewLayoutAttributes LayoutAttributesForItem(NSIndexPath indexPath)
    {
        return LayoutAttributesForItem(indexPath);
    }
}