Rectangle 27 65

Your observation is correct. This behavior is happening due to the reuse of cells. But you dont have to do any thing with the prepareForReuse. Instead do your check in cellForItem and set the properties accordingly. Some thing like..

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"cvCell" forIndexPath:indexPath];


if (cell.selected) {
     cell.backgroundColor = [UIColor blueColor]; // highlight selection 
}
else
{
     cell.backgroundColor = [UIColor redColor]; // Default color
}
return cell;
}

-(void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath  {

    UICollectionViewCell *datasetCell =[collectionView cellForItemAtIndexPath:indexPath];
    datasetCell.backgroundColor = [UIColor blueColor]; // highlight selection
 }  

 -(void)collectionView:(UICollectionView *)collectionView didDeselectItemAtIndexPath:(NSIndexPath *)indexPath {

UICollectionViewCell *datasetCell =[collectionView cellForItemAtIndexPath:indexPath]; 
datasetCell.backgroundColor = [UIColor redColor]; // Default color
}

selected cells image change, but when scrolls the selection changes.

If you have any question you can ask it as a separate question.. I suggested a possible way.. may or may not work for a particular situation.. I canot guarantee anything!

ios - UICollectionView cell selection and cell reuse - Stack Overflow

ios objective-c uicollectionview
Rectangle 27 12

Cells now stay around for a bit after going off-screen. Which means that sometimes we might not be able to get hold of a cell in didDeselectItemAt indexPath in order to adjust it. It can then show up on screen un-updated and un-recycled. prepareForReuse does not help this corner case.

The easiest solution is disabling the new scrolling by setting isPrefetchingEnabled to false. With this, managing the cell's display with cellForItemAt didSelect didDeselect works as it used to.

However, if you'd rather keep the new smooth scrolling behaviour it's better to use willDisplay :

func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
    let customCell = cell as! CustomCell
    if customCell.isSelected {
        customCell.select()
    } else {
        customCell.unselect()
    }
}

func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
    let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "CustomCell", for: indexPath) as! CustomCell
    //Don't even need to set selection-specific things here as recycled cells will also go through willDisplay
    return cell
}

func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
    let cell = collectionView.cellForItem(at: indexPath) as? CustomCell
    cell?.select()
}

func collectionView(_ collectionView: UICollectionView, didDeselectItemAt indexPath: IndexPath) {
    let cell = collectionView.cellForItem(at: indexPath) as? CustomCell
    cell?.unselect() // <----- this can be null here, and the cell can still come back on screen!
}

With the above you control the cell when it's selected, unselected on-screen, recycled, and just re-displayed.

this should be the accepted answer because of the changes occurring for iOS 10

ios - UICollectionView cell selection and cell reuse - Stack Overflow

ios objective-c uicollectionview
Rectangle 27 10

Anil was on the right track (his solution looks like it should work, I developed this solution independently of his). I still used the prepareForReuse: method to set the cell's selected to FALSE, then in the cellForItemAtIndexPath I check to see if the cell's index is in `collectionView.indexPathsForSelectedItems', if so, highlight it.

-(void)prepareForReuse {
    self.selected = FALSE;
}
cellForItemAtIndexPath:
if ([collectionView.indexPathsForSelectedItems containsObject:indexPath]) {
    [collectionView selectItemAtIndexPath:indexPath animated:FALSE scrollPosition:UICollectionViewScrollPositionNone];
    // Select Cell
}
else {
    // Set cell to non-highlight
}
didDeselectItemAtIndexPath:
didSelectItemAtIndexPath:

This works like a charm for me.

The only and the best way

Don't forget the super call in prepare for reuse!

Using prepareForReuse worked! But as @NYCTechEngineer says, don't forget the call to super. Thanks!

ios - UICollectionView cell selection and cell reuse - Stack Overflow

ios objective-c uicollectionview
Rectangle 27 6

I had a horizontal scrolling collection view (I use collection view in Tableview) and I too faced problems withcell reuse, whenever I select one item and scroll towards right, some other cells in the next visible set gets select automatically. Trying to solve this using any custom cell properties like "selected", highlighted etc didnt help me so I came up with the below solution and this worked for me.

Create a variable in the collectionView to store the selected index, here I have used a class level variable called selectedIndex

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath

{

    MyCVCell *cell = (MyCVCell*)[collectionView dequeueReusableCellWithReuseIdentifier:@"MyCVCell" forIndexPath:indexPath];    

// When scrolling happens, set the selection status only if the index matches the selected Index

if (selectedIndex == indexPath.row) {

        cell.layer.borderWidth = 1.0;

        cell.layer.borderColor = [[UIColor redColor] CGColor];

    }
    else
    {
        // Turn off the selection
        cell.layer.borderWidth = 0.0;

    }
    return cell;

}
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath

{
    MyCVCell *cell = (MyCVCell *)[collectionView cellForItemAtIndexPath:indexPath];
    // Set the index once user taps on a cell
    selectedIndex = indexPath.row;
    // Set the selection here so that selection of cell is shown to ur user immediately
    cell.layer.borderWidth = 1.0;
    cell.layer.borderColor = [[UIColor redColor] CGColor];
    [cell setNeedsDisplay];
}

- (void)collectionView:(UICollectionView *)collectionView didDeselectItemAtIndexPath:(NSIndexPath *)indexPath

{

    MyCVCell *cell = (MyCVCell *)[collectionView cellForItemAtIndexPath:indexPath];

    // Set the index to an invalid value so that the cells get deselected
    selectedIndex = -1;
    cell.layer.borderWidth = 0.0;
    [cell setNeedsDisplay];

}

ios - UICollectionView cell selection and cell reuse - Stack Overflow

ios objective-c uicollectionview
Rectangle 27 21

backgroundView
selectedBackgroundView
UIView* backgroundView = [[UIView alloc] initWithFrame:self.bounds];
backgroundView.backgroundColor = [UIColor redColor];
self.backgroundView = backgroundView;

UIView* selectedBGView = [[UIView alloc] initWithFrame:self.bounds];
selectedBGView.backgroundColor = [UIColor whiteColor];
self.selectedBackgroundView = selectedBGView;

you only need in your class that implements UICollectionViewDelegate enable cells to be highlighted and selected like this:

- (BOOL)collectionView:(UICollectionView *)collectionView
        shouldHighlightItemAtIndexPath:(NSIndexPath *)indexPath
{
    return YES;
}

- (BOOL)collectionView:(UICollectionView *)collectionView
        shouldSelectItemAtIndexPath:(NSIndexPath *)indexPath;
{
    return YES;
}

These two methods (shouldHighlightItemAtIndexPath/shouldSelectItemAtIndexPath) are in UICollectionViewDelegate not datasource

ios - UICollectionView cell selection and cell reuse - Stack Overflow

ios objective-c uicollectionview
Rectangle 27 2

- (void)showSelection:(BOOL)selection
{
    self.contentView.backgroundColor = selection ? [UIColor blueColor] : [UIColor white];
}

Also write redefenition of -prepareForReuse cell method:

- (void)prepareForReuse
{
    [self showSelection:NO];
    [super prepareForReuse];
}

And in your ViewController you should have _selectedIndexPath variable, which defined in -didSelectItemAtIndexPath and nullified in -didDeselectItemAtIndexPath

NSIndexPath *_selectedIndexPath;

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *cellIdentifier = @"Cell";
    UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:cellIdentifier forIndexPath:indexPath];

    if (_selectedIndexPath) {
        [cell showSelection:[indexPath isEqual:_selectedIndexPath]];
    }
}

- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath
{
    UICollectionViewCell *cell = [collectionView cellForItemAtIndexPath:indexPath];
    [cell showSelection:![indexPath isEqual:_selectedIndexPath]];// on/off selection
    _selectedIndexPath = [indexPath isEqual:_selectedIndexPath] ? nil : indexPath;
}

- (void)collectionView:(UICollectionView *)collectionView didDeselectItemAtIndexPath:(NSIndexPath *)indexPath
{
    UICollectionViewCell *cell = [collectionView cellForItemAtIndexPath:indexPath];
    [cell showSelection:NO];
    _selectedIndexPath = nil;
}

ios - UICollectionView cell selection and cell reuse - Stack Overflow

ios objective-c uicollectionview
Rectangle 27 1

Only @stefanB solution worked for me on iOS 9.3

Here what I have to change for Swift 2

func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {

        //prepare your cell here..

        //Add background view for normal cell
        let backgroundView: UIView = UIView(frame: cell!.bounds)
        backgroundView.backgroundColor = UIColor.lightGrayColor()
        cell!.backgroundView = backgroundView

        //Add background view for selected cell
        let selectedBGView: UIView = UIView(frame: cell!.bounds)
        selectedBGView.backgroundColor = UIColor.redColor()
        cell!.selectedBackgroundView = selectedBGView

        return cell!
 }

 func collectionView(collectionView: UICollectionView, shouldHighlightItemAtIndexPath indexPath: NSIndexPath) -> Bool {
        return true
 }

 func collectionView(collectionView: UICollectionView, shouldSelectItemAtIndexPath indexPath: NSIndexPath) -> Bool {
        return true
 }
collectionView(_:willDisplayCell:forItemAtIndexPath:)

ios - UICollectionView cell selection and cell reuse - Stack Overflow

ios objective-c uicollectionview
Rectangle 27 41

Animating the size of a cell works the same way for collection views as it does for table views. If you want to animate the size of a cell, have a data model that reflects the state of the cell (in my case, I just use a flag (CollectionViewCellExpanded, CollectionViewCellCollapsed) and return a CGSize accordingly.

When you call and empty performBathUpdates call, the view animates automatically.

- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath
{
    [collectionView performBatchUpdates:^{
        // append your data model to return a larger size for
        // cell at this index path
    } completion:^(BOOL finished) {

    }];
}

Great answer. In my case, I just had to change the model, then call performBatchUpdates and within the block, call invalidateLayout.

I've got a custom layout that holds a transient property regarding which index path is a "standout" index path, and when I set that index path, the view redraws itself and moves the item at the index path towards the top of the collection, away from the rest of the items (kinda like Passbook). Setting that property and doing with @DorianRoy suggested was a huge help.

This is far better solution especially considering that the changes are persisted after cell reuse.

ios - UICollectionView: Animate cell size change on selection - Stack ...

ios uicollectionview
Rectangle 27 6

  • 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 cells 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.

Hi. I have already attempted to animate from the collectionView:didSelectItemAtIndexPath:. Per the question, though, I want to actually move the cell to the center of the screen to focus on it. I'm wondering now if this doesn't mean I should use a modal view controller and do something to make it appear the cell is animating.

ios - Animate a UICollectionView cell on selection - Stack Overflow

ios uicollectionview uicollectionviewcell uicollectionviewlayout
Rectangle 27 81

With the help of Chase Roberts, I now found the solution to my problem. My code basically looks like this:

// Prepare for animation

[self.collectionView.collectionViewLayout invalidateLayout];
UICollectionViewCell *__weak cell = [self.collectionView cellForItemAtIndexPath:indexPath]; // Avoid retain cycles
void (^animateChangeWidth)() = ^()
{
    CGRect frame = cell.frame;
    frame.size = cell.intrinsicContentSize;
    cell.frame = frame;
};

// Animate

[UIView transitionWithView:cellToChangeSize duration:0.1f options: UIViewAnimationOptionCurveLinear animations:animateChangeWidth completion:nil];

I hope this will help some people. For me it took several hours to figure out how to make that animation work correctly, so I try to free others from that time-consuming burden.

Nice job posting a follow up.

Thanks, and thank you for your help :)

The part about avoiding the retain cycle, shouldn't it rather be declared outside the block? And since you're using uicollectionview (which is iOS 6+), the preferred qualifier is __weak now. If cell appears in your block, a strong reference is created, so you're not avoiding any retain cycle. Nevertheless, thanks a lot for your follow up, very helpful!

A little note of advice to those who read this: Using AutoLayout in your cells might break this. I had to change the height constraint of the cell instead, and then use [cell layoutIfNeeded] inside the animation block

ios - UICollectionView: Animate cell size change on selection - Stack ...

ios uicollectionview
Rectangle 27 29

I've had success animating changes by using -setCollectionViewLayout:animated: to create a new instance of the same collection view layout.

UICollectionViewFlowLayout
// Make sure that your datasource/delegate are up-to-date first

// Then animate the layout changes
[collectionView setCollectionViewLayout:[[UICollectionViewFlowLayout alloc] init] animated:YES];

This gives the smoothest animation by far. Great solution.

None of the other answers worked for methe others performed an animation on the cell, but the act of invalidating the layout (whether explicitly or behind the scenes) also caused issues with the placement of the other cells before or after the individual cell animation. This is the only answer that worked for me!

This works except on the case (tested on iPhone4S, iOS8): during the animation time, if you scroll the CollectionView, the cell will get hidden.

ios - UICollectionView: Animate cell size change on selection - Stack ...

ios uicollectionview
Rectangle 27 2

A good way to animate the cell size is to override isSelected in the collectionViewCell itself.

class CollectionViewCell: UICollectionViewCell {

    override var isHighlighted: Bool {
        didSet {
            if isHighlighted {
                UIView.animate(withDuration: 0.2, delay: 0, options: .curveEaseOut, animations: {
                    self.transform = CGAffineTransform(scaleX: 0.95, y: 0.95)
                }, completion: nil)
            } else {
                UIView.animate(withDuration: 0.2, delay: 0, options: .curveEaseOut, animations: {
                    self.transform = CGAffineTransform(scaleX: 1, y: 1)
                }, completion: nil)
            }
        }
    }

}

ios - UICollectionView: Animate cell size change on selection - Stack ...

ios uicollectionview
Rectangle 27 33

I had a Deselect issue with UICollectionView and I found that I was not allowing multiple selection on collectionView. So when I was testing I tried always on the same cell and if single selection is ON you can't Deselect a cell already selected.

myCollectionView.allowsMultipleSelection = YES;

I guess the reason most people miss that, is because it's not exposed in IB for collection views, right? I've looked for it, couldn't find it anywhere in IB.

ios - UICollectionView Select and Deselect issue - Stack Overflow

ios cocoa-touch uicollectionview
Rectangle 27 17

[UIView transitionWithView:collectionView 
                  duration:.5 
                   options:UIViewAnimationOptionTransitionCurlUp 
                animations:^{

    //any animatable attribute here.
    cell.frame = CGRectMake(3, 14, 100, 100);

} completion:^(BOOL finished) {

    //whatever you want to do upon completion

}];

Play around with something along those lines inside your didselectItemAtIndexPath method.

Playing around with that seems to have solved my problem. I'm right no refining my solution on the basis of your proposal for a bit more elegance in order to post it as soon as I'm done. Thank you very much so far.

It seems to work but the results are horrible visually. It seems to stutter to a halt. Any ideas?

ios - UICollectionView: Animate cell size change on selection - Stack ...

ios uicollectionview
Rectangle 27 8

This is the simplest solution I have found - works like a charm. Smooth animation, and does not mess up the layout.

collectionView.performBatchUpdates({}){}
[collectionView performBatchUpdates:^{ } completion:^(BOOL finished) { }];

The blocks (closures in Swift) should be intentionally left empty!

I've tried this solution with reloadData, moveItemAtIndexPath: toIndexPath:, reloadItemsAtIndexPaths: inside updates block, then realized that block should be left empty. Now really works like a charm.

ios - UICollectionView: Animate cell size change on selection - Stack ...

ios uicollectionview
Rectangle 27 11

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.

@interface FMStackedLayout : UICollectionViewLayout

@property(nonatomic,assign) CGPoint     center;
@property(nonatomic,assign) NSInteger   cellCount;

-(id)initWithSelectedCellIndexPath:(NSIndexPath *)indexPath;

@end
#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
MyLayout *stackedLayout = [[MyLayout alloc] initWithSelectedCellIndexPath:indexPath];
[stackedLayout invalidateLayout];
[self.collectionView setCollectionViewLayout:stackedLayout
                                    animated:YES];

This is not an answer. Looks like you have the answer but are not interested in sharing.

@Mark Struzinski can you please share your solution?

It's a rather long bit of code, but I was not holding back for any particular reason. I've posted it above for anyone interested. Not sure why there were downvotes here. The final solution used a method that is thoroughly documented in the Apple docs: developer.apple.com/library/ios/#documentation/uikit/reference/. I had been trying to avoid it because it's quite a bit of work.

Can you have a control over the duration of the animation?

ios - Animate a UICollectionView cell on selection - Stack Overflow

ios uicollectionview uicollectionviewcell uicollectionviewlayout
Rectangle 27 1

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{
        CollectionViewCell *cell = (CollectionViewCell*)[collectionView dequeueReusableCellWithReuseIdentifier:@"cell" forIndexPath:indexPath];
        cell.backgroundColor = [UIColor redColor];
        return cell;
    }

    - (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath{

        if ([indexPath isEqual:selectedIndexPath]) {
            return CGSizeMake(175, 175);  // return enlarged size if selected cell
        }
        return CGSizeMake(150, 150);
    }
    -(void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath{
        selectedIndexPath = indexPath;   // set selected indexPath
        [collectionView performBatchUpdates:^{
                [collectionView.collectionViewLayout invalidateLayout];
            } completion:^(BOOL finished) {
                }];
    }

    - (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout insetForSectionAtIndex:(NSInteger)section{
        return UIEdgeInsetsMake(0, 0,0, 0);
    }

    - (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout minimumLineSpacingForSectionAtIndex:(NSInteger)section{
        return 20;
    }
    - (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout minimumInteritemSpacingForSectionAtIndex:(NSInteger)section{
        return 20;
    }

ios - UICollectionView: minimumInteritemSpacing change on cell selecti...

ios uicollectionview uicollectionviewcell uicollectionviewlayout
Rectangle 27 1

By applying a transform, you are just scaling the view up in size. To adjust the size and allow the rest of the UICollectionView to adjust you will need to return a new size in sizeForItemAtIndexPath:

When the item is selected, you should reload that item at that indexPath using:

[collectionView reloadItemAtIndexPath:selectedIndexPath]

Replace selectedIndexPath with name of your variable storing the selected indexPath.

You don't want to invalidateLayout because you're just changing the appearance of one item.

This works, But last cell moves down. some 100 pixels down from rest of the cells

ios - UICollectionView: minimumInteritemSpacing change on cell selecti...

ios uicollectionview uicollectionviewcell uicollectionviewlayout
Rectangle 27 47

As the documentation says, you can rely on highlighted property to be changed while the cell is highlighted. For example the following code will make the cell red when highlighted (not its subviews though):

- (void)setHighlighted:(BOOL)highlighted {
    [super setHighlighted:highlighted];
    [self setNeedsDisplay];
}

- (void)drawRect:(CGRect)rect {
    [super drawRect:rect];

    if (self.highlighted) {
        CGContextRef context = UIGraphicsGetCurrentContext();
        CGContextSetRGBFillColor(context, 1, 0, 0, 1);
        CGContextFillRect(context, self.bounds);
    } 
}

And if you add something like this the background will become purple (red + opaque blue):

- (void)collectionView:(UICollectionView *)colView didHighlightItemAtIndexPath:(NSIndexPath *)indexPath {
    UICollectionViewCell *cell = [colView cellForItemAtIndexPath:indexPath];
    cell.contentView.backgroundColor = [UIColor colorWithRed:0 green:0 blue:1 alpha:0.5];
}

- (void)collectionView:(UICollectionView *)colView didUnhighlightItemAtIndexPath:(NSIndexPath *)indexPath {
    UICollectionViewCell *cell = [colView cellForItemAtIndexPath:indexPath];
    cell.contentView.backgroundColor = nil;
}

So you can use both together (not necessarily both changing the cell appearance). The difference is that with delegate methods you also have indexPath. It might be used to create multi-selection (you will use this methods together with selection delegate methods), to show some preview while the cell is highlighted, to show some animation with other views... There's quite a few appliance for this delegate methods in my opinion.

As a conclusion, I would leave the cell appearance to be handled by the cell itself and use delegate methods to let controller make something cool in the same time.

+1 Thanks. I like the setNeedsDisplay + drawRect approach. Maybe I would check if self.highlighted != highlighted (on line 1) before calling setNeedsDisplay, to avoid unnecessary drawing.

drawRect
setNeedsDisplay
setHighlighted:
self.contentView.backgroundColor = highlighted ? UIColor(white: 217.0/255.0, alpha: 1.0) : nil

ios - Where to highlight UICollectionViewCell: delegate or cell? - Sta...

ios ios6 uicollectionview uicollectionviewcell
Rectangle 27 7

You need to set property allowsSelection and allowsMultipleSelection also in viewDidLoad instead of didSelectItemAt indexPath, Also you reusing the cell so your selection of cell is change when you scroll the CollectionView for preventing this issue create one instance of type [IndexPath] and access it in your collectionView's method like this.

var selectedCell = [IndexPath]()

func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
    let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath) as! MyCollectionViewCell
    cell.titleLabel?.text = arrayLocation[indexPath.row]
    cell.titleLabel.font = UIFont.systemFont(ofSize: 24)
    if selectedCell.contains(indexPath) {
        cell.contentView.backgroundColor = .red
    }
    else {
        cell.contentView.backgroundColor = .white
    }        
    return cell
}

func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {        
    let cell = collectionView.cellForItem(at: indexPath)!
    selectedCell.append(indexPath)
    cell.contentView.backgroundColor = .red
}

func collectionView(_ collectionView: UICollectionView, didDeselectItemAt indexPath: IndexPath) {
    let cell = collectionView.cellForItem(at: indexPath)!
    if selectedCell.contains(indexPath) {
        selectedCell.remove(at: selectedCell.index(of: indexPath)!)
        cell.contentView.backgroundColor = .white
    }
}

sorry mate I have another question. how to make a button can chose all of cell?

uicollectionview - Swift how to deselect Collection Cell - Stack Overf...

swift uicollectionview swift3 uicollectionviewcell