Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions TimesSquare.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
EFD8DE6F167AF78600F87FBE /* TSQCalendarMonthHeaderCell.h in Headers */ = {isa = PBXBuildFile; fileRef = A8068088167010030071C71E /* TSQCalendarMonthHeaderCell.h */; settings = {ATTRIBUTES = (Public, ); }; };
EFD8DE70167AF78C00F87FBE /* TSQCalendarRowCell.h in Headers */ = {isa = PBXBuildFile; fileRef = A806808A167010030071C71E /* TSQCalendarRowCell.h */; settings = {ATTRIBUTES = (Public, ); }; };
EFD8DE71167AF79000F87FBE /* TSQCalendarView.h in Headers */ = {isa = PBXBuildFile; fileRef = A806808C167010030071C71E /* TSQCalendarView.h */; settings = {ATTRIBUTES = (Public, ); }; };
F6C9C449173D29C3004EA826 /* TSQCalendarRowButton.h in Headers */ = {isa = PBXBuildFile; fileRef = F6C9C447173D29C3004EA826 /* TSQCalendarRowButton.h */; };
F6C9C44A173D29C3004EA826 /* TSQCalendarRowButton.m in Sources */ = {isa = PBXBuildFile; fileRef = F6C9C448173D29C3004EA826 /* TSQCalendarRowButton.m */; };
/* End PBXBuildFile section */

/* Begin PBXCopyFilesBuildPhase section */
Expand Down Expand Up @@ -48,6 +50,8 @@
A806808C167010030071C71E /* TSQCalendarView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TSQCalendarView.h; sourceTree = "<group>"; };
A806808D167010030071C71E /* TSQCalendarView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TSQCalendarView.m; sourceTree = "<group>"; };
A806809E167012980071C71E /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; };
F6C9C447173D29C3004EA826 /* TSQCalendarRowButton.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TSQCalendarRowButton.h; sourceTree = "<group>"; };
F6C9C448173D29C3004EA826 /* TSQCalendarRowButton.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TSQCalendarRowButton.m; sourceTree = "<group>"; };
/* End PBXFileReference section */

/* Begin PBXFrameworksBuildPhase section */
Expand Down Expand Up @@ -99,6 +103,8 @@
A8068087167010030071C71E /* TSQCalendarCell.m */,
A8068088167010030071C71E /* TSQCalendarMonthHeaderCell.h */,
A8068089167010030071C71E /* TSQCalendarMonthHeaderCell.m */,
F6C9C447173D29C3004EA826 /* TSQCalendarRowButton.h */,
F6C9C448173D29C3004EA826 /* TSQCalendarRowButton.m */,
A806808A167010030071C71E /* TSQCalendarRowCell.h */,
A806808B167010030071C71E /* TSQCalendarRowCell.m */,
A806808C167010030071C71E /* TSQCalendarView.h */,
Expand Down Expand Up @@ -128,6 +134,7 @@
EFD8DE6F167AF78600F87FBE /* TSQCalendarMonthHeaderCell.h in Headers */,
EFD8DE70167AF78C00F87FBE /* TSQCalendarRowCell.h in Headers */,
EFD8DE71167AF79000F87FBE /* TSQCalendarView.h in Headers */,
F6C9C449173D29C3004EA826 /* TSQCalendarRowButton.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down Expand Up @@ -205,6 +212,7 @@
A8068090167010030071C71E /* TSQCalendarMonthHeaderCell.m in Sources */,
A8068092167010030071C71E /* TSQCalendarRowCell.m in Sources */,
A8068094167010030071C71E /* TSQCalendarView.m in Sources */,
F6C9C44A173D29C3004EA826 /* TSQCalendarRowButton.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down
34 changes: 34 additions & 0 deletions TimesSquare/TSQCalendarRowButton.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
//
// TSQCalendarButton.h
// TimesSquare
//
// Created by Simon Booth on 10/05/2013.
// Licensed to Square, Inc. under one or more contributor license agreements.
// See the LICENSE file distributed with this work for the terms under
// which Square, Inc. licenses this file to you.


#import <UIKit/UIKit.h>
#import "TSQCalendarRowCell.h"

/** The `TSQCalendarRowButton` class is a button that represents single day in the calendar.

The button contains an additional label which is used to display an event marker.
*/
@interface TSQCalendarRowButton : UIButton

/** A label used to display an event marker

The marker is shown using the bullet character '•'

*/
@property (nonatomic, strong, readonly) UILabel *subtitleLabel;

/** Configures the button according to the given row's properties

The button is set up using the text color and shadow offset of the row

*/
- (void)configureWithRowCell:(TSQCalendarRowCell *)rowCell;

@end
82 changes: 82 additions & 0 deletions TimesSquare/TSQCalendarRowButton.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
//
// TSQCalendarButton.m
// TimesSquare
//
// Created by Simon Booth on 10/05/2013.
// Licensed to Square, Inc. under one or more contributor license agreements.
// See the LICENSE file distributed with this work for the terms under
// which Square, Inc. licenses this file to you.

#import "TSQCalendarRowButton.h"


@implementation TSQCalendarRowButton

- (id)initWithFrame:(CGRect)frame;
{
self = [super initWithFrame:frame];
if (self) {
self.titleLabel.font = [UIFont boldSystemFontOfSize:19.f];
self.adjustsImageWhenDisabled = NO;
[self setTitleShadowColor:[UIColor whiteColor] forState:UIControlStateNormal];

_subtitleLabel = [[UILabel alloc] init];
_subtitleLabel.backgroundColor = [UIColor clearColor];
_subtitleLabel.font = self.titleLabel.font;
_subtitleLabel.textAlignment = UITextAlignmentCenter;
[self addSubview:_subtitleLabel];

[self updateSubtitleLabel];
}
return self;
}

- (void)configureWithRowCell:(TSQCalendarRowCell *)rowCell;
{
[self setTitleColor:rowCell.textColor forState:UIControlStateNormal];
self.titleLabel.shadowOffset = rowCell.shadowOffset;
[self updateSubtitleLabel];
}

- (void)layoutSubviews;
{
[super layoutSubviews];

CGRect subtitleFrame = self.bounds;
subtitleFrame.origin.y = subtitleFrame.size.height - 15;
subtitleFrame.size.height = 15;
self.subtitleLabel.frame = subtitleFrame;
}

- (void)updateSubtitleLabel;
{
self.subtitleLabel.textColor = self.currentTitleColor;
self.subtitleLabel.shadowColor = self.currentTitleShadowColor;
self.subtitleLabel.shadowOffset = self.titleLabel.shadowOffset;
}

- (void)setTitleShadowColor:(UIColor *)color forState:(UIControlState)state;
{
[super setTitleShadowColor:color forState:state];
[self updateSubtitleLabel];
}

- (void)setTitleColor:(UIColor *)color forState:(UIControlState)state;
{
[super setTitleColor:color forState:state];
[self updateSubtitleLabel];
}

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

- (void)setSelected:(BOOL)selected;
{
[super setSelected:selected];
[self updateSubtitleLabel];
}

@end
8 changes: 8 additions & 0 deletions TimesSquare/TSQCalendarRowCell.h
Original file line number Diff line number Diff line change
Expand Up @@ -65,4 +65,12 @@
*/
- (void)selectColumnForDate:(NSDate *)date;

/** @name Button configuration */

/** The button class to use for day buttons.

The class should be a subclass of `TSQCalendarRowButton` or at least implement all of its methods.
*/
@property (nonatomic, strong) Class rowButtonClass;

@end
44 changes: 24 additions & 20 deletions TimesSquare/TSQCalendarRowCell.m
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,14 @@

#import "TSQCalendarRowCell.h"
#import "TSQCalendarView.h"

#import "TSQCalendarRowButton.h"

@interface TSQCalendarRowCell ()

@property (nonatomic, strong) NSArray *dayButtons;
@property (nonatomic, strong) NSArray *notThisMonthButtons;
@property (nonatomic, strong) UIButton *todayButton;
@property (nonatomic, strong) UIButton *selectedButton;
@property (nonatomic, strong) TSQCalendarRowButton *todayButton;
@property (nonatomic, strong) TSQCalendarRowButton *selectedButton;

@property (nonatomic, assign) NSInteger indexOfTodayButton;
@property (nonatomic, assign) NSInteger indexOfSelectedButton;
Expand All @@ -42,24 +42,23 @@ - (id)initWithCalendar:(NSCalendar *)calendar reuseIdentifier:(NSString *)reuseI
return self;
}

- (void)configureButton:(UIButton *)button;
- (Class)rowButtonClass;
{
button.titleLabel.font = [UIFont boldSystemFontOfSize:19.f];
button.titleLabel.shadowOffset = self.shadowOffset;
button.adjustsImageWhenDisabled = NO;
[button setTitleColor:self.textColor forState:UIControlStateNormal];
[button setTitleShadowColor:[UIColor whiteColor] forState:UIControlStateNormal];
if (!_rowButtonClass) {
self.rowButtonClass = [TSQCalendarRowButton class];
}
return _rowButtonClass;
}

- (void)createDayButtons;
{
NSMutableArray *dayButtons = [NSMutableArray arrayWithCapacity:self.daysInWeek];
for (NSUInteger index = 0; index < self.daysInWeek; index++) {
UIButton *button = [[UIButton alloc] initWithFrame:self.contentView.bounds];
TSQCalendarRowButton *button = [[self.rowButtonClass alloc] initWithFrame:self.contentView.bounds];
[button addTarget:self action:@selector(dateButtonPressed:) forControlEvents:UIControlEventTouchDown];
[dayButtons addObject:button];
[self.contentView addSubview:button];
[self configureButton:button];
[button configureWithRowCell:self];
[button setTitleColor:[self.textColor colorWithAlphaComponent:0.5f] forState:UIControlStateDisabled];
}
self.dayButtons = dayButtons;
Expand All @@ -69,10 +68,10 @@ - (void)createNotThisMonthButtons;
{
NSMutableArray *notThisMonthButtons = [NSMutableArray arrayWithCapacity:self.daysInWeek];
for (NSUInteger index = 0; index < self.daysInWeek; index++) {
UIButton *button = [[UIButton alloc] initWithFrame:self.contentView.bounds];
TSQCalendarRowButton *button = [[self.rowButtonClass alloc] initWithFrame:self.contentView.bounds];
[notThisMonthButtons addObject:button];
[self.contentView addSubview:button];
[self configureButton:button];
[button configureWithRowCell:self];

button.enabled = NO;
UIColor *backgroundPattern = [UIColor colorWithPatternImage:[self notThisMonthBackgroundImage]];
Expand All @@ -84,9 +83,9 @@ - (void)createNotThisMonthButtons;

- (void)createTodayButton;
{
self.todayButton = [[UIButton alloc] initWithFrame:self.contentView.bounds];
self.todayButton = [[self.rowButtonClass alloc] initWithFrame:self.contentView.bounds];
[self.contentView addSubview:self.todayButton];
[self configureButton:self.todayButton];
[self.todayButton configureWithRowCell:self];
[self.todayButton addTarget:self action:@selector(todayButtonPressed:) forControlEvents:UIControlEventTouchDown];

[self.todayButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
Expand All @@ -98,9 +97,9 @@ - (void)createTodayButton;

- (void)createSelectedButton;
{
self.selectedButton = [[UIButton alloc] initWithFrame:self.contentView.bounds];
self.selectedButton = [[self.rowButtonClass alloc] initWithFrame:self.contentView.bounds];
[self.contentView addSubview:self.selectedButton];
[self configureButton:self.selectedButton];
[self.selectedButton configureWithRowCell:self];

[self.selectedButton setAccessibilityTraits:UIAccessibilityTraitSelected|self.selectedButton.accessibilityTraits];

Expand All @@ -127,11 +126,14 @@ - (void)setBeginningDate:(NSDate *)date;

for (NSUInteger index = 0; index < self.daysInWeek; index++) {
NSString *title = [self.dayFormatter stringFromDate:date];
NSString *subTitle = [self.calendarView shouldDisplayEventMarkerForDate:date] ? @"•" : nil;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you extract this logic out? Have a -setEventMarkerVisible:(BOOL)visible forColumnAtIndex:(NSUInteger)index; that does this and can be overridden by the subclass.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had a look at this, and I ran into a problem in -[selectColumnForDate:] where the day button's properties get copied to the selected button. Would it be OK instead to change the button class so it had an eventMarkerVisible property?

NSString *accessibilityLabel = [self.accessibilityFormatter stringFromDate:date];
[self.dayButtons[index] setTitle:title forState:UIControlStateNormal];
[self.dayButtons[index] setAccessibilityLabel:accessibilityLabel];
[[self.dayButtons[index] subtitleLabel] setText:subTitle];
[self.notThisMonthButtons[index] setTitle:title forState:UIControlStateNormal];
[self.notThisMonthButtons[index] setAccessibilityLabel:accessibilityLabel];
[[self.notThisMonthButtons[index] subtitleLabel] setText:subTitle];
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(Move these in to that extracted method, too.)


NSDateComponents *thisDateComponents = [self.calendar components:NSDayCalendarUnit|NSMonthCalendarUnit|NSYearCalendarUnit fromDate:date];

Expand All @@ -147,9 +149,10 @@ - (void)setBeginningDate:(NSDate *)date;
self.todayButton.hidden = NO;
[self.todayButton setTitle:title forState:UIControlStateNormal];
[self.todayButton setAccessibilityLabel:accessibilityLabel];
[self.todayButton.subtitleLabel setText:subTitle];
self.indexOfTodayButton = index;
} else {
UIButton *button = self.dayButtons[index];
TSQCalendarRowButton *button = self.dayButtons[index];
button.enabled = ![self.calendarView.delegate respondsToSelector:@selector(calendarView:shouldSelectDate:)] || [self.calendarView.delegate calendarView:self.calendarView shouldSelectDate:date];
button.hidden = NO;
}
Expand Down Expand Up @@ -209,8 +212,8 @@ - (void)layoutSubviews;

- (void)layoutViewsForColumnAtIndex:(NSUInteger)index inRect:(CGRect)rect;
{
UIButton *dayButton = self.dayButtons[index];
UIButton *notThisMonthButton = self.notThisMonthButtons[index];
TSQCalendarRowButton *dayButton = self.dayButtons[index];
TSQCalendarRowButton *notThisMonthButton = self.notThisMonthButtons[index];

dayButton.frame = rect;
notThisMonthButton.frame = rect;
Expand Down Expand Up @@ -246,6 +249,7 @@ - (void)selectColumnForDate:(NSDate *)date;
self.selectedButton.hidden = NO;
[self.selectedButton setTitle:[self.dayButtons[newIndexOfSelectedButton] currentTitle] forState:UIControlStateNormal];
[self.selectedButton setAccessibilityLabel:[self.dayButtons[newIndexOfSelectedButton] accessibilityLabel]];
[self.selectedButton.subtitleLabel setText:[[self.dayButtons[newIndexOfSelectedButton] subtitleLabel] text]];
} else {
self.selectedButton.hidden = YES;
}
Expand Down
19 changes: 19 additions & 0 deletions TimesSquare/TSQCalendarView.h
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,15 @@
*/
- (void)scrollToDate:(NSDate *)date animated:(BOOL)animated;

/** Whether a particular date should display an event marker

This method passes straight through to the delegate

@param date The date being displayed.
@return Whether or not the date should display an event marker.
*/
- (BOOL)shouldDisplayEventMarkerForDate:(NSDate *)date;

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't need to be public, does it?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It needs to be visible from inside TSQCalendarRowCell - it's used at line 128:

NSString *subTitle = [self.calendarView shouldDisplayEventMarkerForDate:date] ? @"•" : nil;

@end

/** The methods in the `TSQCalendarViewDelegate` protocol allow the adopting delegate to either prevent a day from being selected or respond to it.
Expand Down Expand Up @@ -131,4 +140,14 @@
*/
- (void)calendarView:(TSQCalendarView *)calendarView didSelectDate:(NSDate *)date;

/** @name Displaying event markers */

/** Asks the delegate whether a particular date should display an event marker

@param calendarView The calendar view that is displaying a date.
@param date The date being displayed.
@return Whether or not the date should display an event marker.
*/
- (BOOL)calendarView:(TSQCalendarView *)calendarView shouldDisplayEventMarkerForDate:(NSDate *)date;

@end
9 changes: 9 additions & 0 deletions TimesSquare/TSQCalendarView.m
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,15 @@ - (void)scrollToDate:(NSDate *)date animated:(BOOL)animated
[self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:section] atScrollPosition:UITableViewScrollPositionTop animated:animated];
}

- (BOOL)shouldDisplayEventMarkerForDate:(NSDate *)date;
{
if ([self.delegate respondsToSelector:@selector(calendarView:shouldDisplayEventMarkerForDate:)]) {
return [self.delegate calendarView:self shouldDisplayEventMarkerForDate:date];
}

return NO;
}

- (TSQCalendarMonthHeaderCell *)makeHeaderCellWithIdentifier:(NSString *)identifier;
{
TSQCalendarMonthHeaderCell *cell = [[[self headerCellClass] alloc] initWithCalendar:self.calendar reuseIdentifier:identifier];
Expand Down
11 changes: 10 additions & 1 deletion TimesSquareTestApp/TSQTAViewController.m
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
#import <TimesSquare/TimesSquare.h>


@interface TSQTAViewController ()
@interface TSQTAViewController () <TSQCalendarViewDelegate>

@property (nonatomic, retain) NSTimer *timer;

Expand All @@ -39,6 +39,7 @@ - (void)loadView;
calendarView.pagingEnabled = YES;
CGFloat onePixel = 1.0f / [UIScreen mainScreen].scale;
calendarView.contentInset = UIEdgeInsetsMake(0.0f, onePixel, 0.0f, onePixel);
calendarView.delegate = self;

self.view = calendarView;
}
Expand Down Expand Up @@ -81,4 +82,12 @@ - (void)scroll;
atTop = !atTop;
}

- (BOOL)calendarView:(TSQCalendarView *)calendarView shouldDisplayEventMarkerForDate:(NSDate *)date;
{
NSDateComponents *components = [calendarView.calendar components:NSMonthCalendarUnit|NSDayCalendarUnit fromDate:date];

// This gives a nice pattern
return (components.day % 9 == components.month % 9) || (components.day % 11 == components.month % 11);
}

@end