Subclassing UICollectionViewLayout and assign to UICollectionView - ios

I have a CollectionViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// assign layout (subclassed below)
self.collectionView.collectionViewLayout = [[CustomCollectionLayout alloc] init];
}
// data source is working, here's what matters:
- (UICollectionViewCell *)collectionView:(UICollectionView *)cv cellForItemAtIndexPath:(NSIndexPath *)indexPath {
ThumbCell *cell = (ThumbCell *)[cv dequeueReusableCellWithReuseIdentifier:#"ThumbCell" forIndexPath:indexPath];
return cell;
}
I also have a UICollectionViewLayout subclass: CustomCollectionLayout.m
#pragma mark - Overriden methods
- (CGSize)collectionViewContentSize
{
return CGSizeMake(320, 480);
}
- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect
{
return [[super layoutAttributesForElementsInRect:rect] mutableCopy];
}
- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath
{
// configure CellAttributes
cellAttributes = [super layoutAttributesForItemAtIndexPath:indexPath];
// random position
int xRand = arc4random() % 320;
int yRand = arc4random() % 460;
cellAttributes.frame = CGRectMake(xRand, yRand, 150, 170);
return cellAttributes;
}
I'm trying to use a new frame for the cell, just one at a random position.
The problem is layoutAttributesForItemAtIndexPath is not called.
Thanks in advance for any advice.

Edit: I didn't notice that you've solved your case, but I had the same problem and here's what helped in my case. I'm leaving it here, it may be useful for someone given that there's not much on this topic yet.
Upon drawing, UICollectionView does not call the method layoutAttributesForItemAtIndexPath: but layoutAttributesForElementsInRect: to determine which cells should be visible. It's your responsibility to implement this method and from there call layoutAttributesForItemAtIndexPath: for all visible index paths to determine exact locations.
In other words, add this to your layout:
- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect
{
NSMutableArray * array = [NSMutableArray arrayWithCapacity:16];
// Determine visible index paths
for(???) {
[array addObject:[self layoutAttributesForItemAtIndexPath:[NSIndexPath indexPathForItem:??? inSection:???]]];
}
return [array copy];
}

Was an issue with Storyboard parameters. Assigned custom UICollectionLayout from Inspector panel.

Related

Cell disappears after reloading when using estimatedSize

I'm having a weird issue, when using a collection view with dynamic sizes, this issue doesn't happens while using fixed sizes.
After a reload the first cell of each section disappears, but only if they are out of the screen. After a few tests I realize that the cell didn't disappear, but its hidden bellow the section header.
Do you have any idea what is causing this?
Collection without reloading:
Collection after reloading with cell visible:
Collection after reloading with cell out of screen:
3D view of the cell after reloading:
The code:
#pragma mark - UICollectionViewDataSource
- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView {
return self.sections.count;
}
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)sectionIndex {
Section *section = [self.sections objectAtIndex:sectionIndex];
return section.items.count;
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
Section *section = [self.sections objectAtIndex:indexPath.section];
Item *item = [section.items objectAtIndex:indexPath.row];
if (self.editing) {
EditingCollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:#"cell-editing" forIndexPath:indexPath];
cell.item = item;
return cell;
} else {
BasicCollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:#"cell" forIndexPath:indexPath];
cell.item = item;
return cell;
}
}
- (UICollectionReusableView *)collectionView:(UICollectionView *)collectionView viewForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath {
if ([kind isEqualToString:UICollectionElementKindSectionHeader]) {
HeaderCollectionReusableView *header = [collectionView dequeueReusableSupplementaryViewOfKind:kind withReuseIdentifier:#"header" forIndexPath:indexPath];
Section *section = [self.sections objectAtIndex:indexPath.section];
header.title = section.title;
return header;
} else {
UICollectionReusableView *footer = [collectionView dequeueReusableSupplementaryViewOfKind:kind withReuseIdentifier:#"footer" forIndexPath:indexPath];
return footer;
}
}
#implementation DetailCollectionViewLayout
- (instancetype)init {
if (self = [super init]) {
[self initialize];
}
return self;
}
- (instancetype)initWithCoder:(NSCoder *)aDecoder {
if (self = [super initWithCoder:aDecoder]) {
[self initialize];
}
return self;
}
- (void)prepareLayout {
CGFloat cellWidth = (isIPAD) ? 288 : CGRectGetWidth(self.collectionView.bounds);
CGFloat headerWidth = CGRectGetWidth(self.collectionView.bounds);
// CGFloat ratio = (isIPAD) ? 0.33 : 0.66;
self.estimatedItemSize = CGSizeMake(cellWidth, 53);
self.headerReferenceSize = CGSizeMake(headerWidth, 50);
self.footerReferenceSize = CGSizeMake(headerWidth, 1 + self.minimumInteritemSpacing);
[super prepareLayout];
}
- (void)initialize {
self.minimumLineSpacing = 0;
self.minimumInteritemSpacing = (isIPAD) ? 5 : 10;
self.estimatedItemSize = CGSizeZero;
self.scrollDirection = UICollectionViewScrollDirectionVertical;
self.sectionInset = UIEdgeInsetsZero;
}
-(BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds {
return YES;
}
#end
I made a simple sample project and record a video: http://sendvid.com/330uo5jm
It looks like the issue is the position from the first cell.
UICollectionViewCell autosizing can be a little, uh... interesting even at the best of times. I've had this exact issue in the past, and similar issues too.
Use a different value for .estimatedItemSize, the closer to the actual item size the better. I noticed you're using a size of CGRectZero at first. I wouldn't recommend doing that. Just set it once, at the start, with a value close to your size. Try a few values, see what works for you. For me, it took a bit of fine tuning.
For anyone developing for iOS 10 (at the time of writing this hasn't been released) there is a new collection view property that lets the collection view determine the estimated size itself. Set the itemSize to UICollectionViewFlowLayoutAutomaticSize, you shouldn't need to set .estimatedItemSize explicitly.
make sure your estimatedItemSize in your code same as size of cell in your xib or storyboard.Don't changes it's size runtime.
can you check with use of identifier like...
NSString *CellIdentifier = [NSString stringWithFormat:#"%d_%d",indexPath.section,indexPath.row];
Sometimes it happens if you have a big difference between estimated size and real size of cell.
Check you have a clear consequence of top to bottom constraints (if you are using autolayout).
Is there something that can break the autolayout to work properly ? E.g compresion resistance settings ?
Are you sure there are data for the cell after reload ? (Will be weird, but to be sure, just double check that.)
Also as Apple denotes here Apple - self sizing guide try to set the estimation of size as close as possible to real dimensions.
You can also try to refer to invalidation of collection layout as you are using your own. Refer to Possible flow-layout help
Try to set the estimation as close as possible and you will see if it solve your problem.

How do I create a UICollectionView with column and row headers?

I want to create a UICollectionView that looks like this:
It won't be scrollable or editable. I'm currently wondering how to write the layout for this. I'm guessing it won't be a subclass of UICollectionViewFlowLayout. I can think of a number of ways, but was curious if there was any "right" way. The cells will be animate-able.
Should each row or column be its own section?
I've done something like what you want with a subclass of UICollectionViewFlowLayout. It looks like this,
#implementation MultpleLineLayout {
NSInteger itemWidth;
NSInteger itemHeight;
}
-(id)init {
if (self = [super init]) {
itemWidth = 80;
itemHeight = 80;
}
return self;
}
-(CGSize)collectionViewContentSize {
NSInteger xSize = [self.collectionView numberOfItemsInSection:0] * (itemWidth + 2); // the 2 is for spacing between cells.
NSInteger ySize = [self.collectionView numberOfSections] * (itemHeight + 2);
return CGSizeMake(xSize, ySize);
}
- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)path {
UICollectionViewLayoutAttributes* attributes = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:path];
NSInteger xValue;
attributes.size = CGSizeMake(itemWidth,itemHeight);
xValue = itemWidth/2 + path.row * (itemWidth +2);
NSInteger yValue = itemHeight + path.section * (itemHeight +2);
attributes.center = CGPointMake(xValue, yValue);
return attributes;
}
-(NSArray*)layoutAttributesForElementsInRect:(CGRect)rect {
NSInteger minRow = (rect.origin.x > 0)? rect.origin.x/(itemWidth +2) : 0; // need to check because bounce gives negative values for x.
NSInteger maxRow = rect.size.width/(itemWidth +2) + minRow;
NSMutableArray* attributes = [NSMutableArray array];
for(NSInteger i=0 ; i < self.collectionView.numberOfSections; i++) {
for (NSInteger j=minRow ; j < maxRow; j++) {
NSIndexPath* indexPath = [NSIndexPath indexPathForItem:j inSection:i];
[attributes addObject:[self layoutAttributesForItemAtIndexPath:indexPath]];
}
}
return attributes;
}
The data is arranged as an array of arrays where each inner array supplies the data for one horizontal row. With the values I have in there now, and using your data as an example, the view looked like this,
This is the code I haven the view controller,
#interface ViewController ()
#property (strong,nonatomic) UICollectionView *collectionView;
#property (strong,nonatomic) NSArray *theData;
#end
#implementation ViewController
- (void)viewDidLoad {
self.theData = #[#[#"",#"A",#"B",#"C"],#[#"1",#"115",#"127",#"132"],#[#"2",#"",#"",#"153"],#[#"3",#"",#"199",#""]];
MultpleLineLayout *layout = [[MultpleLineLayout alloc] init];
self.collectionView = [[UICollectionView alloc] initWithFrame:self.view.bounds collectionViewLayout:layout];
self.collectionView.dataSource = self;
self.collectionView.delegate = self;
layout.scrollDirection = UICollectionViewScrollDirectionHorizontal;
self.view.backgroundColor = [UIColor blackColor];
[self.view addSubview:self.collectionView];
[self.collectionView registerNib:[UINib nibWithNibName:#"CustomDataCell" bundle:nil] forCellWithReuseIdentifier:#"DataCell"];
}
- (NSInteger)collectionView:(UICollectionView *)view numberOfItemsInSection:(NSInteger)section {
return [self.theData[section] count];
}
- (NSInteger)numberOfSectionsInCollectionView: (UICollectionView *)collectionView {
return [self.theData count];
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
DataCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:#"DataCell" forIndexPath:indexPath];
cell.label.text = self.theData[indexPath.section][indexPath.row];
return cell;
}
You can do it with UICollectionViewFlowLayout but if you go that way you need to count carefully and get the padding (i.e. the top left, and other "empty" cells) right. If you miscount it's obvious because the flow ruins everything quickly and you will find your header cells halfway down your columns. (I have done a similar thing)
I only did it that way because of a fear of custom layouts - but in fact it is as easy as UITableView. If your layout does not scroll at all then yours will be particularly simple as your only real work will be calculating frame values for cells, to be returned in layoutAttributesForItemAtIndexPath. Since your whole view fits in the visible area, layoutAttributesForElementsInRect will mean you simply iterate through all the cells in the view. collectionViewContentSize will be the size of your view.
Looking at your sample picture you might find it convenient to organise your data as a dictionary of arrays, one per column. You can get a column array by name ("A", "B" etc.) and the position in the array corresponds to the value in the leftmost column, which you might name "index".
There are many more methods you can use but those are the basics, and will get your basic display up and running.

iOS UICollectionView header & footer location

Working in iOS 7, how does one specify where the header & footer boxes go in a UICollectionView?
I have a custom UICollectionViewFlowLayout. I have overwritten
-(void)prepareLayout
-(NSArray*) layoutAttributesForElementsInRect:(CGRect)rect
-(UICollectionViewLayoutAttributes*) layoutAttributesForSupplementaryViewOfKind: (NSString*)kind atIndexPath:(NSIndexPath*)indexPath
My problem is, I'm not sure how to specify header location. I have already specified that a header exists in prepareLayout:
-(void)prepareLayout
{
[super prepareLayout];
boundsSize = self.collectionView.bounds.size;
midX = boundsSize.width / 2.0f;
curIndex = 0;
self.headerReferenceSize = CGSizeMake(CELL_SIZE, TITLE_HEIGHT);
self.footerReferenceSize = CGSizeMake(0, 0);
self.scrollDirection = UICollectionViewScrollDirectionHorizontal;
self.sectionInset = UIEdgeInsetsMake(TOP_INSET, LEFT_INSET, BOTTOM_INSET, RIGHT_INSET);
self.minimumLineSpacing = LINE_SPACING;
self.minimumInteritemSpacing = INTERIM_SPACING;
self.itemSize = CGSizeMake(CELL_SIZE, CELL_SIZE);
}
I just don't know the right property of my custom FlowLayout to set, as there doesn't seem to be something like "HeaderLocation" to set, either as a LayoutAttributes or in the layout object itself. Right now, it is appearing to the side/between my images, when I'd like them to be appearing above each image (horizontal scroll).
I have tried the following:
-(UICollectionReusableView*) collectionView: (UICollectionView*)collectionView viewForSupplementaryElementOfKind:(NSString*)kind atIndexPath:(NSIndexPath*)indexPath
{
NSLog(#"**ViewForSupplementaryElementOfKind called***");
CGFloat centerX = collectionView.center.x;
CGFloat centerY = collectionView.center.y;
CGFloat titleWidth = [MyLayout titleWidth];
CGFloat titleHeight = [MyLayout titleHeight];
MyTitleView* titleView = [collectionView dequeueReusableSupplementaryViewOfKind:kind withReuseIdentifier:ImageTitleIdentifier forIndexPath:indexPath];
titleView.frame = CGRectMake(centerX - titleWidth/2.0,
0.0,
titleWidth,
titleHeight);
return titleView;
}
This doesn't work. The title appears above overlapped with a bunch of other titles, then the moment I start scrolling (horizontally), they jump back into the wrong place, horizontally between the images rather than above.
PS> Please do not suggest anything that has to do with NIB or XIB placement. I am using a UICollectionView, NOT a UICollectionViewController, so I actually have no prototypical cell to work with. The layout is being done entirely programatically -- from code alone -- so I can't simply open a XIB file and adjust the location of a text box.
Amending the attributes returned by -layoutAttributesForElementsInRect is the right approach, but if you want to alter the position of offscreen headers and footers, you may need to fetch the supplementary view attributes yourself.
For example, in your UICollectionViewFlowLayout subclass:
- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect
{
NSMutableArray *attributesArray = [[super layoutAttributesForElementsInRect:rect] mutableCopy];
// the call to super only returns attributes for headers that are in the bounds,
// so locate attributes for out of bounds headers and include them in the array
NSMutableIndexSet *omittedSections = [NSMutableIndexSet indexSet];
for (UICollectionViewLayoutAttributes *attributes in attributesArray) {
if (attributes.representedElementCategory == UICollectionElementCategoryCell) {
[omittedSections addIndex:attributes.indexPath.section];
}
}
for (UICollectionViewLayoutAttributes *attributes in attributesArray) {
if ([attributes.representedElementKind isEqualToString:UICollectionElementKindSectionHeader]) {
[omittedSections removeIndex:attributes.indexPath.section];
}
}
[omittedSections enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL *stop) {
NSIndexPath *indexPath = [NSIndexPath indexPathForItem:0 inSection:idx];
UICollectionViewLayoutAttributes *attributes = [self layoutAttributesForSupplementaryViewOfKind:UICollectionElementKindSectionHeader
atIndexPath:indexPath];
[attributesArray addObject:attributes];
}];
for (UICollectionViewLayoutAttributes *attributes in attributesArray) {
if ([attributes.representedElementKind isEqualToString:UICollectionElementKindSectionHeader]) {
// adjust any aspect of each header's attributes here, including frame or zIndex
}
}
return attributesArray;
}
CollectionView Header height is set below Collectionview delegate
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout referenceSizeForHeaderInSection:(NSInteger)section
And Set view in Collectionview Header in Below Delegate
- (UICollectionReusableView*)collectionView:(UICollectionView *)collectionView viewForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath
{
UICollectionReusableView * view = nil;
if ([kind isEqualToString:UICollectionElementKindSectionHeader])
{
ColorSectionHeaderView *header = [collectionView dequeueReusableSupplementaryViewOfKind:UICollectionElementKindSectionHeader
withReuseIdentifier:NSStringFromClass([ColorSectionHeaderView class])
forIndexPath:indexPath];
header.sectionIndex = indexPath.section;
header.hideDelete = collectionView.numberOfSections == 1; // hide when only one section
header.delegate = self;
view = header;
}
return view;
}
Ragistred Class in ViewDidLoad
-(void)ViewDidLoad
{
[collectionView registerNib:[UINib nibWithNibName:NSStringFromClass([ColorSectionFooterView class]) bundle:nil]
forSupplementaryViewOfKind:UICollectionElementKindSectionFooter
withReuseIdentifier:NSStringFromClass([ColorSectionFooterView class])];
[Super ViewDidLoad];
}

How to modify the size of a UIView within a cell in a tableView?

I want my cells to have a UIView (functioning as a simple bar containing a color) with a specific color, for 8 different rows in my table view.
The thing is that in the method cellForRowAtIndexPath, the changes I made are not displayed in the simulator. I only want to make the width of the bar variable. So here is my code:
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"cellR";
ResumenCelda *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
// Configure the cell...
//dictSR is a dictionary where I store some data
NSString *key = [self.dictSR allKeys][indexPath.row];
NSString *sum = self.dictSR[key];
cell.categoria.text = key;
cell.montoTotal.text = sum;
//the function "calcularBarra" is implemented down below
float w = [self calcularBarra:[sum floatValue]];
NSLog(#"VALOR BARRA : %f",w);
[cell.barra setFrame:CGRectMake(129.0f, 25.0f, w, 50.0f)]; // <--- This line doesn't work!
[cell.barra setBackgroundColor:[UIColor grayColor]];
//[cell.contentView addSubview:cell.barra]; Could I omit this line of code if I already had a UIView in my Storyboard tied to "cell.barra" ?
return cell;
}
-(int)calcularBarra:(float)a
{
int widthB = (100*a)/max; //max is declared in myViewController.h as #property float max;
return widthB;
}
In my viewDidLoad method I calculate the maximum value:
- (void)viewDidLoad
{
[super viewDidLoad];
for (int i=0; i<8; i++) {
NSString *key = [self.dictSR allKeys][i];
float b=[dictSR[key] floatValue];
if( b > max )
{
max=b;
}
}
NSLog(#"EL MAXIMO ES %f",max);
// Do any additional setup after loading the view.
}
Instead of using an UIView, I also tried with a UIButton.
Hope you can help me.
Thanks.
Its probably the Auto layout, check that it is disabled on your cell.

Animate a UICollectionView cell on selection

I have a basic grid in a UICollectionView. It's a simple 2 column, multiple row layout using the UICollectionViewDelegateFlowLayout. When a cell is selected, I want to dim the background, float the cell to the center of the screen and then have a workflow based on the selected cell. I'm fairly new to UICollectionViews, and I'm not sure of the best way to go about this.
Should I have a custom layout of the UICollectionView for when a cell is selected?
Or is there a way I can animate the selected cell without having to create a new layout
If anyone can just get me going on the right direction, I think I'll be good to research how to execute it.
After trying several (and rather hacky) solutions, I solved this by writing my own UICollectionView layout. Previous solutions I attempted:
Add a custom UIView overtop the UICollectionView and attempt to fake the original cell moving by overlaying it, dimming the background, and animating the new UIView
Animate the cell without creating a brand new layout
I was trying to avoid it, but after I got into coding it, it was a lot less painful than I originally thought.
Here is my code, for anyone interested:
.h:
#interface FMStackedLayout : UICollectionViewLayout
#property(nonatomic,assign) CGPoint center;
#property(nonatomic,assign) NSInteger cellCount;
-(id)initWithSelectedCellIndexPath:(NSIndexPath *)indexPath;
#end
.m:
#define ITEM_WIDTH 128.0f
#define ITEM_HEIGHT 180.0f
static NSUInteger const RotationCount = 32;
static NSUInteger const RotationStride = 3;
static NSUInteger const PhotoCellBaseZIndex = 100;
#interface FMStackedLayout ()
#property(strong,nonatomic) NSArray *rotations;
#property(strong,nonatomic) NSIndexPath *selectedIndexPath;
#end
#implementation FMStackedLayout
#pragma mark - Lifecycle
-(id)initWithSelectedCellIndexPath:(NSIndexPath *)indexPath{
self = [super init];
if (self) {
self.selectedIndexPath = indexPath;
[self setup];
}
return self;
}
-(id)initWithCoder:(NSCoder *)aDecoder{
self = [super init];
if (self){
[self setup];
}
return self;
}
-(void)setup{
NSMutableArray *rotations = [NSMutableArray arrayWithCapacity:RotationCount];
CGFloat percentage = 0.0f;
for (NSUInteger i = 0; i < RotationCount; i++) {
// Ensure that each angle is different enough to be seen
CGFloat newPercentage = 0.0f;
do {
newPercentage = ((CGFloat)(arc4random() % 220) - 110) * 0.0001f;
} while (fabsf(percentage - newPercentage) < 0.006);
percentage = newPercentage;
CGFloat angle = 2 * M_PI * (1.0f + percentage);
CATransform3D transform = CATransform3DMakeRotation(angle, 0.0f, 0.0f, 1.0f);
[rotations addObject:[NSValue valueWithCATransform3D:transform]];
}
self.rotations = rotations;
}
#pragma mark - Layout
-(void)prepareLayout{
[super prepareLayout];
CGSize size = self.collectionView.frame.size;
self.cellCount = [self.collectionView numberOfItemsInSection:0];
self.center = CGPointMake(size.width / 2.0, size.height / 2.0);
}
-(CGSize)collectionViewContentSize{
return self.collectionView.frame.size;
}
-(UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath{
UICollectionViewLayoutAttributes *attributes = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
attributes.size = CGSizeMake(ITEM_WIDTH, ITEM_HEIGHT);
attributes.center = self.center;
if (indexPath.item == self.selectedIndexPath.item) {
attributes.zIndex = 100;
}else{
attributes.transform3D = [self transformForPersonViewAtIndex:indexPath];
}
return attributes;
}
-(NSArray *)layoutAttributesForElementsInRect:(CGRect)rect{
NSMutableArray *attributes = [NSMutableArray array];
for (NSInteger i = 0; i < self.cellCount; i++) {
NSIndexPath *indexPath = [NSIndexPath indexPathForItem:i
inSection:0];
[attributes addObject:[self layoutAttributesForItemAtIndexPath:indexPath]];
}
return attributes;
}
#pragma mark - Private
-(CATransform3D)transformForPersonViewAtIndex:(NSIndexPath *)indexPath{
NSInteger offset = (indexPath.section * RotationStride + indexPath.item);
return [self.rotations[offset % RotationCount] CATransform3DValue];
}
#end
Then, on your UICollectionView, you call
MyLayout *stackedLayout = [[MyLayout alloc] initWithSelectedCellIndexPath:indexPath];
[stackedLayout invalidateLayout];
[self.collectionView setCollectionViewLayout:stackedLayout
animated:YES];
Animations between cells will be handled by the custom layout.
Seems to be a multi-part question, so I'll answer part of it.
UICollectionviewCells do not automatically adjust on highlight or selection. It will only tell you when the cell is highlighted or selected, with one exception:
For the most part, the collection view modifies only the properties of
a cell to indicate that it is selected or highlighted; it does not
change the visual appearance of your cells, with one exception. If a
cell’s selectedBackgroundView property contains a valid view, the
collection view shows that view when the cell is highlighted or
selected.
Otherwise, you must do the visual highlighting manually. Usually by adjusting either the .alpha property of the entire cell, or swapping out the .image property of a background UIImageView in the cell itself using one or more of the following methods, which are accessible in the <UICollectionViewDelegate> Protocol:
collectionView:didDeselectItemAtIndexPath:
collectionView:didHighlightItemAtIndexPath:
collectionView:didSelectItemAtIndexPath:
collectionView:didUnhighlightItemAtIndexPath:
collectionView:shouldDeselectItemAtIndexPath:
collectionView:shouldHighlightItemAtIndexPath:
collectionView:shouldSelectItemAtIndexPath:
As for the rest of your question,I wish I could be of more help, but I'm not as fluent at the custom view animation.

Resources