Imitating the Calendar app GUI

Imitating the Calendar app GUI

We wanted our calendar to look similar to the list view from the Calendar app on the iPhone. It’s trickier than it sounds, though. Upon close inspection, you’ll notice that the event’s title is centered in the cell if the event does not have a location. If it does have a location, the title shifts up slightly and the location fits underneath it.

We tried to replicate this behavior using table cells designed in XIB files and by configuring standard table cells programmatically, but it just didn’t work. So we wrote a custom UITableViewCell subclass that handles the layout for us. Here’s an excerpt from our table view cell subclass. It’s the bit that actually performs the layout.

// Note: This is not the complete listing of this class.
// Helper function that calculates label positions.
// NOTE: This function resides OUTSIDE the @implementation block.
CGRect rectForTitleView(BOOL slideUp)
{
    if (slideUp)
    {
        return CGRectMake(70.0, 3.0, 230.0, 21.0);
    }
    else {
        return CGRectMake(70.0, 11.5, 230.0, 21.0);
    }
}

// This is where the view customizes itself to the new event.
// It moves around and hides and updates views.
// NOTE: This method resides INSIDE the @implementation block.
- (void)layoutSubviews
{
    [super layoutSubviews];

    // Title
    titleLabel.text = self.event.title;

    // Location
    if (self.event.location != nil)
    {
        locationLabel.text = self.event.location;
        locationLabel.hidden = NO;
        titleLabel.frame = rectForTitleView(YES);
    }
    else
    {
        locationLabel.hidden = YES;
        titleLabel.frame = rectForTitleView(NO);
    }

    // Time
    if (self.event.isAllDayEvent)
    {
        timeLabel.hidden = YES;
        amPMLabel.hidden = YES;
        allDayLabel.hidden = NO;
    }
    else
    {
        allDayLabel.hidden = YES;
        amPMLabel.hidden = NO;
        timeLabel.hidden = NO;
        // have to format the time and day
        timeLabel.text =
            hourWithOptionalMinutes(self.event.startDate);
        if (isBeforeMidDay(self.event.startDate))
        {
            amPMLabel.text = @"AM";
        }
        else
        {
            amPMLabel.text = @"PM";
        }
    }
}

When the data (the self.event key) gets set, we simply call [self setNeedsLayout], and the UIView calls our layoutSubviews method when the cell is ready to be displayed. Here’s a screen shot of the finished product:

A screenshot of the finished list view

If you want more details about the code, leave us a comment or send us an email!

Leave a Reply

Your email address will not be published. Required fields are marked *