I know this is fairly easy for UITableViewCells but I'm not sure how to approach this using a UICollectionView.
EDIT. Pictures for clarification. Text content of the cells are not the same here but they should be. In landscape:
In portrait:
I tried to naively switch the color of my cell's text label with the cell's background color based on the index path's row property in the cellForItemAtIndexPath:
method. However Index Path's row property isn't really a row in UICollectionViewFlowLayout.
This is yucky, subject to breaking if there's a header, but at least this will get you going. In cellForItemAtIndexPath...
Neither the collection view nor its layout will tell you a “row number” for items. You have to compute it yourself.
If all of your measurements are uniform (which is the case if you didn't implement any
UICollectionViewDelegateFlowLayout
methods), you can compute it pretty easily.There are a few places you could do the computation and assign the color. I'm going to show you the “proper” place to do it: in the layout manager.
The “Knowing When to Subclass the Flow Layout” section of the Collection View Programming Guide for iOS tells you the basic things you need to do, but it's pretty bare bones, so I'll walk you through it.
Note: since
layoutAttributes
is a pretty cumbersome identifier, I usually usepose
instead.UICollectionViewLayoutAttributes
subclassFirst of all, we need a way to pass the background color from the layout manager to the cell. The right way to do that is by subclassing
UICollectionViewLayoutAttributes
. The interface just adds one property:The implementation needs to implement the
NSCopying
protocol:UICollectionViewCell
subclassNext, the cell needs to use that
backgroundColor
attributes. You probably already have aUICollectionViewCell
subclass. You need to implementapplyLayoutAttributes:
in your subclass, like this:UICollectionViewFlowLayout
subclassNow you need to make a subclass of
UICollectionViewFlowLayout
that usesMyLayoutAttributes
instead ofUICollectionViewLayoutAttributes
, and sets thebackgroundColor
of eachMyLayoutAttributes
correctly. Let's define the interface to have a property which is the array of colors to assign to rows:The first thing we need to do in our implementation is specify what layout attributes class we want it to use:
Next, we need to override two methods of
UICollectionViewLayout
to set thebackgroundColor
property of each pose. We call onsuper
to get the set of poses, and then use a helper method to set the background colors:Here's the method that actually assigns the background colors:
Note that this method makes several assumptions:
The reason for the last assumption is that all rows in a section are followed by the minimum line spacing, except for the last row, which is followed by the bottom section inset. I need to know how many rows preceded the current item's row. I try to do that by dividing the Y coordinate by the height of a row… but the last row of each section is shorter than the others. Hence the fudging.
Applying Your New Classes
Anyway, now we need to put these new classes to use.
First, we need to use
MyLayout
instead ofUICollectionViewFlowLayout
. If you're setting up your collection view in code, just create an instead ofMyLayout
and assign it to the collection view. If you're setting things up in a storyboard, find the collection view's layout (it is in the storyboard outliner as a child of the collection view) and set its custom class toMyLayout
.Second, we need to assign an array of colors to the layout. If you're using a storyboard, you probably want to do this in
viewDidLoad
. You could ask the collection view for its layout and then cast it toMyLayout
. I prefer to use an outlet of the proper type, connected to the layout object in the storyboard.Result
If you got everything set up correctly, you'll get a result like this:
and in landscape orientation it looks like this:
I've put my test project in this github repository.
I don' think there's any good general way to do this without subclassing the flow layout. For a more specific case, you can use integer math combined with the modulus operator to get "rows" from the indexPath. So, if you had 5 items on each row, you could return either 0 or 1 from this expression:
Just test this value, and change your color accordingly. Of course, you'll have to change the expression on rotation since you'll have a different number of items on each row.
Something like this should do it. May take some tweaking, etc. Assuming that you are not using sections which would have to have some changes..
Use the indexPath.section property to get a row.