I'm building an app where I really need to display a list like the one seen in the iOS calendar app. I need to create a collectionView where I can have cells that expand enough to cover their respective hours, like so:
I've tried various things, including this project at Github which I didn't understand how to use in another project
As well as this project I quickly made with a UITableViewController while exploring different methods:
But I'm not really getting where I want to. I need to have the design seen in the first picture and was wondering if anyone could point me in the right direction to achieve that?
Thank you so much for help!!
Update:
Shouldn't the actual cell frame represent the start of an hour, 10:00 in this example? (Rather than the custom-made separator)
Update
Why does the hierarchy look like this:
This kind of positioning appears to be "blocking" the touch of the cells and the didSelectItemAtIndexPath
method doesn't get called. This applies to the cells that appear further back than the other ones.
As per discussion in comments on the question, using the Custom Collection View Layout library linked in the question is suitable for the mentioned problem which can be easily integrated using CocoaPods.
UICollectionView is highly customizable using custom
UICollectionViewLayout
. See the official apple documentation for creating custom layouts.To be frank, Apple's documentation is too overwhelming for most of us. So, you can look up (google) custom UICollectionViewLayout tutorial, go through any one you find the easiest and go on to use the library to your specific needs.
I don't think this is so hard to do.
You just need a different perspective, than what you tried.
Make all the CollectionView cells the same size. They represent 1h time, so they should have equal size.
Then have another view in each cell. This is the one that show that there is an event. This view you will give a color, and place it in the cell according to the time when the event start or finish. So in the cellForRowAtIndexPath, you would check your events and if this cell have any event then you are going to show this event view, color it, and position it. If you make the cells height 60, it gets super easy to position the view. For example if the event start at 1:20, at the cell for hour 1, you will size the event view (0, 20, 320, 40).
You should indeed use a
UICollectionView
along with a custom layout. Just providesupplementary views
to build the underlying daily schedule (one view per hour), and then use thecells
to lay down your events.When subclassing
UICollectionViewLayout
, you need to implement a few methods:collectionViewContentSize
should return a height equal to the number of hours (24) multiplied by the height of a supplementary view representing an hour.prepareLayout
does almost everything. In that method, you calculate every layoutAttributes you will need to use. By getting the time of the event and its duration, you are able to compute the frame of every event. The supplementary view frames (every single "hour" block) are pretty straightforward too, since their height is fixed (origin.y = fixed height multiplied by the hour).layoutAttributesForElementsInRect:
simply iterates through your previously prepared layoutAttributes and returns all whose frame intersect the provided frame.layoutAttributesForItemAtIndexPath:
finds and returns the cell's layoutAttributes that match the provided indexPath.layoutAttributesForSupplementaryViewOfKind:atIndexPath
finds and returns the supplementary view's layoutAttributes that math the provided indexPath.Next, you give an instance of that layout to a collection view:
Then, you only need to provide the required views for the supplementary views (a.k.a the hour blocks) and the cells, via the UICollectionView's delegate & dataSource. Feel free to create a custom delegate in your custom layout if you need the UIViewController to provide more info that you have with the standard delegate/dataSource!
Note: Since the line that indicate the start of an hour is a few pixels below the cell's top border, you need to shift every time of event by the same number of pixels. Say you have 6 pixels above every line and every hour block has a height of 60 pixels, then if an even start at 2AM, you will set its
origin.y = 2 * 60 + 6
(2 hours * 60 pixels per hour + 6 pixels padding). You will also need to adjust you last cell block to be 6 pixels taller since it won't have another cell below.I recommend you to read the official documentation about creating custom layouts.
To help you: I made a quick sample project since making your own layout can sometime be troublesome. Go check the
CalendarViewLayout
class, I added a few comments to explain how I dealt with the padding. Here is was it looks like: