diff --git a/DayFlow.podspec b/DayFlow.podspec deleted file mode 100644 index 67086b6..0000000 --- a/DayFlow.podspec +++ /dev/null @@ -1,14 +0,0 @@ -Pod::Spec.new do |s| - s.name = "DayFlow" - s.version = "0.0.2" - s.summary = "Strollable Date Picker." - s.homepage = "http://github.com/evadne/DayFlow" - s.license = 'MIT' - s.author = { "Evadne Wu" => "ev@radi.ws" } - s.source = { :git => "http://github.com/evadne/DayFlow.git", :tag => "0.0.2" } - s.platform = :ios, '6.0' - s.source_files = 'DayFlow', 'DayFlow/**/*.{h,m}' - s.exclude_files = 'DayFlow/Exclude' - s.frameworks = 'QuartzCore', 'UIKit' - s.requires_arc = true -end diff --git a/DayFlow.xcodeproj/project.pbxproj b/DayFlow.xcodeproj/project.pbxproj deleted file mode 100644 index 97539ee..0000000 --- a/DayFlow.xcodeproj/project.pbxproj +++ /dev/null @@ -1,278 +0,0 @@ -// !$*UTF8*$! -{ - archiveVersion = 1; - classes = { - }; - objectVersion = 46; - objects = { - -/* Begin PBXBuildFile section */ - FF2A69CE172D2C07005A1A21 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FF2A69CD172D2C07005A1A21 /* Foundation.framework */; }; - FF2A69D3172D2C07005A1A21 /* DayFlow.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = FF2A69D2172D2C07005A1A21 /* DayFlow.h */; }; - FF2A69DD172D2C64005A1A21 /* DFDatePickerView.m in Sources */ = {isa = PBXBuildFile; fileRef = FF2A69DC172D2C64005A1A21 /* DFDatePickerView.m */; }; - FFB686AD172E8A660027ADF2 /* NSCalendar+DFAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = FFB686A3172E8A660027ADF2 /* NSCalendar+DFAdditions.m */; }; - FFB686AE172E8A660027ADF2 /* DFDatePickerViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = FFB686A5172E8A660027ADF2 /* DFDatePickerViewController.m */; }; - FFB686AF172E8A660027ADF2 /* DFDatePickerMonthHeader.m in Sources */ = {isa = PBXBuildFile; fileRef = FFB686A7172E8A660027ADF2 /* DFDatePickerMonthHeader.m */; }; - FFB686B0172E8A660027ADF2 /* DFDatePickerDayCell.m in Sources */ = {isa = PBXBuildFile; fileRef = FFB686A9172E8A660027ADF2 /* DFDatePickerDayCell.m */; }; - FFB686B1172E8A660027ADF2 /* DFDatePickerCollectionView.m in Sources */ = {isa = PBXBuildFile; fileRef = FFB686AB172E8A660027ADF2 /* DFDatePickerCollectionView.m */; }; - FFB686B3172E8A970027ADF2 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FFB686B2172E8A970027ADF2 /* UIKit.framework */; }; -/* End PBXBuildFile section */ - -/* Begin PBXCopyFilesBuildPhase section */ - FF2A69C8172D2C07005A1A21 /* CopyFiles */ = { - isa = PBXCopyFilesBuildPhase; - buildActionMask = 2147483647; - dstPath = "include/${PRODUCT_NAME}"; - dstSubfolderSpec = 16; - files = ( - FF2A69D3172D2C07005A1A21 /* DayFlow.h in CopyFiles */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXCopyFilesBuildPhase section */ - -/* Begin PBXFileReference section */ - FF2A69CA172D2C07005A1A21 /* libDayFlow.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libDayFlow.a; sourceTree = BUILT_PRODUCTS_DIR; }; - FF2A69CD172D2C07005A1A21 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; - FF2A69D1172D2C07005A1A21 /* DayFlow-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "DayFlow-Prefix.pch"; sourceTree = ""; }; - FF2A69D2172D2C07005A1A21 /* DayFlow.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = DayFlow.h; path = DayFlow/DayFlow.h; sourceTree = ""; }; - FF2A69DB172D2C64005A1A21 /* DFDatePickerView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DFDatePickerView.h; path = DayFlow/DFDatePickerView.h; sourceTree = ""; }; - FF2A69DC172D2C64005A1A21 /* DFDatePickerView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = DFDatePickerView.m; path = DayFlow/DFDatePickerView.m; sourceTree = ""; }; - FFB686A3172E8A660027ADF2 /* NSCalendar+DFAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "NSCalendar+DFAdditions.m"; path = "DayFlow/NSCalendar+DFAdditions.m"; sourceTree = ""; }; - FFB686A4172E8A660027ADF2 /* NSCalendar+DFAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "NSCalendar+DFAdditions.h"; path = "DayFlow/NSCalendar+DFAdditions.h"; sourceTree = ""; }; - FFB686A5172E8A660027ADF2 /* DFDatePickerViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = DFDatePickerViewController.m; path = DayFlow/DFDatePickerViewController.m; sourceTree = ""; }; - FFB686A6172E8A660027ADF2 /* DFDatePickerViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DFDatePickerViewController.h; path = DayFlow/DFDatePickerViewController.h; sourceTree = ""; }; - FFB686A7172E8A660027ADF2 /* DFDatePickerMonthHeader.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = DFDatePickerMonthHeader.m; path = DayFlow/DFDatePickerMonthHeader.m; sourceTree = ""; }; - FFB686A8172E8A660027ADF2 /* DFDatePickerMonthHeader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DFDatePickerMonthHeader.h; path = DayFlow/DFDatePickerMonthHeader.h; sourceTree = ""; }; - FFB686A9172E8A660027ADF2 /* DFDatePickerDayCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = DFDatePickerDayCell.m; path = DayFlow/DFDatePickerDayCell.m; sourceTree = ""; }; - FFB686AA172E8A660027ADF2 /* DFDatePickerDayCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DFDatePickerDayCell.h; path = DayFlow/DFDatePickerDayCell.h; sourceTree = ""; }; - FFB686AB172E8A660027ADF2 /* DFDatePickerCollectionView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = DFDatePickerCollectionView.m; path = DayFlow/DFDatePickerCollectionView.m; sourceTree = ""; }; - FFB686AC172E8A660027ADF2 /* DFDatePickerCollectionView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DFDatePickerCollectionView.h; path = DayFlow/DFDatePickerCollectionView.h; sourceTree = ""; }; - FFB686B2172E8A970027ADF2 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; -/* End PBXFileReference section */ - -/* Begin PBXFrameworksBuildPhase section */ - FF2A69C7172D2C07005A1A21 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - FF2A69CE172D2C07005A1A21 /* Foundation.framework in Frameworks */, - FFB686B3172E8A970027ADF2 /* UIKit.framework in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXFrameworksBuildPhase section */ - -/* Begin PBXGroup section */ - FF2A69C1172D2C06005A1A21 = { - isa = PBXGroup; - children = ( - FF2A69D2172D2C07005A1A21 /* DayFlow.h */, - FFB686A6172E8A660027ADF2 /* DFDatePickerViewController.h */, - FFB686A5172E8A660027ADF2 /* DFDatePickerViewController.m */, - FF2A69DB172D2C64005A1A21 /* DFDatePickerView.h */, - FF2A69DC172D2C64005A1A21 /* DFDatePickerView.m */, - FFB686AC172E8A660027ADF2 /* DFDatePickerCollectionView.h */, - FFB686AB172E8A660027ADF2 /* DFDatePickerCollectionView.m */, - FFB686A8172E8A660027ADF2 /* DFDatePickerMonthHeader.h */, - FFB686A7172E8A660027ADF2 /* DFDatePickerMonthHeader.m */, - FFB686AA172E8A660027ADF2 /* DFDatePickerDayCell.h */, - FFB686A9172E8A660027ADF2 /* DFDatePickerDayCell.m */, - FFB686A4172E8A660027ADF2 /* NSCalendar+DFAdditions.h */, - FFB686A3172E8A660027ADF2 /* NSCalendar+DFAdditions.m */, - FF2A69D0172D2C07005A1A21 /* Supporting Files */, - FF2A69CC172D2C07005A1A21 /* Frameworks */, - FF2A69CB172D2C07005A1A21 /* Products */, - ); - sourceTree = ""; - }; - FF2A69CB172D2C07005A1A21 /* Products */ = { - isa = PBXGroup; - children = ( - FF2A69CA172D2C07005A1A21 /* libDayFlow.a */, - ); - name = Products; - sourceTree = ""; - }; - FF2A69CC172D2C07005A1A21 /* Frameworks */ = { - isa = PBXGroup; - children = ( - FF2A69CD172D2C07005A1A21 /* Foundation.framework */, - FFB686B2172E8A970027ADF2 /* UIKit.framework */, - ); - name = Frameworks; - sourceTree = ""; - }; - FF2A69D0172D2C07005A1A21 /* Supporting Files */ = { - isa = PBXGroup; - children = ( - FF2A69D1172D2C07005A1A21 /* DayFlow-Prefix.pch */, - ); - name = "Supporting Files"; - path = DayFlow; - sourceTree = ""; - }; -/* End PBXGroup section */ - -/* Begin PBXNativeTarget section */ - FF2A69C9172D2C07005A1A21 /* DayFlow */ = { - isa = PBXNativeTarget; - buildConfigurationList = FF2A69D8172D2C07005A1A21 /* Build configuration list for PBXNativeTarget "DayFlow" */; - buildPhases = ( - FF2A69C6172D2C07005A1A21 /* Sources */, - FF2A69C7172D2C07005A1A21 /* Frameworks */, - FF2A69C8172D2C07005A1A21 /* CopyFiles */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = DayFlow; - productName = DayFlow; - productReference = FF2A69CA172D2C07005A1A21 /* libDayFlow.a */; - productType = "com.apple.product-type.library.static"; - }; -/* End PBXNativeTarget section */ - -/* Begin PBXProject section */ - FF2A69C2172D2C06005A1A21 /* Project object */ = { - isa = PBXProject; - attributes = { - ORGANIZATIONNAME = Radius; - }; - buildConfigurationList = FF2A69C5172D2C06005A1A21 /* Build configuration list for PBXProject "DayFlow" */; - compatibilityVersion = "Xcode 3.2"; - developmentRegion = English; - hasScannedForEncodings = 0; - knownRegions = ( - en, - ); - mainGroup = FF2A69C1172D2C06005A1A21; - productRefGroup = FF2A69CB172D2C07005A1A21 /* Products */; - projectDirPath = ""; - projectRoot = ""; - targets = ( - FF2A69C9172D2C07005A1A21 /* DayFlow */, - ); - }; -/* End PBXProject section */ - -/* Begin PBXSourcesBuildPhase section */ - FF2A69C6172D2C07005A1A21 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - FF2A69DD172D2C64005A1A21 /* DFDatePickerView.m in Sources */, - FFB686AD172E8A660027ADF2 /* NSCalendar+DFAdditions.m in Sources */, - FFB686AE172E8A660027ADF2 /* DFDatePickerViewController.m in Sources */, - FFB686AF172E8A660027ADF2 /* DFDatePickerMonthHeader.m in Sources */, - FFB686B0172E8A660027ADF2 /* DFDatePickerDayCell.m in Sources */, - FFB686B1172E8A660027ADF2 /* DFDatePickerCollectionView.m in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXSourcesBuildPhase section */ - -/* Begin XCBuildConfiguration section */ - FF2A69D6172D2C07005A1A21 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - COPY_PHASE_STRIP = NO; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_DYNAMIC_NO_PIC = NO; - GCC_OPTIMIZATION_LEVEL = 0; - GCC_PREPROCESSOR_DEFINITIONS = ( - "DEBUG=1", - "$(inherited)", - ); - GCC_SYMBOLS_PRIVATE_EXTERN = NO; - GCC_WARN_ABOUT_RETURN_TYPE = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 6.1; - ONLY_ACTIVE_ARCH = YES; - SDKROOT = iphoneos; - }; - name = Debug; - }; - FF2A69D7172D2C07005A1A21 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - COPY_PHASE_STRIP = YES; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_WARN_ABOUT_RETURN_TYPE = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 6.1; - SDKROOT = iphoneos; - VALIDATE_PRODUCT = YES; - }; - name = Release; - }; - FF2A69D9172D2C07005A1A21 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - DSTROOT = /tmp/DayFlow.dst; - GCC_PRECOMPILE_PREFIX_HEADER = YES; - GCC_PREFIX_HEADER = "DayFlow/DayFlow-Prefix.pch"; - OTHER_LDFLAGS = "-ObjC"; - PRODUCT_NAME = "$(TARGET_NAME)"; - SKIP_INSTALL = YES; - }; - name = Debug; - }; - FF2A69DA172D2C07005A1A21 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - DSTROOT = /tmp/DayFlow.dst; - GCC_PRECOMPILE_PREFIX_HEADER = YES; - GCC_PREFIX_HEADER = "DayFlow/DayFlow-Prefix.pch"; - OTHER_LDFLAGS = "-ObjC"; - PRODUCT_NAME = "$(TARGET_NAME)"; - SKIP_INSTALL = YES; - }; - name = Release; - }; -/* End XCBuildConfiguration section */ - -/* Begin XCConfigurationList section */ - FF2A69C5172D2C06005A1A21 /* Build configuration list for PBXProject "DayFlow" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - FF2A69D6172D2C07005A1A21 /* Debug */, - FF2A69D7172D2C07005A1A21 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - FF2A69D8172D2C07005A1A21 /* Build configuration list for PBXNativeTarget "DayFlow" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - FF2A69D9172D2C07005A1A21 /* Debug */, - FF2A69DA172D2C07005A1A21 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; -/* End XCConfigurationList section */ - }; - rootObject = FF2A69C2172D2C06005A1A21 /* Project object */; -} diff --git a/DayFlow/DFDatePickerCollectionView.h b/DayFlow/DFDatePickerCollectionView.h deleted file mode 100644 index 9e33aba..0000000 --- a/DayFlow/DFDatePickerCollectionView.h +++ /dev/null @@ -1,14 +0,0 @@ -#import - -@class DFDatePickerCollectionView; -@protocol DFDatePickerCollectionViewDelegate - -- (void) pickerCollectionViewWillLayoutSubviews:(DFDatePickerCollectionView *)pickerCollectionView; - -@end - -@interface DFDatePickerCollectionView : UICollectionView - -@property (nonatomic, assign) id delegate; - -@end diff --git a/DayFlow/DFDatePickerCollectionView.m b/DayFlow/DFDatePickerCollectionView.m deleted file mode 100644 index 6c7dad9..0000000 --- a/DayFlow/DFDatePickerCollectionView.m +++ /dev/null @@ -1,13 +0,0 @@ -#import "DFDatePickerCollectionView.h" - -@implementation DFDatePickerCollectionView -@dynamic delegate; - -- (void) layoutSubviews { - - [self.delegate pickerCollectionViewWillLayoutSubviews:self]; - [super layoutSubviews]; - -} - -@end diff --git a/DayFlow/DFDatePickerDayCell.h b/DayFlow/DFDatePickerDayCell.h deleted file mode 100644 index d14fc77..0000000 --- a/DayFlow/DFDatePickerDayCell.h +++ /dev/null @@ -1,9 +0,0 @@ -#import -#import "DayFlow.h" - -@interface DFDatePickerDayCell : UICollectionViewCell - -@property (nonatomic, readwrite, assign) DFDatePickerDate date; -@property (nonatomic, getter=isEnabled) BOOL enabled; - -@end diff --git a/DayFlow/DFDatePickerDayCell.m b/DayFlow/DFDatePickerDayCell.m deleted file mode 100644 index e565732..0000000 --- a/DayFlow/DFDatePickerDayCell.m +++ /dev/null @@ -1,144 +0,0 @@ -#import "DFDatePickerDayCell.h" - -@interface DFDatePickerDayCell () -+ (NSCache *) imageCache; -+ (id) cacheKeyForPickerDate:(DFDatePickerDate)date; -+ (id) fetchObjectForKey:(id)key withCreator:(id(^)(void))block; -@property (nonatomic, readonly, strong) UIImageView *imageView; -@property (nonatomic, readonly, strong) UIView *overlayView; -@end - -@implementation DFDatePickerDayCell -@synthesize imageView = _imageView; -@synthesize overlayView = _overlayView; - -- (id) initWithFrame:(CGRect)frame { - self = [super initWithFrame:frame]; - if (self) { - self.backgroundColor = [UIColor whiteColor]; - } - return self; -} - -- (void) setDate:(DFDatePickerDate)date { - _date = date; - [self setNeedsLayout]; -} - -- (void) setEnabled:(BOOL)enabled { - _enabled = enabled; - [self setNeedsLayout]; -} - -- (void) setHighlighted:(BOOL)highlighted { - [super setHighlighted:highlighted]; - [self setNeedsLayout]; -} -- (void) setSelected:(BOOL)selected { - [super setSelected:selected]; - [self setNeedsLayout]; -} - -- (void) layoutSubviews { - - [super layoutSubviews]; - - // Instead of using labels, use images keyed by day. - // This avoids redrawing text within labels, which involve lots of parts of - // WebCore and CoreGraphics, and makes sure scrolling is always smooth. - - // Reason: when the view is first shown, all common days are drawn once and cached. - // Memory pressure is also low. - - // Note: Assumption! If there is a calendar with unique day names - // we will be in big trouble. If there is one odd month with 1000 days we will - // also be in some sort of trouble. But for most use cases we are probably good. - - // We still have DFDatePickerMonthHeader take a NSDateFormatter formatted title - // and draw it, but since that’s only one bitmap instead of 35-odd (7 weeks) - // that’s mostly okay. - - self.imageView.alpha = self.enabled ? 1.0f : 0.25f; - - self.imageView.image = [[self class] fetchObjectForKey:[[self class] cacheKeyForPickerDate:self.date] withCreator:^{ - - UIGraphicsBeginImageContextWithOptions(self.bounds.size, YES, self.window.screen.scale); - CGContextRef context = UIGraphicsGetCurrentContext(); - -#if 0 - - // Generate a random color - // https://gist.github.com/kylefox/1689973 - CGFloat hue = ( arc4random() % 256 / 256.0 ); // 0.0 to 1.0 - CGFloat saturation = ( arc4random() % 128 / 256.0 ) + 0.5; // 0.5 to 1.0, away from white - CGFloat brightness = ( arc4random() % 128 / 256.0 ) + 0.5; // 0.5 to 1.0, away from black - CGContextSetFillColorWithColor(context, [UIColor colorWithHue:hue saturation:saturation brightness:brightness alpha:1.0f].CGColor); - -#else - - CGContextSetFillColorWithColor(context, [UIColor colorWithRed:53.0f/256.0f green:145.0f/256.0f blue:195.0f/256.0f alpha:1.0f].CGColor); - -#endif - - CGContextFillRect(context, self.bounds); - - UIFont *font = [UIFont boldSystemFontOfSize:20.0f]; - CGRect textBounds = (CGRect){ 0.0f, 10.0f, 44.0f, 24.0f }; - - CGContextSetFillColorWithColor(context, [UIColor whiteColor].CGColor); - [[NSString stringWithFormat:@"%i", self.date.day] drawInRect:textBounds withFont:font lineBreakMode:NSLineBreakByCharWrapping alignment:NSTextAlignmentCenter]; - - UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); - UIGraphicsEndImageContext(); - - return image; - - }]; - - self.overlayView.hidden = !(self.selected || self.highlighted); - -} - -- (UIView *) overlayView { - if (!_overlayView) { - _overlayView = [[UIView alloc] initWithFrame:self.contentView.bounds]; - _overlayView.autoresizingMask = UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleHeight; - _overlayView.backgroundColor = [UIColor blackColor]; - _overlayView.alpha = 0.25f; - [self.contentView addSubview:_overlayView]; - } - return _overlayView; -} - -- (UIImageView *) imageView { - if (!_imageView) { - _imageView = [[UIImageView alloc] initWithFrame:self.contentView.bounds]; - _imageView.autoresizingMask = UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleHeight; - [self.contentView addSubview:_imageView]; - } - return _imageView; -} - -+ (NSCache *) imageCache { - static NSCache *cache; - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - cache = [NSCache new]; - }); - return cache; -} - -+ (id) cacheKeyForPickerDate:(DFDatePickerDate)date { - return @(date.day); -} - -+ (id) fetchObjectForKey:(id)key withCreator:(id(^)(void))block { - id answer = [[self imageCache] objectForKey:key]; - if (!answer) { - answer = block(); - [[self imageCache] setObject:answer forKey:key]; - } - return answer; -} - -@end diff --git a/DayFlow/DFDatePickerMonthHeader.h b/DayFlow/DFDatePickerMonthHeader.h deleted file mode 100644 index 618f0e4..0000000 --- a/DayFlow/DFDatePickerMonthHeader.h +++ /dev/null @@ -1,7 +0,0 @@ -#import - -@interface DFDatePickerMonthHeader : UICollectionReusableView - -@property (nonatomic, readonly, strong) UILabel *textLabel; - -@end diff --git a/DayFlow/DFDatePickerMonthHeader.m b/DayFlow/DFDatePickerMonthHeader.m deleted file mode 100644 index 4199c51..0000000 --- a/DayFlow/DFDatePickerMonthHeader.m +++ /dev/null @@ -1,17 +0,0 @@ -#import "DFDatePickerMonthHeader.h" - -@implementation DFDatePickerMonthHeader -@synthesize textLabel = _textLabel; - -- (UILabel *) textLabel { - if (!_textLabel) { - _textLabel = [[UILabel alloc] initWithFrame:self.bounds]; - _textLabel.textAlignment = NSTextAlignmentCenter; - _textLabel.font = [UIFont boldSystemFontOfSize:20.0f]; - _textLabel.autoresizingMask = UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleHeight; - [self addSubview:_textLabel]; - } - return _textLabel; -} - -@end diff --git a/DayFlow/DFDatePickerView.h b/DayFlow/DFDatePickerView.h deleted file mode 100644 index 8700f38..0000000 --- a/DayFlow/DFDatePickerView.h +++ /dev/null @@ -1,9 +0,0 @@ -#import - -@interface DFDatePickerView : UIView - -- (instancetype) initWithCalendar:(NSCalendar *)calendar; - -@property (nonatomic, readwrite, strong) NSDate *selectedDate; - -@end diff --git a/DayFlow/DFDatePickerView.m b/DayFlow/DFDatePickerView.m deleted file mode 100644 index 0bc2f42..0000000 --- a/DayFlow/DFDatePickerView.m +++ /dev/null @@ -1,399 +0,0 @@ -#import -#import "DayFlow.h" -#import "DFDatePickerCollectionView.h" -#import "DFDatePickerDayCell.h" -#import "DFDatePickerMonthHeader.h" -#import "DFDatePickerView.h" -#import "NSCalendar+DFAdditions.h" - -static NSString * const DFDatePickerViewCellIdentifier = @"dateCell"; -static NSString * const DFDatePickerViewMonthHeaderIdentifier = @"monthHeader"; - -@interface DFDatePickerView () -@property (nonatomic, readonly, strong) NSCalendar *calendar; -@property (nonatomic, readonly, assign) DFDatePickerDate fromDate; -@property (nonatomic, readonly, assign) DFDatePickerDate toDate; -@property (nonatomic, readonly, strong) UICollectionView *collectionView; -@property (nonatomic, readonly, strong) UICollectionViewFlowLayout *collectionViewLayout; -@end - -@implementation DFDatePickerView -@synthesize calendar = _calendar; -@synthesize fromDate = _fromDate; -@synthesize toDate = _toDate; -@synthesize collectionView = _collectionView; -@synthesize collectionViewLayout = _collectionViewLayout; - -- (instancetype) initWithCalendar:(NSCalendar *)calendar { - - self = [super initWithFrame:CGRectZero]; - if (self) { - - _calendar = calendar; - - NSDate *now = [_calendar dateFromComponents:[_calendar components:NSYearCalendarUnit|NSMonthCalendarUnit fromDate:[NSDate date]]]; - - _fromDate = [self pickerDateFromDate:[_calendar dateByAddingComponents:((^{ - NSDateComponents *components = [NSDateComponents new]; - components.month = -6; - return components; - })()) toDate:now options:0]]; - - _toDate = [self pickerDateFromDate:[_calendar dateByAddingComponents:((^{ - NSDateComponents *components = [NSDateComponents new]; - components.month = 6; - return components; - })()) toDate:now options:0]]; - - } - - return self; - -} - -- (id) initWithFrame:(CGRect)frame { - - self = [self initWithCalendar:[NSCalendar currentCalendar]]; - if (self) { - self.frame = frame; - } - - return self; - -} - -- (void) layoutSubviews { - - [super layoutSubviews]; - - self.collectionView.frame = self.bounds; - if (!self.collectionView.superview) { - [self addSubview:self.collectionView]; - } - -} - -- (void) willMoveToSuperview:(UIView *)newSuperview { - - [super willMoveToSuperview:newSuperview]; - - if (newSuperview && !_collectionView) { - // do some initialization! - UICollectionView *cv = self.collectionView; - NSIndexPath *cellIndexPath = [NSIndexPath indexPathForItem:([cv numberOfItemsInSection:(cv.numberOfSections / 2)] / 2) inSection:(cv.numberOfSections / 2)]; - [self.collectionView scrollToItemAtIndexPath:cellIndexPath atScrollPosition:UICollectionViewScrollPositionCenteredVertically animated:NO]; - } - -} - -- (UICollectionView *) collectionView { - - if (!_collectionView) { - _collectionView = [[DFDatePickerCollectionView alloc] initWithFrame:self.bounds collectionViewLayout:self.collectionViewLayout]; - _collectionView.backgroundColor = [UIColor whiteColor]; - _collectionView.dataSource = self; - _collectionView.delegate = self; - _collectionView.showsVerticalScrollIndicator = NO; - _collectionView.showsHorizontalScrollIndicator = NO; - [_collectionView registerClass:[DFDatePickerDayCell class] forCellWithReuseIdentifier:DFDatePickerViewCellIdentifier]; - [_collectionView registerClass:[DFDatePickerMonthHeader class] forSupplementaryViewOfKind:UICollectionElementKindSectionHeader withReuseIdentifier:DFDatePickerViewMonthHeaderIdentifier]; - - [_collectionView reloadData]; - } - - return _collectionView; - -} - -- (UICollectionViewFlowLayout *) collectionViewLayout { - - // Hard key these things. - // 44 * 7 + 2 * 6 = 320; this is how the Calendar.app works - // and this also avoids the “one pixel” confusion which might or might not work - // If you need to decorate, key decorative views in. - - if (!_collectionViewLayout) { - _collectionViewLayout = [UICollectionViewFlowLayout new]; - _collectionViewLayout.headerReferenceSize = (CGSize){ 320, 64 }; - _collectionViewLayout.itemSize = (CGSize){ 44, 44 }; - _collectionViewLayout.minimumLineSpacing = 2.0f; - _collectionViewLayout.minimumInteritemSpacing = 2.0f; - } - - return _collectionViewLayout; - -} - -- (void) pickerCollectionViewWillLayoutSubviews:(DFDatePickerCollectionView *)pickerCollectionView { - - // Note: relayout is slower than calculating 3 or 6 months’ worth of data at a time - // So we punt 6 months at a time. - - // Running Time Self Symbol Name - // - // 1647.0ms 23.7% 1647.0 objc_msgSend - // 193.0ms 2.7% 193.0 -[NSIndexPath compare:] - // 163.0ms 2.3% 163.0 objc::DenseMap, objc::DenseMapInfo >::LookupBucketFor(objc_object* const&, std::pair*&) const - // 141.0ms 2.0% 141.0 DYLD-STUB$$-[_UIHostedTextServiceSession dismissTextServiceAnimated:] - // 138.0ms 1.9% 138.0 -[NSObject retain] - // 136.0ms 1.9% 136.0 -[NSIndexPath indexAtPosition:] - // 124.0ms 1.7% 124.0 -[_UICollectionViewItemKey isEqual:] - // 118.0ms 1.7% 118.0 _objc_rootReleaseWasZero - // 105.0ms 1.5% 105.0 DYLD-STUB$$CFDictionarySetValue$shim - - if (pickerCollectionView.contentOffset.y < 0.0f) { - [self appendPastDates]; - } - - if (pickerCollectionView.contentOffset.y > (pickerCollectionView.contentSize.height - CGRectGetHeight(pickerCollectionView.bounds))) { - [self appendFutureDates]; - } - -} - -- (void) appendPastDates { - - [self shiftDatesByComponents:((^{ - NSDateComponents *dateComponents = [NSDateComponents new]; - dateComponents.month = -6; - return dateComponents; - })())]; - -} - -- (void) appendFutureDates { - - [self shiftDatesByComponents:((^{ - NSDateComponents *dateComponents = [NSDateComponents new]; - dateComponents.month = 6; - return dateComponents; - })())]; - -} - -- (void) shiftDatesByComponents:(NSDateComponents *)components { - - UICollectionView *cv = self.collectionView; - UICollectionViewFlowLayout *cvLayout = (UICollectionViewFlowLayout *)self.collectionView.collectionViewLayout; - - NSArray *visibleCells = [self.collectionView visibleCells]; - if (![visibleCells count]) - return; - - NSIndexPath *fromIndexPath = [cv indexPathForCell:((UICollectionViewCell *)visibleCells[0]) ]; - NSInteger fromSection = fromIndexPath.section; - NSDate *fromSectionOfDate = [self dateForFirstDayInSection:fromSection]; - CGPoint fromSectionOrigin = [self convertPoint:[cvLayout layoutAttributesForItemAtIndexPath:fromIndexPath].frame.origin fromView:cv]; - - _fromDate = [self pickerDateFromDate:[self.calendar dateByAddingComponents:components toDate:[self dateFromPickerDate:self.fromDate] options:0]]; - _toDate = [self pickerDateFromDate:[self.calendar dateByAddingComponents:components toDate:[self dateFromPickerDate:self.toDate] options:0]]; - -#if 0 - - // This solution trips up the collection view a bit - // because our reload is reactionary, and happens before a relayout - // since we must do it to avoid flickering and to heckle the CA transaction (?) - // that could be a small red flag too - - [cv performBatchUpdates:^{ - - if (components.month < 0) { - - [cv deleteSections:[NSIndexSet indexSetWithIndexesInRange:(NSRange){ - cv.numberOfSections - abs(components.month), - abs(components.month) - }]]; - - [cv insertSections:[NSIndexSet indexSetWithIndexesInRange:(NSRange){ - 0, - abs(components.month) - }]]; - - } else { - - [cv insertSections:[NSIndexSet indexSetWithIndexesInRange:(NSRange){ - cv.numberOfSections, - abs(components.month) - }]]; - - [cv deleteSections:[NSIndexSet indexSetWithIndexesInRange:(NSRange){ - 0, - abs(components.month) - }]]; - - } - - } completion:^(BOOL finished) { - - NSLog(@"%s %x", __PRETTY_FUNCTION__, finished); - - }]; - - for (UIView *view in cv.subviews) - [view.layer removeAllAnimations]; - -#else - - [cv reloadData]; - [cvLayout invalidateLayout]; - [cvLayout prepareLayout]; - -#endif - - NSInteger toSection = [self.calendar components:NSMonthCalendarUnit fromDate:[self dateForFirstDayInSection:0] toDate:fromSectionOfDate options:0].month; - UICollectionViewLayoutAttributes *toAttrs = [cvLayout layoutAttributesForItemAtIndexPath:[NSIndexPath indexPathForItem:0 inSection:toSection]]; - CGPoint toSectionOrigin = [self convertPoint:toAttrs.frame.origin fromView:cv]; - - [cv setContentOffset:(CGPoint) { - cv.contentOffset.x, - cv.contentOffset.y + (toSectionOrigin.y - fromSectionOrigin.y) - }]; - -} - -- (NSInteger) numberOfSectionsInCollectionView:(UICollectionView *)collectionView { - - return [self.calendar components:NSMonthCalendarUnit fromDate:[self dateFromPickerDate:self.fromDate] toDate:[self dateFromPickerDate:self.toDate] options:0].month; - -} - -- (NSInteger) collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section { - - return 7 * [self numberOfWeeksForMonthOfDate:[self dateForFirstDayInSection:section]]; - -} - -- (NSDate *) dateForFirstDayInSection:(NSInteger)section { - - return [self.calendar dateByAddingComponents:((^{ - NSDateComponents *dateComponents = [NSDateComponents new]; - dateComponents.month = section; - return dateComponents; - })()) toDate:[self dateFromPickerDate:self.fromDate] options:0]; - -} - -- (NSUInteger) numberOfWeeksForMonthOfDate:(NSDate *)date { - - NSDate *firstDayInMonth = [self.calendar dateFromComponents:[self.calendar components:NSYearCalendarUnit|NSMonthCalendarUnit fromDate:date]]; - - NSDate *lastDayInMonth = [self.calendar dateByAddingComponents:((^{ - NSDateComponents *dateComponents = [NSDateComponents new]; - dateComponents.month = 1; - dateComponents.day = -1; - return dateComponents; - })()) toDate:firstDayInMonth options:0]; - - NSDate *fromSunday = [self.calendar dateFromComponents:((^{ - NSDateComponents *dateComponents = [self.calendar components:NSWeekOfYearCalendarUnit|NSYearForWeekOfYearCalendarUnit fromDate:firstDayInMonth]; - dateComponents.weekday = 1; - return dateComponents; - })())]; - - NSDate *toSunday = [self.calendar dateFromComponents:((^{ - NSDateComponents *dateComponents = [self.calendar components:NSWeekOfYearCalendarUnit|NSYearForWeekOfYearCalendarUnit fromDate:lastDayInMonth]; - dateComponents.weekday = 1; - return dateComponents; - })())]; - - return 1 + [self.calendar components:NSWeekCalendarUnit fromDate:fromSunday toDate:toSunday options:0].week; - -} - -- (DFDatePickerDayCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath { - - DFDatePickerDayCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:DFDatePickerViewCellIdentifier forIndexPath:indexPath]; - - NSDate *firstDayInMonth = [self dateForFirstDayInSection:indexPath.section]; - DFDatePickerDate firstDayPickerDate = [self pickerDateFromDate:firstDayInMonth]; - NSUInteger weekday = [self.calendar components:NSWeekdayCalendarUnit fromDate:firstDayInMonth].weekday; - - NSDate *cellDate = [self.calendar dateByAddingComponents:((^{ - NSDateComponents *dateComponents = [NSDateComponents new]; - dateComponents.day = indexPath.item - (weekday - 1); - return dateComponents; - })()) toDate:firstDayInMonth options:0]; - DFDatePickerDate cellPickerDate = [self pickerDateFromDate:cellDate]; - - cell.date = cellPickerDate; - cell.enabled = ((firstDayPickerDate.year == cellPickerDate.year) && (firstDayPickerDate.month == cellPickerDate.month)); - cell.selected = [self.selectedDate isEqualToDate:cellDate]; - - return cell; - -} - -// We are cheating by piggybacking on view state to avoid recalculation -// in -collectionView:shouldHighlightItemAtIndexPath: -// and -collectionView:shouldSelectItemAtIndexPath:. - -// A naïve refactoring process might introduce duplicate state which is bad too. - -- (BOOL) collectionView:(UICollectionView *)collectionView shouldHighlightItemAtIndexPath:(NSIndexPath *)indexPath { - return ((DFDatePickerDayCell *)[collectionView cellForItemAtIndexPath:indexPath]).enabled; -} - -- (BOOL) collectionView:(UICollectionView *)collectionView shouldSelectItemAtIndexPath:(NSIndexPath *)indexPath { - return ((DFDatePickerDayCell *)[collectionView cellForItemAtIndexPath:indexPath]).enabled; -} - -- (void) collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath { - DFDatePickerDayCell *cell = ((DFDatePickerDayCell *)[collectionView cellForItemAtIndexPath:indexPath]); - [self willChangeValueForKey:@"selectedDate"]; - _selectedDate = cell - ? [self.calendar dateFromComponents:[self dateComponentsFromPickerDate:cell.date]] - : nil; - [self didChangeValueForKey:@"selectedDate"]; -} - -- (void) setSelectedDate:(NSDate *)selectedDate { - _selectedDate = selectedDate; - [self.collectionView reloadData]; -} - -- (UICollectionReusableView *) collectionView:(UICollectionView *)collectionView viewForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath { - - if ([kind isEqualToString:UICollectionElementKindSectionHeader]) { - - DFDatePickerMonthHeader *monthHeader = [collectionView dequeueReusableSupplementaryViewOfKind:kind withReuseIdentifier:DFDatePickerViewMonthHeaderIdentifier forIndexPath:indexPath]; - - NSDateFormatter *dateFormatter = [self.calendar df_dateFormatterNamed:@"calendarMonthHeader" withConstructor:^{ - NSDateFormatter *dateFormatter = [NSDateFormatter new]; - dateFormatter.calendar = self.calendar; - dateFormatter.dateFormat = [dateFormatter.class dateFormatFromTemplate:@"yyyyLLLL" options:0 locale:[NSLocale currentLocale]]; - return dateFormatter; - }]; - - NSDate *formattedDate = [self dateForFirstDayInSection:indexPath.section]; - monthHeader.textLabel.text = [dateFormatter stringFromDate:formattedDate]; - - return monthHeader; - - } - - return nil; - -} - -- (NSDate *) dateFromPickerDate:(DFDatePickerDate)dateStruct { - return [self.calendar dateFromComponents:[self dateComponentsFromPickerDate:dateStruct]]; -} - -- (NSDateComponents *) dateComponentsFromPickerDate:(DFDatePickerDate)dateStruct { - NSDateComponents *components = [NSDateComponents new]; - components.year = dateStruct.year; - components.month = dateStruct.month; - components.day = dateStruct.day; - return components; -} - -- (DFDatePickerDate) pickerDateFromDate:(NSDate *)date { - NSDateComponents *components = [self.calendar components:NSYearCalendarUnit|NSMonthCalendarUnit|NSDayCalendarUnit fromDate:date]; - return (DFDatePickerDate) { - components.year, - components.month, - components.day - }; -} - -@end diff --git a/DayFlow/DFDatePickerViewController.h b/DayFlow/DFDatePickerViewController.h deleted file mode 100644 index 5afac2f..0000000 --- a/DayFlow/DFDatePickerViewController.h +++ /dev/null @@ -1,16 +0,0 @@ -#import -#import "DFDatePickerView.h" - -@class DFDatePickerViewController; -@protocol DFDatePickerViewControllerDelegate - -- (void) datePickerViewController:(DFDatePickerViewController *)controller didSelectDate:(NSDate *)date; - -@end - -@interface DFDatePickerViewController : UIViewController - -@property (nonatomic, readonly, strong) DFDatePickerView *datePickerView; -@property (nonatomic, readwrite, weak) id delegate; - -@end diff --git a/DayFlow/DFDatePickerViewController.m b/DayFlow/DFDatePickerViewController.m deleted file mode 100644 index 1e934c6..0000000 --- a/DayFlow/DFDatePickerViewController.m +++ /dev/null @@ -1,40 +0,0 @@ -#import "DFDatePickerViewController.h" - -@implementation DFDatePickerViewController -@synthesize datePickerView = _datePickerView; - -- (void) viewDidLoad { - [super viewDidLoad]; - [self.view addSubview:self.datePickerView]; -} - -- (DFDatePickerView *) datePickerView { - if (!_datePickerView) { - _datePickerView = [DFDatePickerView new]; - _datePickerView.frame = self.view.bounds; - _datePickerView.autoresizingMask = UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleHeight; - } - return _datePickerView; -} - -- (void) viewDidAppear:(BOOL)animated { - [super viewDidAppear:animated]; - [self addObserver:self forKeyPath:@"datePickerView.selectedDate" options:NSKeyValueObservingOptionOld|NSKeyValueObservingOptionNew context:(void *)self]; -} - -- (void) viewWillDisappear:(BOOL)animated { - [super viewWillDisappear:animated]; - [self removeObserver:self forKeyPath:@"datePickerView.selectedDate" context:(void *)self]; -} - -- (void) observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { - if ([keyPath isEqualToString:@"datePickerView.selectedDate"]) { - NSDate *toDate = change[NSKeyValueChangeNewKey]; - if ([toDate isKindOfClass:[NSDate class]]) { - // toDate might be NSNull - [self.delegate datePickerViewController:self didSelectDate:toDate]; - } - } -} - -@end diff --git a/DayFlow/DayFlow-Prefix.pch b/DayFlow/DayFlow-Prefix.pch deleted file mode 100644 index eecd91e..0000000 --- a/DayFlow/DayFlow-Prefix.pch +++ /dev/null @@ -1,5 +0,0 @@ -#ifdef __OBJC__ - #import - #import - #import -#endif diff --git a/DayFlow/DayFlow.h b/DayFlow/DayFlow.h deleted file mode 100644 index e31aec1..0000000 --- a/DayFlow/DayFlow.h +++ /dev/null @@ -1,8 +0,0 @@ -typedef struct { - NSUInteger year; - NSUInteger month; - NSUInteger day; -} DFDatePickerDate; - -#import "DFDatePickerView.h" -#import "DFDatePickerViewController.h" diff --git a/DayFlow/NSCalendar+DFAdditions.h b/DayFlow/NSCalendar+DFAdditions.h deleted file mode 100644 index 027d6f9..0000000 --- a/DayFlow/NSCalendar+DFAdditions.h +++ /dev/null @@ -1,7 +0,0 @@ -#import - -@interface NSCalendar (DFAdditions) - -- (NSDateFormatter *) df_dateFormatterNamed:(NSString *)name withConstructor:(NSDateFormatter *(^)(void))block; - -@end diff --git a/DayFlow/NSCalendar+DFAdditions.m b/DayFlow/NSCalendar+DFAdditions.m deleted file mode 100644 index 131105a..0000000 --- a/DayFlow/NSCalendar+DFAdditions.m +++ /dev/null @@ -1,23 +0,0 @@ -#import "NSCalendar+DFAdditions.h" - -@implementation NSCalendar (DFAdditions) - -- (NSDateFormatter *) df_dateFormatterNamed:(NSString *)name withConstructor:(NSDateFormatter *(^)(void))block { - - // We can not use objc_setAssociatedObject() because it has no thread safety - // Modeled after http://api.rubyonrails.org/classes/ActiveSupport/Cache/Store.html - // Intended for use where there are a myriad of date formatters keyed on a calendar - - NSMutableDictionary *threadDictionary = [[NSThread currentThread] threadDictionary]; - NSDateFormatter *dateFormatter = threadDictionary[name]; - - if (!dateFormatter) { - dateFormatter = block(); - threadDictionary[name] = dateFormatter; - } - - return dateFormatter; - -} - -@end diff --git a/Example/RSDayFlowExample.xcodeproj/project.pbxproj b/Example/RSDayFlowExample.xcodeproj/project.pbxproj new file mode 100644 index 0000000..b430179 --- /dev/null +++ b/Example/RSDayFlowExample.xcodeproj/project.pbxproj @@ -0,0 +1,591 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + B82FFF9819A35722000816FF /* RSDFDatePickerCollectionViewLayout.m in Sources */ = {isa = PBXBuildFile; fileRef = B82FFF9719A35722000816FF /* RSDFDatePickerCollectionViewLayout.m */; }; + B82FFFA519A37EDB000816FF /* RSDFCustomDatePickerCollectionView.m in Sources */ = {isa = PBXBuildFile; fileRef = B82FFF9A19A37EDB000816FF /* RSDFCustomDatePickerCollectionView.m */; }; + B82FFFA619A37EDB000816FF /* RSDFCustomDatePickerCollectionViewLayout.m in Sources */ = {isa = PBXBuildFile; fileRef = B82FFF9C19A37EDB000816FF /* RSDFCustomDatePickerCollectionViewLayout.m */; }; + B82FFFA719A37EDB000816FF /* RSDFCustomDatePickerDayCell.m in Sources */ = {isa = PBXBuildFile; fileRef = B82FFF9E19A37EDB000816FF /* RSDFCustomDatePickerDayCell.m */; }; + B82FFFA819A37EDB000816FF /* RSDFCustomDatePickerDaysOfWeekView.m in Sources */ = {isa = PBXBuildFile; fileRef = B82FFFA019A37EDB000816FF /* RSDFCustomDatePickerDaysOfWeekView.m */; }; + B82FFFA919A37EDB000816FF /* RSDFCustomDatePickerMonthHeader.m in Sources */ = {isa = PBXBuildFile; fileRef = B82FFFA219A37EDB000816FF /* RSDFCustomDatePickerMonthHeader.m */; }; + B82FFFAA19A37EDB000816FF /* RSDFCustomDatePickerView.m in Sources */ = {isa = PBXBuildFile; fileRef = B82FFFA419A37EDB000816FF /* RSDFCustomDatePickerView.m */; }; + B872EE8319E82DF000E52146 /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = B872EE8119E82DF000E52146 /* LaunchScreen.xib */; }; + B8F807C119A34B990047C24B /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B8F807C019A34B990047C24B /* Foundation.framework */; }; + B8F807C319A34B9A0047C24B /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B8F807C219A34B9A0047C24B /* CoreGraphics.framework */; }; + B8F807C519A34B9A0047C24B /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B8F807C419A34B9A0047C24B /* UIKit.framework */; }; + B8F807CB19A34B9A0047C24B /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = B8F807C919A34B9A0047C24B /* InfoPlist.strings */; }; + B8F807CD19A34B9A0047C24B /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = B8F807CC19A34B9A0047C24B /* main.m */; }; + B8F807D119A34B9A0047C24B /* RSDFAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = B8F807D019A34B9A0047C24B /* RSDFAppDelegate.m */; }; + B8F807D319A34B9A0047C24B /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = B8F807D219A34B9A0047C24B /* Images.xcassets */; }; + B8F807DA19A34B9A0047C24B /* XCTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B8F807D919A34B9A0047C24B /* XCTest.framework */; }; + B8F807DB19A34B9A0047C24B /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B8F807C019A34B990047C24B /* Foundation.framework */; }; + B8F807DC19A34B9A0047C24B /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B8F807C419A34B9A0047C24B /* UIKit.framework */; }; + B8F807E419A34B9A0047C24B /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = B8F807E219A34B9A0047C24B /* InfoPlist.strings */; }; + B8F807E619A34B9A0047C24B /* RSDayFlowExampleTests.m in Sources */ = {isa = PBXBuildFile; fileRef = B8F807E519A34B9A0047C24B /* RSDayFlowExampleTests.m */; }; + B8F8080819A34CDC0047C24B /* RSDFDatePickerViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = B8F8080719A34CDC0047C24B /* RSDFDatePickerViewController.m */; }; + B8F8081819A3502D0047C24B /* NSCalendar+RSDFAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = B8F8080B19A3502D0047C24B /* NSCalendar+RSDFAdditions.m */; }; + B8F8081919A3502D0047C24B /* RSDFDatePickerCollectionView.m in Sources */ = {isa = PBXBuildFile; fileRef = B8F8080F19A3502D0047C24B /* RSDFDatePickerCollectionView.m */; }; + B8F8081A19A3502D0047C24B /* RSDFDatePickerDayCell.m in Sources */ = {isa = PBXBuildFile; fileRef = B8F8081119A3502D0047C24B /* RSDFDatePickerDayCell.m */; }; + B8F8081B19A3502D0047C24B /* RSDFDatePickerDaysOfWeekView.m in Sources */ = {isa = PBXBuildFile; fileRef = B8F8081319A3502D0047C24B /* RSDFDatePickerDaysOfWeekView.m */; }; + B8F8081C19A3502D0047C24B /* RSDFDatePickerMonthHeader.m in Sources */ = {isa = PBXBuildFile; fileRef = B8F8081519A3502D0047C24B /* RSDFDatePickerMonthHeader.m */; }; + B8F8081D19A3502D0047C24B /* RSDFDatePickerView.m in Sources */ = {isa = PBXBuildFile; fileRef = B8F8081719A3502D0047C24B /* RSDFDatePickerView.m */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + B8F807DD19A34B9A0047C24B /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = B8F807B519A34B990047C24B /* Project object */; + proxyType = 1; + remoteGlobalIDString = B8F807BC19A34B990047C24B; + remoteInfo = RSDayFlowExample; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXFileReference section */ + B82FFF9619A35722000816FF /* RSDFDatePickerCollectionViewLayout.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RSDFDatePickerCollectionViewLayout.h; sourceTree = ""; }; + B82FFF9719A35722000816FF /* RSDFDatePickerCollectionViewLayout.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RSDFDatePickerCollectionViewLayout.m; sourceTree = ""; }; + B82FFF9919A37EDB000816FF /* RSDFCustomDatePickerCollectionView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RSDFCustomDatePickerCollectionView.h; sourceTree = ""; }; + B82FFF9A19A37EDB000816FF /* RSDFCustomDatePickerCollectionView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RSDFCustomDatePickerCollectionView.m; sourceTree = ""; }; + B82FFF9B19A37EDB000816FF /* RSDFCustomDatePickerCollectionViewLayout.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RSDFCustomDatePickerCollectionViewLayout.h; sourceTree = ""; }; + B82FFF9C19A37EDB000816FF /* RSDFCustomDatePickerCollectionViewLayout.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RSDFCustomDatePickerCollectionViewLayout.m; sourceTree = ""; }; + B82FFF9D19A37EDB000816FF /* RSDFCustomDatePickerDayCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RSDFCustomDatePickerDayCell.h; sourceTree = ""; }; + B82FFF9E19A37EDB000816FF /* RSDFCustomDatePickerDayCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RSDFCustomDatePickerDayCell.m; sourceTree = ""; }; + B82FFF9F19A37EDB000816FF /* RSDFCustomDatePickerDaysOfWeekView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RSDFCustomDatePickerDaysOfWeekView.h; sourceTree = ""; }; + B82FFFA019A37EDB000816FF /* RSDFCustomDatePickerDaysOfWeekView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RSDFCustomDatePickerDaysOfWeekView.m; sourceTree = ""; }; + B82FFFA119A37EDB000816FF /* RSDFCustomDatePickerMonthHeader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RSDFCustomDatePickerMonthHeader.h; sourceTree = ""; }; + B82FFFA219A37EDB000816FF /* RSDFCustomDatePickerMonthHeader.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RSDFCustomDatePickerMonthHeader.m; sourceTree = ""; }; + B82FFFA319A37EDB000816FF /* RSDFCustomDatePickerView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RSDFCustomDatePickerView.h; sourceTree = ""; }; + B82FFFA419A37EDB000816FF /* RSDFCustomDatePickerView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RSDFCustomDatePickerView.m; sourceTree = ""; }; + B872EE8219E82DF000E52146 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/LaunchScreen.xib; sourceTree = ""; }; + B8F807BD19A34B990047C24B /* RSDayFlowExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = RSDayFlowExample.app; sourceTree = BUILT_PRODUCTS_DIR; }; + B8F807C019A34B990047C24B /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; + B8F807C219A34B9A0047C24B /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; + B8F807C419A34B9A0047C24B /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; + B8F807C819A34B9A0047C24B /* RSDayFlowExample-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "RSDayFlowExample-Info.plist"; sourceTree = ""; }; + B8F807CA19A34B9A0047C24B /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; + B8F807CC19A34B9A0047C24B /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; + B8F807CE19A34B9A0047C24B /* RSDayFlowExample-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "RSDayFlowExample-Prefix.pch"; sourceTree = ""; }; + B8F807CF19A34B9A0047C24B /* RSDFAppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RSDFAppDelegate.h; sourceTree = ""; }; + B8F807D019A34B9A0047C24B /* RSDFAppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RSDFAppDelegate.m; sourceTree = ""; }; + B8F807D219A34B9A0047C24B /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; + B8F807D819A34B9A0047C24B /* RSDayFlowExampleTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RSDayFlowExampleTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + B8F807D919A34B9A0047C24B /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; }; + B8F807E119A34B9A0047C24B /* RSDayFlowExampleTests-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "RSDayFlowExampleTests-Info.plist"; sourceTree = ""; }; + B8F807E319A34B9A0047C24B /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; + B8F807E519A34B9A0047C24B /* RSDayFlowExampleTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RSDayFlowExampleTests.m; sourceTree = ""; }; + B8F8080619A34CDC0047C24B /* RSDFDatePickerViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RSDFDatePickerViewController.h; sourceTree = ""; }; + B8F8080719A34CDC0047C24B /* RSDFDatePickerViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RSDFDatePickerViewController.m; sourceTree = ""; }; + B8F8080A19A3502D0047C24B /* NSCalendar+RSDFAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSCalendar+RSDFAdditions.h"; sourceTree = ""; }; + B8F8080B19A3502D0047C24B /* NSCalendar+RSDFAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSCalendar+RSDFAdditions.m"; sourceTree = ""; }; + B8F8080D19A3502D0047C24B /* RSDayFlow.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RSDayFlow.h; sourceTree = ""; }; + B8F8080E19A3502D0047C24B /* RSDFDatePickerCollectionView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RSDFDatePickerCollectionView.h; sourceTree = ""; }; + B8F8080F19A3502D0047C24B /* RSDFDatePickerCollectionView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RSDFDatePickerCollectionView.m; sourceTree = ""; }; + B8F8081019A3502D0047C24B /* RSDFDatePickerDayCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RSDFDatePickerDayCell.h; sourceTree = ""; }; + B8F8081119A3502D0047C24B /* RSDFDatePickerDayCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RSDFDatePickerDayCell.m; sourceTree = ""; }; + B8F8081219A3502D0047C24B /* RSDFDatePickerDaysOfWeekView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RSDFDatePickerDaysOfWeekView.h; sourceTree = ""; }; + B8F8081319A3502D0047C24B /* RSDFDatePickerDaysOfWeekView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RSDFDatePickerDaysOfWeekView.m; sourceTree = ""; }; + B8F8081419A3502D0047C24B /* RSDFDatePickerMonthHeader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RSDFDatePickerMonthHeader.h; sourceTree = ""; }; + B8F8081519A3502D0047C24B /* RSDFDatePickerMonthHeader.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RSDFDatePickerMonthHeader.m; sourceTree = ""; }; + B8F8081619A3502D0047C24B /* RSDFDatePickerView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RSDFDatePickerView.h; sourceTree = ""; }; + B8F8081719A3502D0047C24B /* RSDFDatePickerView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RSDFDatePickerView.m; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + B8F807BA19A34B990047C24B /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + B8F807C319A34B9A0047C24B /* CoreGraphics.framework in Frameworks */, + B8F807C519A34B9A0047C24B /* UIKit.framework in Frameworks */, + B8F807C119A34B990047C24B /* Foundation.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + B8F807D519A34B9A0047C24B /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + B8F807DA19A34B9A0047C24B /* XCTest.framework in Frameworks */, + B8F807DC19A34B9A0047C24B /* UIKit.framework in Frameworks */, + B8F807DB19A34B9A0047C24B /* Foundation.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + B82FFFAB19A37EDF000816FF /* Views */ = { + isa = PBXGroup; + children = ( + B82FFFA319A37EDB000816FF /* RSDFCustomDatePickerView.h */, + B82FFFA419A37EDB000816FF /* RSDFCustomDatePickerView.m */, + B82FFF9F19A37EDB000816FF /* RSDFCustomDatePickerDaysOfWeekView.h */, + B82FFFA019A37EDB000816FF /* RSDFCustomDatePickerDaysOfWeekView.m */, + B82FFF9919A37EDB000816FF /* RSDFCustomDatePickerCollectionView.h */, + B82FFF9A19A37EDB000816FF /* RSDFCustomDatePickerCollectionView.m */, + B82FFF9B19A37EDB000816FF /* RSDFCustomDatePickerCollectionViewLayout.h */, + B82FFF9C19A37EDB000816FF /* RSDFCustomDatePickerCollectionViewLayout.m */, + B82FFFA119A37EDB000816FF /* RSDFCustomDatePickerMonthHeader.h */, + B82FFFA219A37EDB000816FF /* RSDFCustomDatePickerMonthHeader.m */, + B82FFF9D19A37EDB000816FF /* RSDFCustomDatePickerDayCell.h */, + B82FFF9E19A37EDB000816FF /* RSDFCustomDatePickerDayCell.m */, + ); + name = Views; + sourceTree = ""; + }; + B8F807B419A34B990047C24B = { + isa = PBXGroup; + children = ( + B8F807C619A34B9A0047C24B /* Classes */, + B8F8080419A34C3C0047C24B /* Vendor */, + B8F807DF19A34B9A0047C24B /* RSDayFlowExampleTests */, + B8F807BF19A34B990047C24B /* Frameworks */, + B8F807BE19A34B990047C24B /* Products */, + ); + sourceTree = ""; + }; + B8F807BE19A34B990047C24B /* Products */ = { + isa = PBXGroup; + children = ( + B8F807BD19A34B990047C24B /* RSDayFlowExample.app */, + B8F807D819A34B9A0047C24B /* RSDayFlowExampleTests.xctest */, + ); + name = Products; + sourceTree = ""; + }; + B8F807BF19A34B990047C24B /* Frameworks */ = { + isa = PBXGroup; + children = ( + B8F807C019A34B990047C24B /* Foundation.framework */, + B8F807C219A34B9A0047C24B /* CoreGraphics.framework */, + B8F807C419A34B9A0047C24B /* UIKit.framework */, + B8F807D919A34B9A0047C24B /* XCTest.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; + B8F807C619A34B9A0047C24B /* Classes */ = { + isa = PBXGroup; + children = ( + B8F807CF19A34B9A0047C24B /* RSDFAppDelegate.h */, + B8F807D019A34B9A0047C24B /* RSDFAppDelegate.m */, + B8F8080519A34CC00047C24B /* Controllers */, + B82FFFAB19A37EDF000816FF /* Views */, + B8F807D219A34B9A0047C24B /* Images.xcassets */, + B872EE8119E82DF000E52146 /* LaunchScreen.xib */, + B8F807C719A34B9A0047C24B /* Supporting Files */, + ); + name = Classes; + path = RSDayFlowExample; + sourceTree = ""; + }; + B8F807C719A34B9A0047C24B /* Supporting Files */ = { + isa = PBXGroup; + children = ( + B8F807C819A34B9A0047C24B /* RSDayFlowExample-Info.plist */, + B8F807C919A34B9A0047C24B /* InfoPlist.strings */, + B8F807CC19A34B9A0047C24B /* main.m */, + B8F807CE19A34B9A0047C24B /* RSDayFlowExample-Prefix.pch */, + ); + name = "Supporting Files"; + sourceTree = ""; + }; + B8F807DF19A34B9A0047C24B /* RSDayFlowExampleTests */ = { + isa = PBXGroup; + children = ( + B8F807E519A34B9A0047C24B /* RSDayFlowExampleTests.m */, + B8F807E019A34B9A0047C24B /* Supporting Files */, + ); + path = RSDayFlowExampleTests; + sourceTree = ""; + }; + B8F807E019A34B9A0047C24B /* Supporting Files */ = { + isa = PBXGroup; + children = ( + B8F807E119A34B9A0047C24B /* RSDayFlowExampleTests-Info.plist */, + B8F807E219A34B9A0047C24B /* InfoPlist.strings */, + ); + name = "Supporting Files"; + sourceTree = ""; + }; + B8F8080419A34C3C0047C24B /* Vendor */ = { + isa = PBXGroup; + children = ( + B8F8080919A3502D0047C24B /* RSDayFlow */, + ); + name = Vendor; + sourceTree = ""; + }; + B8F8080519A34CC00047C24B /* Controllers */ = { + isa = PBXGroup; + children = ( + B8F8080619A34CDC0047C24B /* RSDFDatePickerViewController.h */, + B8F8080719A34CDC0047C24B /* RSDFDatePickerViewController.m */, + ); + name = Controllers; + sourceTree = ""; + }; + B8F8080919A3502D0047C24B /* RSDayFlow */ = { + isa = PBXGroup; + children = ( + B8F8080D19A3502D0047C24B /* RSDayFlow.h */, + B8F8081619A3502D0047C24B /* RSDFDatePickerView.h */, + B8F8081719A3502D0047C24B /* RSDFDatePickerView.m */, + B8F8081219A3502D0047C24B /* RSDFDatePickerDaysOfWeekView.h */, + B8F8081319A3502D0047C24B /* RSDFDatePickerDaysOfWeekView.m */, + B8F8080E19A3502D0047C24B /* RSDFDatePickerCollectionView.h */, + B8F8080F19A3502D0047C24B /* RSDFDatePickerCollectionView.m */, + B82FFF9619A35722000816FF /* RSDFDatePickerCollectionViewLayout.h */, + B82FFF9719A35722000816FF /* RSDFDatePickerCollectionViewLayout.m */, + B8F8081419A3502D0047C24B /* RSDFDatePickerMonthHeader.h */, + B8F8081519A3502D0047C24B /* RSDFDatePickerMonthHeader.m */, + B8F8081019A3502D0047C24B /* RSDFDatePickerDayCell.h */, + B8F8081119A3502D0047C24B /* RSDFDatePickerDayCell.m */, + B8F8080A19A3502D0047C24B /* NSCalendar+RSDFAdditions.h */, + B8F8080B19A3502D0047C24B /* NSCalendar+RSDFAdditions.m */, + ); + name = RSDayFlow; + path = ../RSDayFlow; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + B8F807BC19A34B990047C24B /* RSDayFlowExample */ = { + isa = PBXNativeTarget; + buildConfigurationList = B8F807E919A34B9A0047C24B /* Build configuration list for PBXNativeTarget "RSDayFlowExample" */; + buildPhases = ( + B8F807B919A34B990047C24B /* Sources */, + B8F807BA19A34B990047C24B /* Frameworks */, + B8F807BB19A34B990047C24B /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = RSDayFlowExample; + productName = RSDayFlowExample; + productReference = B8F807BD19A34B990047C24B /* RSDayFlowExample.app */; + productType = "com.apple.product-type.application"; + }; + B8F807D719A34B9A0047C24B /* RSDayFlowExampleTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = B8F807EC19A34B9A0047C24B /* Build configuration list for PBXNativeTarget "RSDayFlowExampleTests" */; + buildPhases = ( + B8F807D419A34B9A0047C24B /* Sources */, + B8F807D519A34B9A0047C24B /* Frameworks */, + B8F807D619A34B9A0047C24B /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + B8F807DE19A34B9A0047C24B /* PBXTargetDependency */, + ); + name = RSDayFlowExampleTests; + productName = RSDayFlowExampleTests; + productReference = B8F807D819A34B9A0047C24B /* RSDayFlowExampleTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + B8F807B519A34B990047C24B /* Project object */ = { + isa = PBXProject; + attributes = { + CLASSPREFIX = RSDF; + LastUpgradeCheck = 0510; + ORGANIZATIONNAME = "Ruslan Skorb"; + TargetAttributes = { + B8F807D719A34B9A0047C24B = { + TestTargetID = B8F807BC19A34B990047C24B; + }; + }; + }; + buildConfigurationList = B8F807B819A34B990047C24B /* Build configuration list for PBXProject "RSDayFlowExample" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = B8F807B419A34B990047C24B; + productRefGroup = B8F807BE19A34B990047C24B /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + B8F807BC19A34B990047C24B /* RSDayFlowExample */, + B8F807D719A34B9A0047C24B /* RSDayFlowExampleTests */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + B8F807BB19A34B990047C24B /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + B8F807CB19A34B9A0047C24B /* InfoPlist.strings in Resources */, + B872EE8319E82DF000E52146 /* LaunchScreen.xib in Resources */, + B8F807D319A34B9A0047C24B /* Images.xcassets in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + B8F807D619A34B9A0047C24B /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + B8F807E419A34B9A0047C24B /* InfoPlist.strings in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + B8F807B919A34B990047C24B /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + B82FFFA619A37EDB000816FF /* RSDFCustomDatePickerCollectionViewLayout.m in Sources */, + B82FFFA519A37EDB000816FF /* RSDFCustomDatePickerCollectionView.m in Sources */, + B8F8081C19A3502D0047C24B /* RSDFDatePickerMonthHeader.m in Sources */, + B82FFFA919A37EDB000816FF /* RSDFCustomDatePickerMonthHeader.m in Sources */, + B8F807CD19A34B9A0047C24B /* main.m in Sources */, + B82FFFA819A37EDB000816FF /* RSDFCustomDatePickerDaysOfWeekView.m in Sources */, + B82FFFAA19A37EDB000816FF /* RSDFCustomDatePickerView.m in Sources */, + B8F8081D19A3502D0047C24B /* RSDFDatePickerView.m in Sources */, + B8F8081A19A3502D0047C24B /* RSDFDatePickerDayCell.m in Sources */, + B8F8081819A3502D0047C24B /* NSCalendar+RSDFAdditions.m in Sources */, + B82FFF9819A35722000816FF /* RSDFDatePickerCollectionViewLayout.m in Sources */, + B8F8081B19A3502D0047C24B /* RSDFDatePickerDaysOfWeekView.m in Sources */, + B8F8080819A34CDC0047C24B /* RSDFDatePickerViewController.m in Sources */, + B82FFFA719A37EDB000816FF /* RSDFCustomDatePickerDayCell.m in Sources */, + B8F807D119A34B9A0047C24B /* RSDFAppDelegate.m in Sources */, + B8F8081919A3502D0047C24B /* RSDFDatePickerCollectionView.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + B8F807D419A34B9A0047C24B /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + B8F807E619A34B9A0047C24B /* RSDayFlowExampleTests.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + B8F807DE19A34B9A0047C24B /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = B8F807BC19A34B990047C24B /* RSDayFlowExample */; + targetProxy = B8F807DD19A34B9A0047C24B /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin PBXVariantGroup section */ + B872EE8119E82DF000E52146 /* LaunchScreen.xib */ = { + isa = PBXVariantGroup; + children = ( + B872EE8219E82DF000E52146 /* Base */, + ); + name = LaunchScreen.xib; + sourceTree = ""; + }; + B8F807C919A34B9A0047C24B /* InfoPlist.strings */ = { + isa = PBXVariantGroup; + children = ( + B8F807CA19A34B9A0047C24B /* en */, + ); + name = InfoPlist.strings; + sourceTree = ""; + }; + B8F807E219A34B9A0047C24B /* InfoPlist.strings */ = { + isa = PBXVariantGroup; + children = ( + B8F807E319A34B9A0047C24B /* en */, + ); + name = InfoPlist.strings; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + B8F807E719A34B9A0047C24B /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "NS_ENABLE_CALENDAR_NEW_API=1", + "$(inherited)", + ); + GCC_SYMBOLS_PRIVATE_EXTERN = NO; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 7.0; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + }; + name = Debug; + }; + B8F807E819A34B9A0047C24B /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = YES; + ENABLE_NS_ASSERTIONS = NO; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 7.0; + SDKROOT = iphoneos; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + B8F807EA19A34B9A0047C24B /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = "RSDayFlowExample/RSDayFlowExample-Prefix.pch"; + INFOPLIST_FILE = "RSDayFlowExample/RSDayFlowExample-Info.plist"; + PRODUCT_NAME = "$(TARGET_NAME)"; + TARGETED_DEVICE_FAMILY = "1,2"; + WRAPPER_EXTENSION = app; + }; + name = Debug; + }; + B8F807EB19A34B9A0047C24B /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = "RSDayFlowExample/RSDayFlowExample-Prefix.pch"; + INFOPLIST_FILE = "RSDayFlowExample/RSDayFlowExample-Info.plist"; + PRODUCT_NAME = "$(TARGET_NAME)"; + TARGETED_DEVICE_FAMILY = "1,2"; + WRAPPER_EXTENSION = app; + }; + name = Release; + }; + B8F807ED19A34B9A0047C24B /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/RSDayFlowExample.app/RSDayFlowExample"; + FRAMEWORK_SEARCH_PATHS = ( + "$(SDKROOT)/Developer/Library/Frameworks", + "$(inherited)", + "$(DEVELOPER_FRAMEWORKS_DIR)", + ); + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = "RSDayFlowExample/RSDayFlowExample-Prefix.pch"; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + INFOPLIST_FILE = "RSDayFlowExampleTests/RSDayFlowExampleTests-Info.plist"; + PRODUCT_NAME = "$(TARGET_NAME)"; + TEST_HOST = "$(BUNDLE_LOADER)"; + WRAPPER_EXTENSION = xctest; + }; + name = Debug; + }; + B8F807EE19A34B9A0047C24B /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/RSDayFlowExample.app/RSDayFlowExample"; + FRAMEWORK_SEARCH_PATHS = ( + "$(SDKROOT)/Developer/Library/Frameworks", + "$(inherited)", + "$(DEVELOPER_FRAMEWORKS_DIR)", + ); + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = "RSDayFlowExample/RSDayFlowExample-Prefix.pch"; + INFOPLIST_FILE = "RSDayFlowExampleTests/RSDayFlowExampleTests-Info.plist"; + PRODUCT_NAME = "$(TARGET_NAME)"; + TEST_HOST = "$(BUNDLE_LOADER)"; + WRAPPER_EXTENSION = xctest; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + B8F807B819A34B990047C24B /* Build configuration list for PBXProject "RSDayFlowExample" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + B8F807E719A34B9A0047C24B /* Debug */, + B8F807E819A34B9A0047C24B /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + B8F807E919A34B9A0047C24B /* Build configuration list for PBXNativeTarget "RSDayFlowExample" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + B8F807EA19A34B9A0047C24B /* Debug */, + B8F807EB19A34B9A0047C24B /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + B8F807EC19A34B9A0047C24B /* Build configuration list for PBXNativeTarget "RSDayFlowExampleTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + B8F807ED19A34B9A0047C24B /* Debug */, + B8F807EE19A34B9A0047C24B /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = B8F807B519A34B990047C24B /* Project object */; +} diff --git a/Example/RSDayFlowExample/Base.lproj/LaunchScreen.xib b/Example/RSDayFlowExample/Base.lproj/LaunchScreen.xib new file mode 100644 index 0000000..72df6e5 --- /dev/null +++ b/Example/RSDayFlowExample/Base.lproj/LaunchScreen.xib @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/Example/RSDayFlowExample/Images.xcassets/AppIcon.appiconset/Contents.json b/Example/RSDayFlowExample/Images.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..a396706 --- /dev/null +++ b/Example/RSDayFlowExample/Images.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Example/RSDayFlowExample/Images.xcassets/LaunchImage.launchimage/Contents.json b/Example/RSDayFlowExample/Images.xcassets/LaunchImage.launchimage/Contents.json new file mode 100644 index 0000000..c79ebd3 --- /dev/null +++ b/Example/RSDayFlowExample/Images.xcassets/LaunchImage.launchimage/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "orientation" : "portrait", + "idiom" : "iphone", + "extent" : "full-screen", + "minimum-system-version" : "7.0", + "scale" : "2x" + }, + { + "orientation" : "portrait", + "idiom" : "iphone", + "subtype" : "retina4", + "extent" : "full-screen", + "minimum-system-version" : "7.0", + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Example/RSDayFlowExample/RSDFAppDelegate.h b/Example/RSDayFlowExample/RSDFAppDelegate.h new file mode 100644 index 0000000..87486a3 --- /dev/null +++ b/Example/RSDayFlowExample/RSDFAppDelegate.h @@ -0,0 +1,32 @@ +// +// RSDFAppDelegate.h +// +// Copyright (c) 2013 Evadne Wu, http://radi.ws/ +// Copyright (c) 2013-2015 Ruslan Skorb, http://ruslanskorb.com +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +#import + +@interface RSDFAppDelegate : UIResponder + +@property (strong, nonatomic) UIWindow *window; + +@end diff --git a/Example/RSDayFlowExample/RSDFAppDelegate.m b/Example/RSDayFlowExample/RSDFAppDelegate.m new file mode 100644 index 0000000..c187e58 --- /dev/null +++ b/Example/RSDayFlowExample/RSDFAppDelegate.m @@ -0,0 +1,90 @@ +// +// RSDFAppDelegate.m +// +// Copyright (c) 2013 Evadne Wu, http://radi.ws/ +// Copyright (c) 2013-2015 Ruslan Skorb, http://ruslanskorb.com +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +#import "RSDFAppDelegate.h" +#import "RSDFDatePickerViewController.h" + +@implementation RSDFAppDelegate + +- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions +{ + self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; + self.window.backgroundColor = [UIColor whiteColor]; + + // ------------------ + // Gregorian calendar + // ------------------ + + RSDFDatePickerViewController *gregorianVC = [[RSDFDatePickerViewController alloc] init]; + gregorianVC.calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierGregorian]; + gregorianVC.calendar.locale = [NSLocale currentLocale]; + UINavigationController *gregorianNC = [[UINavigationController alloc] initWithRootViewController:gregorianVC]; + + // --------------- + // Hebrew calendar + // --------------- + + RSDFDatePickerViewController *hebrewVC = [[RSDFDatePickerViewController alloc] init]; + hebrewVC.calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierHebrew]; + hebrewVC.calendar.locale = [NSLocale currentLocale]; + UINavigationController *hebrewNC = [[UINavigationController alloc] initWithRootViewController:hebrewVC]; + + // ---------------- + // Islamic UmmAlQura calendar + // ---------------- + + RSDFDatePickerViewController *islamicVC = [[RSDFDatePickerViewController alloc] init]; + islamicVC.calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierIslamicUmmAlQura]; + islamicVC.calendar.locale = [NSLocale currentLocale]; + UINavigationController *islamicNC = [[UINavigationController alloc] initWithRootViewController:islamicVC]; + + // --------------- + // Indian calendar + // --------------- + + RSDFDatePickerViewController *indianVC = [[RSDFDatePickerViewController alloc] init]; + indianVC.calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierIndian]; + indianVC.calendar.locale = [NSLocale currentLocale]; + UINavigationController *indianNC = [[UINavigationController alloc] initWithRootViewController:indianVC]; + + // ---------------- + // Persian calendar + // ---------------- + + RSDFDatePickerViewController *persianVC = [[RSDFDatePickerViewController alloc] init]; + persianVC.calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierPersian]; + persianVC.calendar.locale = [NSLocale currentLocale]; + UINavigationController *persianNC = [[UINavigationController alloc] initWithRootViewController:persianVC]; + + UITabBarController *tabBarController = [[UITabBarController alloc] init]; + tabBarController.viewControllers = @[gregorianNC, hebrewNC, islamicNC, indianNC, persianNC]; + self.window.rootViewController = tabBarController; + + [self.window makeKeyAndVisible]; + + return YES; +} + +@end diff --git a/Example/RSDayFlowExample/RSDFCustomDatePickerCollectionView.h b/Example/RSDayFlowExample/RSDFCustomDatePickerCollectionView.h new file mode 100644 index 0000000..d770e67 --- /dev/null +++ b/Example/RSDayFlowExample/RSDFCustomDatePickerCollectionView.h @@ -0,0 +1,30 @@ +// +// RSDFCustomDatePickerCollectionView.h +// +// Copyright (c) 2013 Evadne Wu, http://radi.ws/ +// Copyright (c) 2013-2015 Ruslan Skorb, http://ruslanskorb.com +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +#import "RSDFDatePickerCollectionView.h" + +@interface RSDFCustomDatePickerCollectionView : RSDFDatePickerCollectionView + +@end diff --git a/Example/RSDayFlowExample/RSDFCustomDatePickerCollectionView.m b/Example/RSDayFlowExample/RSDFCustomDatePickerCollectionView.m new file mode 100644 index 0000000..468083d --- /dev/null +++ b/Example/RSDayFlowExample/RSDFCustomDatePickerCollectionView.m @@ -0,0 +1,35 @@ +// +// RSDFCustomDatePickerCollectionView.m +// +// Copyright (c) 2013 Evadne Wu, http://radi.ws/ +// Copyright (c) 2013-2015 Ruslan Skorb, http://ruslanskorb.com +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +#import "RSDFCustomDatePickerCollectionView.h" + +@implementation RSDFCustomDatePickerCollectionView + +- (UIColor *)selfBackgroundColor +{ + return [UIColor clearColor]; +} + +@end diff --git a/Example/RSDayFlowExample/RSDFCustomDatePickerCollectionViewLayout.h b/Example/RSDayFlowExample/RSDFCustomDatePickerCollectionViewLayout.h new file mode 100644 index 0000000..b523e60 --- /dev/null +++ b/Example/RSDayFlowExample/RSDFCustomDatePickerCollectionViewLayout.h @@ -0,0 +1,30 @@ +// +// RSDFCustomDatePickerCollectionViewLayout.h +// +// Copyright (c) 2013 Evadne Wu, http://radi.ws/ +// Copyright (c) 2013-2015 Ruslan Skorb, http://ruslanskorb.com +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +#import "RSDFDatePickerCollectionViewLayout.h" + +@interface RSDFCustomDatePickerCollectionViewLayout : RSDFDatePickerCollectionViewLayout + +@end diff --git a/Example/RSDayFlowExample/RSDFCustomDatePickerCollectionViewLayout.m b/Example/RSDayFlowExample/RSDFCustomDatePickerCollectionViewLayout.m new file mode 100644 index 0000000..2dfe121 --- /dev/null +++ b/Example/RSDayFlowExample/RSDFCustomDatePickerCollectionViewLayout.m @@ -0,0 +1,42 @@ +// +// RSDFCustomDatePickerCollectionViewLayout.m +// +// Copyright (c) 2013 Evadne Wu, http://radi.ws/ +// Copyright (c) 2013-2015 Ruslan Skorb, http://ruslanskorb.com +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +#import "RSDFCustomDatePickerCollectionViewLayout.h" + +@implementation RSDFCustomDatePickerCollectionViewLayout + +#pragma mark - Layout Atrributes + +- (CGSize)selfHeaderReferenceSize +{ + return (CGSize){ [super selfHeaderReferenceSize].width, 54 }; +} + +- (CGSize)selfItemSize +{ + return (CGSize){ [super selfItemSize].width, 60 }; +} + +@end diff --git a/Example/RSDayFlowExample/RSDFCustomDatePickerDayCell.h b/Example/RSDayFlowExample/RSDFCustomDatePickerDayCell.h new file mode 100644 index 0000000..c2ee024 --- /dev/null +++ b/Example/RSDayFlowExample/RSDFCustomDatePickerDayCell.h @@ -0,0 +1,30 @@ +// +// RSDFCustomDatePickerDayCell.h +// +// Copyright (c) 2013 Evadne Wu, http://radi.ws/ +// Copyright (c) 2013-2015 Ruslan Skorb, http://ruslanskorb.com +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +#import "RSDFDatePickerDayCell.h" + +@interface RSDFCustomDatePickerDayCell : RSDFDatePickerDayCell + +@end diff --git a/Example/RSDayFlowExample/RSDFCustomDatePickerDayCell.m b/Example/RSDayFlowExample/RSDFCustomDatePickerDayCell.m new file mode 100644 index 0000000..dbc895b --- /dev/null +++ b/Example/RSDayFlowExample/RSDFCustomDatePickerDayCell.m @@ -0,0 +1,95 @@ +// +// RSDFCustomDatePickerDayCell.m +// +// Copyright (c) 2013 Evadne Wu, http://radi.ws/ +// Copyright (c) 2013-2015 Ruslan Skorb, http://ruslanskorb.com +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +#import "RSDFCustomDatePickerDayCell.h" + +@implementation RSDFCustomDatePickerDayCell + +- (UIFont *)dayLabelFont +{ + return [UIFont fontWithName:@"AvenirNext-Regular" size:16.0f]; +} + +- (UIColor *)dayLabelTextColor +{ + return [UIColor colorWithRed:51/255.0f green:37/255.0f blue:36/255.0f alpha:1.0f]; +} + +- (UIColor *)selectedDayImageColor +{ + return [UIColor clearColor]; +} + +- (UIFont *)selectedDayLabelFont +{ + return [self dayLabelFont]; +} + +- (UIColor *)selectedDayLabelTextColor +{ + return [self dayLabelTextColor]; +} + +- (UIColor *)dayOffLabelTextColor +{ + return [UIColor colorWithRed:51/255.0f green:37/255.0f blue:36/255.0f alpha:1.0f]; +} + +- (UIFont *)todayLabelFont +{ + return [UIFont fontWithName:@"AvenirNext-Bold" size:17.0f]; +} + +- (UIColor *)todayLabelTextColor +{ + return [UIColor colorWithRed:3/255.0f green:117/255.0f blue:214/255.0f alpha:1.0f]; +} + +- (UIColor *)selectedTodayImageColor +{ + return [UIColor clearColor]; +} + +- (UIFont *)selectedTodayLabelFont +{ + return [self todayLabelFont]; +} + +- (UIColor *)selectedTodayLabelTextColor +{ + return [self todayLabelTextColor]; +} + +- (UIColor *)overlayImageColor +{ + return [UIColor colorWithWhite:1.0f alpha:1.0f]; +} + +- (UIColor *)dividerImageColor +{ + return [UIColor clearColor]; +} + +@end diff --git a/Example/RSDayFlowExample/RSDFCustomDatePickerDaysOfWeekView.h b/Example/RSDayFlowExample/RSDFCustomDatePickerDaysOfWeekView.h new file mode 100644 index 0000000..77e3b9b --- /dev/null +++ b/Example/RSDayFlowExample/RSDFCustomDatePickerDaysOfWeekView.h @@ -0,0 +1,30 @@ +// +// RSDFCustomDatePickerDaysOfWeekView.h +// +// Copyright (c) 2013 Evadne Wu, http://radi.ws/ +// Copyright (c) 2013-2015 Ruslan Skorb, http://ruslanskorb.com +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +#import "RSDFDatePickerDaysOfWeekView.h" + +@interface RSDFCustomDatePickerDaysOfWeekView : RSDFDatePickerDaysOfWeekView + +@end diff --git a/Example/RSDayFlowExample/RSDFCustomDatePickerDaysOfWeekView.m b/Example/RSDayFlowExample/RSDFCustomDatePickerDaysOfWeekView.m new file mode 100644 index 0000000..1ea5227 --- /dev/null +++ b/Example/RSDayFlowExample/RSDFCustomDatePickerDaysOfWeekView.m @@ -0,0 +1,40 @@ +// +// RSDFCustomDatePickerDaysOfWeekView.m +// +// Copyright (c) 2013 Evadne Wu, http://radi.ws/ +// Copyright (c) 2013-2015 Ruslan Skorb, http://ruslanskorb.com +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +#import "RSDFCustomDatePickerDaysOfWeekView.h" + +@implementation RSDFCustomDatePickerDaysOfWeekView + +- (UIColor *)selfBackgroundColor +{ + return [UIColor colorWithRed:244/255.0f green:245/255.0f blue:247/255.0f alpha:1.0f]; +} + +- (UIColor *)dayOffOfWeekLabelTextColor +{ + return [UIColor blackColor]; +} + +@end diff --git a/Example/RSDayFlowExample/RSDFCustomDatePickerMonthHeader.h b/Example/RSDayFlowExample/RSDFCustomDatePickerMonthHeader.h new file mode 100644 index 0000000..4e42d66 --- /dev/null +++ b/Example/RSDayFlowExample/RSDFCustomDatePickerMonthHeader.h @@ -0,0 +1,30 @@ +// +// RSDFCustomDatePickerMonthHeader.h +// +// Copyright (c) 2013 Evadne Wu, http://radi.ws/ +// Copyright (c) 2013-2015 Ruslan Skorb, http://ruslanskorb.com +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +#import "RSDFDatePickerMonthHeader.h" + +@interface RSDFCustomDatePickerMonthHeader : RSDFDatePickerMonthHeader + +@end diff --git a/Example/RSDayFlowExample/RSDFCustomDatePickerMonthHeader.m b/Example/RSDayFlowExample/RSDFCustomDatePickerMonthHeader.m new file mode 100644 index 0000000..d093798 --- /dev/null +++ b/Example/RSDayFlowExample/RSDFCustomDatePickerMonthHeader.m @@ -0,0 +1,50 @@ +// +// RSDFCustomDatePickerMonthHeader.m +// +// Copyright (c) 2013 Evadne Wu, http://radi.ws/ +// Copyright (c) 2013-2015 Ruslan Skorb, http://ruslanskorb.com +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +#import "RSDFCustomDatePickerMonthHeader.h" + +@implementation RSDFCustomDatePickerMonthHeader + +- (UIColor *)selfBackgroundColor +{ + return [UIColor colorWithRed:244/255.0f green:245/255.0f blue:247/255.0f alpha:1.0f]; +} + +- (UIFont *)monthLabelFont +{ + return [UIFont fontWithName:@"Avenir-Medium" size:18.0f]; +} + +- (UIColor *)monthLabelTextColor +{ + return [UIColor colorWithRed:51/255.0f green:37/255.0f blue:36/255.0f alpha:1.0f]; +} + +- (UIColor *)currentMonthLabelTextColor +{ + return [UIColor colorWithRed:3/255.0f green:117/255.0f blue:214/255.0f alpha:1.0f]; +} + +@end diff --git a/Example/RSDayFlowExample/RSDFCustomDatePickerView.h b/Example/RSDayFlowExample/RSDFCustomDatePickerView.h new file mode 100644 index 0000000..51217ca --- /dev/null +++ b/Example/RSDayFlowExample/RSDFCustomDatePickerView.h @@ -0,0 +1,30 @@ +// +// RSDFCustomDatePickerView.h +// +// Copyright (c) 2013 Evadne Wu, http://radi.ws/ +// Copyright (c) 2013-2015 Ruslan Skorb, http://ruslanskorb.com +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +#import "RSDFDatePickerView.h" + +@interface RSDFCustomDatePickerView : RSDFDatePickerView + +@end diff --git a/Example/RSDayFlowExample/RSDFCustomDatePickerView.m b/Example/RSDayFlowExample/RSDFCustomDatePickerView.m new file mode 100644 index 0000000..5e72f42 --- /dev/null +++ b/Example/RSDayFlowExample/RSDFCustomDatePickerView.m @@ -0,0 +1,60 @@ +// +// RSDFCustomDatePickerView.m +// +// Copyright (c) 2013 Evadne Wu, http://radi.ws/ +// Copyright (c) 2013-2015 Ruslan Skorb, http://ruslanskorb.com +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +#import "RSDFCustomDatePickerView.h" +#import "RSDFCustomDatePickerDaysOfWeekView.h" +#import "RSDFCustomDatePickerCollectionView.h" +#import "RSDFCustomDatePickerCollectionViewLayout.h" +#import "RSDFCustomDatePickerMonthHeader.h" +#import "RSDFCustomDatePickerDayCell.h" + +@implementation RSDFCustomDatePickerView + +- (Class)daysOfWeekViewClass +{ + return [RSDFCustomDatePickerDaysOfWeekView class]; +} + +- (Class)collectionViewClass +{ + return [RSDFCustomDatePickerCollectionView class]; +} + +- (Class)collectionViewLayoutClass +{ + return [RSDFCustomDatePickerCollectionViewLayout class]; +} + +- (Class)monthHeaderClass +{ + return [RSDFCustomDatePickerMonthHeader class]; +} + +- (Class)dayCellClass +{ + return [RSDFCustomDatePickerDayCell class]; +} + +@end diff --git a/Example/RSDayFlowExample/RSDFDatePickerViewController.h b/Example/RSDayFlowExample/RSDFDatePickerViewController.h new file mode 100755 index 0000000..153f301 --- /dev/null +++ b/Example/RSDayFlowExample/RSDFDatePickerViewController.h @@ -0,0 +1,34 @@ +// +// RSDFDatePickerViewController.h +// +// Copyright (c) 2013 Evadne Wu, http://radi.ws/ +// Copyright (c) 2013-2015 Ruslan Skorb, http://ruslanskorb.com +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +#import + +@class RSDFDatePickerView; + +@interface RSDFDatePickerViewController : UIViewController + +@property (strong, nonatomic) NSCalendar *calendar; + +@end diff --git a/Example/RSDayFlowExample/RSDFDatePickerViewController.m b/Example/RSDayFlowExample/RSDFDatePickerViewController.m new file mode 100755 index 0000000..854fd61 --- /dev/null +++ b/Example/RSDayFlowExample/RSDFDatePickerViewController.m @@ -0,0 +1,229 @@ +// +// RSDFDatePickerViewController.m +// +// Copyright (c) 2013 Evadne Wu, http://radi.ws/ +// Copyright (c) 2013-2015 Ruslan Skorb, http://ruslanskorb.com +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +#import "RSDFDatePickerViewController.h" +#import "RSDFDatePickerView.h" +#import "RSDFCustomDatePickerView.h" + +@interface RSDFDatePickerViewController() + +@property (strong, nonatomic) NSArray *datesToMark; +@property (strong, nonatomic) NSDictionary *statesOfTasks; +@property (strong, nonatomic) NSDateFormatter *dateFormatter; +@property (strong, nonatomic) RSDFDatePickerView *datePickerView; +@property (strong, nonatomic) RSDFCustomDatePickerView *customDatePickerView; +@property (copy, nonatomic) UIColor *completedTasksColor; +@property (copy, nonatomic) UIColor *uncompletedTasksColor; + +@end + +@implementation RSDFDatePickerViewController + +#pragma mark - Lifecycle + +- (void)viewDidLoad +{ + [super viewDidLoad]; + + self.edgesForExtendedLayout = UIRectEdgeNone; + self.automaticallyAdjustsScrollViewInsets = NO; + + self.navigationController.navigationBar.translucent = NO; + self.navigationController.navigationBar.opaque = YES; + self.navigationController.navigationBar.barTintColor = [UIColor colorWithRed:248/255.0f green:248/255.0f blue:248/255.0f alpha:1.0f]; + + self.navigationController.navigationBar.titleTextAttributes = @{NSFontAttributeName: [UIFont fontWithName:@"HelveticaNeue-Medium" size:17.0f]}; + + [self.navigationController.navigationBar setBackgroundImage:[[UIImage alloc] init] forBarMetrics:UIBarMetricsDefault]; + self.navigationController.navigationBar.shadowImage = [[UIImage alloc] init]; + + UIBarButtonItem *todayBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"Today" style:UIBarButtonItemStylePlain target:self action:@selector(onTodayButtonTouch:)]; + self.navigationItem.rightBarButtonItem = todayBarButtonItem; + + UIBarButtonItem *restyleBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"Restyle" style:UIBarButtonItemStylePlain target:self action:@selector(onRestyleButtonTouch:)]; + self.navigationItem.leftBarButtonItem = restyleBarButtonItem; + + self.view.backgroundColor = [UIColor colorWithWhite:0.8 alpha:0.3]; + + NSDateComponents *todayComponents = [self.calendar components:(NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitDay) fromDate:[NSDate date]]; + NSDate *today = [self.calendar dateFromComponents:todayComponents]; + [self.datePickerView selectDate:today]; + + self.customDatePickerView.hidden = YES; + + [self.view addSubview:self.customDatePickerView]; + [self.view addSubview:self.datePickerView]; +} + +#pragma mark - Custom Accessors + +- (void)setCalendar:(NSCalendar *)calendar +{ + if (![_calendar isEqual:calendar]) { + _calendar = calendar; + + self.title = [_calendar.calendarIdentifier capitalizedString]; + } +} + +- (NSArray *)datesToMark +{ + if (!_datesToMark) { + NSDateComponents *todayComponents = [self.calendar components:(NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitDay) fromDate:[NSDate date]]; + NSDate *today = [self.calendar dateFromComponents:todayComponents]; + + NSArray *numberOfDaysFromToday = @[@(-8), @(-2), @(-1), @(0), @(2), @(4), @(8), @(13), @(22)]; + + NSDateComponents *dateComponents = [[NSDateComponents alloc] init]; + NSMutableArray *datesToMark = [[NSMutableArray alloc] initWithCapacity:[numberOfDaysFromToday count]]; + [numberOfDaysFromToday enumerateObjectsUsingBlock:^(NSNumber *numberOfDays, NSUInteger idx, BOOL *stop) { + dateComponents.day = [numberOfDays integerValue]; + NSDate *date = [self.calendar dateByAddingComponents:dateComponents toDate:today options:0]; + [datesToMark addObject:date]; + }]; + + _datesToMark = [datesToMark copy]; + } + return _datesToMark; +} + +- (NSDictionary *)statesOfTasks +{ + if (!_statesOfTasks) { + NSDateComponents *todayComponents = [self.calendar components:(NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitDay) fromDate:[NSDate date]]; + NSDate *today = [self.calendar dateFromComponents:todayComponents]; + + NSMutableDictionary *statesOfTasks = [[NSMutableDictionary alloc] initWithCapacity:[self.datesToMark count]]; + [self.datesToMark enumerateObjectsUsingBlock:^(NSDate *date, NSUInteger idx, BOOL *stop) { + BOOL isCompletedAllTasks = NO; + if ([date compare:today] == NSOrderedAscending) { + isCompletedAllTasks = YES; + } + statesOfTasks[date] = @(isCompletedAllTasks); + }]; + + _statesOfTasks = [statesOfTasks copy]; + } + return _statesOfTasks; +} + +- (UIColor *)completedTasksColor +{ + if (!_completedTasksColor) { + _completedTasksColor = [UIColor colorWithRed:83/255.0f green:215/255.0f blue:105/255.0f alpha:1.0f]; + } + return _completedTasksColor; +} + +- (UIColor *)uncompletedTasksColor +{ + if (!_uncompletedTasksColor) { + _uncompletedTasksColor = [UIColor colorWithRed:184/255.0f green:184/255.0f blue:184/255.0f alpha:1.0f]; + } + return _uncompletedTasksColor; +} + +- (NSDateFormatter *)dateFormatter +{ + if (!_dateFormatter) { + _dateFormatter = [[NSDateFormatter alloc] init]; + [_dateFormatter setCalendar:self.calendar]; + [_dateFormatter setLocale:[self.calendar locale]]; + [_dateFormatter setDateStyle:NSDateFormatterFullStyle]; + } + return _dateFormatter; +} + +- (RSDFDatePickerView *)datePickerView +{ + if (!_datePickerView) { + _datePickerView = [[RSDFDatePickerView alloc] initWithFrame:self.view.bounds calendar:self.calendar]; + _datePickerView.delegate = self; + _datePickerView.dataSource = self; + _datePickerView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; + } + return _datePickerView; +} + +- (RSDFCustomDatePickerView *)customDatePickerView +{ + if (!_customDatePickerView) { + _customDatePickerView = [[RSDFCustomDatePickerView alloc] initWithFrame:self.view.bounds calendar:self.calendar]; + _customDatePickerView.delegate = self; + _customDatePickerView.dataSource = self; + _customDatePickerView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; + _customDatePickerView.pagingEnabled = YES; + } + return _customDatePickerView; +} + +#pragma mark - Action handling + +- (void)onTodayButtonTouch:(UIBarButtonItem *)sender +{ + if (!self.datePickerView.hidden) { + [self.datePickerView scrollToToday:YES]; + } else { + [self.customDatePickerView scrollToToday:YES]; + } +} + +- (void)onRestyleButtonTouch:(UIBarButtonItem *)sender +{ + if (!self.datePickerView.hidden) { + self.navigationController.navigationBar.barTintColor = [UIColor colorWithRed:244/255.0f green:245/255.0f blue:247/255.0f alpha:1.0f]; + self.datePickerView.hidden = YES; + self.customDatePickerView.hidden = NO; + } else { + self.navigationController.navigationBar.barTintColor = [UIColor colorWithRed:248/255.0f green:248/255.0f blue:248/255.0f alpha:1.0f]; + self.customDatePickerView.hidden = YES; + self.datePickerView.hidden = NO; + } +} + +#pragma mark - RSDFDatePickerViewDelegate + +- (void)datePickerView:(RSDFDatePickerView *)view didSelectDate:(NSDate *)date +{ + [[[UIAlertView alloc] initWithTitle:@"Picked Date" message:[self.dateFormatter stringFromDate:date] delegate:nil cancelButtonTitle:@":D" otherButtonTitles:nil] show]; +} + +#pragma mark - RSDFDatePickerViewDataSource + +- (BOOL)datePickerView:(RSDFDatePickerView *)view shouldMarkDate:(NSDate *)date +{ + return [self.datesToMark containsObject:date]; +} + +- (UIColor *)datePickerView:(RSDFDatePickerView *)view markImageColorForDate:(NSDate *)date +{ + if (![self.statesOfTasks[date] boolValue]) { + return self.uncompletedTasksColor; + } else { + return self.completedTasksColor; + } +} + +@end diff --git a/Example/RSDayFlowExample/RSDayFlowExample-Info.plist b/Example/RSDayFlowExample/RSDayFlowExample-Info.plist new file mode 100644 index 0000000..069d53b --- /dev/null +++ b/Example/RSDayFlowExample/RSDayFlowExample-Info.plist @@ -0,0 +1,40 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleDisplayName + ${PRODUCT_NAME} + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIdentifier + com.ruslanskorb.${PRODUCT_NAME:rfc1034identifier} + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + ${PRODUCT_NAME} + CFBundlePackageType + APPL + CFBundleShortVersionString + 0.9.0 + CFBundleSignature + ???? + CFBundleVersion + 1 + LSRequiresIPhoneOS + + UILaunchStoryboardName + LaunchScreen + UIRequiredDeviceCapabilities + + armv7 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + + diff --git a/Example/RSDayFlowExample/RSDayFlowExample-Prefix.pch b/Example/RSDayFlowExample/RSDayFlowExample-Prefix.pch new file mode 100644 index 0000000..743435c --- /dev/null +++ b/Example/RSDayFlowExample/RSDayFlowExample-Prefix.pch @@ -0,0 +1,16 @@ +// +// Prefix header +// +// The contents of this file are implicitly included at the beginning of every source file. +// + +#import + +#ifndef __IPHONE_3_0 +#warning "This project uses features only available in iOS SDK 3.0 and later." +#endif + +#ifdef __OBJC__ + #import + #import +#endif diff --git a/Example/RSDayFlowExample/en.lproj/InfoPlist.strings b/Example/RSDayFlowExample/en.lproj/InfoPlist.strings new file mode 100644 index 0000000..477b28f --- /dev/null +++ b/Example/RSDayFlowExample/en.lproj/InfoPlist.strings @@ -0,0 +1,2 @@ +/* Localized versions of Info.plist keys */ + diff --git a/Example/RSDayFlowExample/main.m b/Example/RSDayFlowExample/main.m new file mode 100644 index 0000000..3ba6626 --- /dev/null +++ b/Example/RSDayFlowExample/main.m @@ -0,0 +1,35 @@ +// +// main.m +// +// Copyright (c) 2013 Evadne Wu, http://radi.ws/ +// Copyright (c) 2013-2015 Ruslan Skorb, http://ruslanskorb.com +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +#import + +#import "RSDFAppDelegate.h" + +int main(int argc, char * argv[]) +{ + @autoreleasepool { + return UIApplicationMain(argc, argv, nil, NSStringFromClass([RSDFAppDelegate class])); + } +} diff --git a/Example/RSDayFlowExampleTests/RSDayFlowExampleTests-Info.plist b/Example/RSDayFlowExampleTests/RSDayFlowExampleTests-Info.plist new file mode 100644 index 0000000..b0ff0e6 --- /dev/null +++ b/Example/RSDayFlowExampleTests/RSDayFlowExampleTests-Info.plist @@ -0,0 +1,22 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIdentifier + com.ruslanskorb.${PRODUCT_NAME:rfc1034identifier} + CFBundleInfoDictionaryVersion + 6.0 + CFBundlePackageType + BNDL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1 + + diff --git a/Example/RSDayFlowExampleTests/RSDayFlowExampleTests.m b/Example/RSDayFlowExampleTests/RSDayFlowExampleTests.m new file mode 100644 index 0000000..a726477 --- /dev/null +++ b/Example/RSDayFlowExampleTests/RSDayFlowExampleTests.m @@ -0,0 +1,51 @@ +// +// RSDayFlowExampleTests.m +// +// Copyright (c) 2013 Evadne Wu, http://radi.ws/ +// Copyright (c) 2013-2015 Ruslan Skorb, http://ruslanskorb.com +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +#import + +@interface RSDayFlowExampleTests : XCTestCase + +@end + +@implementation RSDayFlowExampleTests + +- (void)setUp +{ + [super setUp]; + // Put setup code here. This method is called before the invocation of each test method in the class. +} + +- (void)tearDown +{ + // Put teardown code here. This method is called after the invocation of each test method in the class. + [super tearDown]; +} + +- (void)testExample +{ + XCTFail(@"No implementation for \"%s\"", __PRETTY_FUNCTION__); +} + +@end diff --git a/Example/RSDayFlowExampleTests/en.lproj/InfoPlist.strings b/Example/RSDayFlowExampleTests/en.lproj/InfoPlist.strings new file mode 100644 index 0000000..477b28f --- /dev/null +++ b/Example/RSDayFlowExampleTests/en.lproj/InfoPlist.strings @@ -0,0 +1,2 @@ +/* Localized versions of Info.plist keys */ + diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..c4f5501 --- /dev/null +++ b/LICENSE @@ -0,0 +1,20 @@ +Copyright (c) 2013 Evadne Wu, http://radi.ws/ +Copyright (c) 2013-2015 Ruslan Skorb, http://ruslanskorb.com/ + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/README.md b/README.md index ec5d042..9f470d8 100644 --- a/README.md +++ b/README.md @@ -1,23 +1,149 @@ -# DayFlow +# RSDayFlow -iOS Date Picker + Infinite Scrolling. +

+ Sample +

-## Play +iOS 7 Calendar with Infinite Scrolling. Only need 4 lines of code to set up. -Look at the [Sample App](https://github.com/evadne/DayFlow-Sample). Check out the [Sample Video](http://vimeo.com/evadne/dayflow-debut). Have fun. Make it faster. Fork and send pull requests. Figure out hooks for customization. +> [RSDayFlow](https://github.com/ruslanskorb/RSDayFlow) is a slim fork of [DayFlow](https://github.com/evadne/DayFlow) with updates and extensions: -## Use +> * Visual feedback of the currently selected cell +* Possibility to mark the date +* Design like iOS 7 +* Much more updates -Plop `DFDatePickerViewController` in, and implement the one method in ``: +## Installation - - (void) datePickerViewController:(DFDatePickerViewController *)controller didSelectDate:(NSDate *)date; +[CocoaPods](http://cocoapods.org) is the recommended method of installing RSDayFlow. Simply add the following line to your `Podfile`: -That pretty much sums up what it does. +#### Podfile -## Licensing +```ruby +pod 'RSDayFlow' +``` -This project is in the public domain. You can embed it in works for hire or use it for evil. Attribution by linking to the [project page](https://github.com/evadne/DayFlow) and chocolate delivery is appreciated. +## Basic Usage -## Credits +Import the class header. -* [Evadne Wu](http://radi.ws) +``` objective-c +#import "RSDFDatePickerView.h" +``` + +Just create your date picker view and set a delegate / a data source if needed. + +``` objective-c +- (void)viewDidLoad +{ + [super viewDidLoad]; + + RSDFDatePickerView *datePickerView = [[RSDFDatePickerView alloc] initWithFrame:self.view.bounds]; + datePickerView.delegate = self; + datePickerView.dataSource = self; + [self.view addSubview:datePickerView]; +} +``` + +## Delegate (optional) + +`RSDFDatePickerView` provides three delegate methods. The method `datePickerView:shouldHighlightDate:` asks the delegate if the date should be highlighted during tracking. The method `datePickerView:shouldSelectDate:` asks the delegate if the specified date should be selected. The method `datePickerView:didSelectDate:` called when a user click on a specific date. To use them, implement the delegate in your view controller. + +```objective-c +@interface ViewController () +``` + +Then implement the delegate functions. + +```objective-c +// Returns YES if the date should be highlighted or NO if it should not. +- (BOOL)datePickerView:(RSDFDatePickerView *)view shouldHighlightDate:(NSDate *)date +{ + return YES; +} + +// Returns YES if the date should be selected or NO if it should not. +- (BOOL)datePickerView:(RSDFDatePickerView *)view shouldSelectDate:(NSDate *)date +{ + return YES; +} + +// Prints out the selected date. +- (void)datePickerView:(RSDFDatePickerView *)view didSelectDate:(NSDate *)date +{ + NSLog(@"%@", [date description]); +} +``` + +## DataSource (optional) + +`RSDFDatePickerView` provides three data source methods. The method `datePickerView:shouldMarkDate:` asks the data source if the date should be marked. The method `datePickerView:markImageColorForDate:` asks the data source about the color of the default mark image for the specified date. The method `datePickerView:markImageForDate:` asks the data source about the mark image for the specified date. The method `datePickerView:markImageColorForDate:` will be ignored if the method `datePickerView:markImageForDate:` is implemented. To use these methods, implement the data source in your view controller. + +```objective-c +@interface ViewController () +``` + +Then implement the data source functions. + +```objective-c +// Returns YES if the date should be marked or NO if it should not. +- (BOOL)datePickerView:(RSDFDatePickerView *)view shouldMarkDate:(NSDate *)date +{ + // The date is an `NSDate` object without time components. + // So, we need to use dates without time components. + + NSCalendar *calendar = [NSCalendar currentCalendar]; + unsigned unitFlags = NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit; + NSDateComponents *todayComponents = [calendar components:unitFlags fromDate:[NSDate date]]; + NSDate *today = [calendar dateFromComponents:todayComponents]; + + return [date isEqual:today]; +} + +// Returns the color of the default mark image for the specified date. +- (UIColor *)datePickerView:(RSDFDatePickerView *)view markImageColorForDate:(NSDate *)date +{ + if (arc4random() % 2 == 0) { + return [UIColor grayColor]; + } else { + return [UIColor greenColor]; + } +} + +// Returns the mark image for the specified date. +- (UIImage *)datePickerView:(RSDFDatePickerView *)view markImageForDate:(NSDate *)date +{ + if (arc4random() % 2 == 0) { + return [UIImage imageNamed:@"img_gray_mark"]; + } else { + return [UIImage imageNamed:@"img_green_mark"]; + } +} +``` + +## Customization + +Every view is customizable to fit your need. +Create a subclass of the desired view and override the default values. + +## Coming Soon + +- If you would like to request a new feature, feel free to raise as an issue. + +## Demo + +Build and run the `RSDayFlowExample` project in Xcode to see `RSDayFlow` in action. +Have fun. Make it faster. Fork and send pull requests. Figure out hooks for customization. + + +## Contact + +Ruslan Skorb + +- http://github.com/ruslanskorb +- http://twitter.com/ruslanskorb +- ruslan.skorb@gmail.com + +## License + +This project is is available under the MIT license. See the LICENSE file for more info. Attribution by linking to the [project page](https://github.com/ruslanskorb/RSDayFlow) is appreciated. diff --git a/RSDayFlow.podspec b/RSDayFlow.podspec new file mode 100644 index 0000000..6b3fb7c --- /dev/null +++ b/RSDayFlow.podspec @@ -0,0 +1,13 @@ +Pod::Spec.new do |s| + s.name = 'RSDayFlow' + s.version = '0.9.0' + s.summary = 'iOS 7 Calendar with Infinite Scrolling.' + s.homepage = 'https://github.com/ruslanskorb/RSDayFlow' + s.license = { :type => 'MIT', :file => 'LICENSE' } + s.authors = { 'Evadne Wu' => 'ev@radi.ws', 'Ruslan Skorb' => 'ruslan.skorb@gmail.com' } + s.source = { :git => 'https://github.com/ruslanskorb/RSDayFlow.git', :tag => s.version.to_s } + s.platform = :ios, '7.0' + s.source_files = 'RSDayFlow' + s.frameworks = 'QuartzCore', 'UIKit' + s.requires_arc = true +end diff --git a/RSDayFlow/NSCalendar+RSDFAdditions.h b/RSDayFlow/NSCalendar+RSDFAdditions.h new file mode 100644 index 0000000..78c34d8 --- /dev/null +++ b/RSDayFlow/NSCalendar+RSDFAdditions.h @@ -0,0 +1,32 @@ +// +// NSCalendar+RSDFAdditions.h +// +// Copyright (c) 2013 Evadne Wu, http://radi.ws/ +// Copyright (c) 2013-2015 Ruslan Skorb, http://ruslanskorb.com +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +#import + +@interface NSCalendar (RSDFAdditions) + +- (NSDateFormatter *)df_dateFormatterNamed:(NSString *)name withConstructor:(NSDateFormatter *(^)(void))block; + +@end diff --git a/RSDayFlow/NSCalendar+RSDFAdditions.m b/RSDayFlow/NSCalendar+RSDFAdditions.m new file mode 100644 index 0000000..347cb6c --- /dev/null +++ b/RSDayFlow/NSCalendar+RSDFAdditions.m @@ -0,0 +1,47 @@ +// +// NSCalendar+RSDFAdditions.m +// +// Copyright (c) 2013 Evadne Wu, http://radi.ws/ +// Copyright (c) 2013-2015 Ruslan Skorb, http://ruslanskorb.com +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +#import "NSCalendar+RSDFAdditions.h" + +@implementation NSCalendar (RSDFAdditions) + +- (NSDateFormatter *)df_dateFormatterNamed:(NSString *)name withConstructor:(NSDateFormatter *(^)(void))block +{ + // We can not use objc_setAssociatedObject() because it has no thread safety + // Modeled after http://api.rubyonrails.org/classes/ActiveSupport/Cache/Store.html + // Intended for use where there are a myriad of date formatters keyed on a calendar + + NSMutableDictionary *threadDictionary = [[NSThread currentThread] threadDictionary]; + NSDateFormatter *dateFormatter = threadDictionary[name]; + + if (!dateFormatter) { + dateFormatter = block(); + threadDictionary[name] = dateFormatter; + } + + return dateFormatter; +} + +@end diff --git a/RSDayFlow/RSDFDatePickerCollectionView.h b/RSDayFlow/RSDFDatePickerCollectionView.h new file mode 100644 index 0000000..4670b58 --- /dev/null +++ b/RSDayFlow/RSDFDatePickerCollectionView.h @@ -0,0 +1,72 @@ +// +// RSDFDatePickerCollectionView.h +// +// Copyright (c) 2013 Evadne Wu, http://radi.ws/ +// Copyright (c) 2013-2015 Ruslan Skorb, http://ruslanskorb.com +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +#import + +@class RSDFDatePickerCollectionView; + +/** + The `RSDFDatePickerCollectionViewDelegate` protocol defines the message sent to a collection view delegate when the collection view will layout subviews. + */ +@protocol RSDFDatePickerCollectionViewDelegate + +///--------------------------------- +/// @name Supporting Layout Subviews +///--------------------------------- + +/** + Tells the delegate that the collection view will layout subviews. + + @param pickerCollectionView The collection view which will layout subviews. + */ +- (void) pickerCollectionViewWillLayoutSubviews:(RSDFDatePickerCollectionView *)pickerCollectionView; + +@end + + +/** + The `RSDFDatePickerCollectionView` is a collection view which used to display days and months in the date picker view. + */ +@interface RSDFDatePickerCollectionView : UICollectionView + +/** + The receiver's delegate. + + @discussion A `RSDFDatePickerCollectionView` delegate uses to support layout subviews in the date picker view. + */ +@property (nonatomic, assign) id delegate; + +///--------------------------------------- +/// @name Accessing Attributes of the View +///--------------------------------------- + +/** + The view’s background color. Default value is `[UIColor whiteColor]`. + + @discussion Can be overridden in subclasses for customization. + */ +- (UIColor *)selfBackgroundColor; + +@end diff --git a/RSDayFlow/RSDFDatePickerCollectionView.m b/RSDayFlow/RSDFDatePickerCollectionView.m new file mode 100644 index 0000000..143028f --- /dev/null +++ b/RSDayFlow/RSDFDatePickerCollectionView.m @@ -0,0 +1,86 @@ +// +// RSDFDatePickerCollectionView.m +// +// Copyright (c) 2013 Evadne Wu, http://radi.ws/ +// Copyright (c) 2013-2015 Ruslan Skorb, http://ruslanskorb.com +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +#import "RSDFDatePickerCollectionView.h" +#import "RSDFDatePickerCollectionViewLayout.h" + +@implementation RSDFDatePickerCollectionView + +@dynamic delegate; + +#pragma mark - Lifecycle + +- (instancetype)initWithFrame:(CGRect)frame +{ + self = [super initWithFrame:frame]; + if (self) { + [self commonInitializer]; + } + return self; +} + +- (instancetype)initWithCoder:(NSCoder *)aDecoder +{ + self = [super initWithCoder:aDecoder]; + if (self) { + [self commonInitializer]; + } + return self; +} + +- (instancetype)initWithFrame:(CGRect)frame collectionViewLayout:(UICollectionViewLayout *)layout +{ + self = [super initWithFrame:frame collectionViewLayout:layout]; + if (self) { + [self commonInitializer]; + } + return self; +} + +- (void)commonInitializer +{ + self.backgroundColor = [self selfBackgroundColor]; + self.showsVerticalScrollIndicator = NO; + self.showsHorizontalScrollIndicator = NO; + self.scrollsToTop = NO; + self.delaysContentTouches = NO; +} + +- (void)layoutSubviews +{ + if ([self.delegate respondsToSelector:@selector(pickerCollectionViewWillLayoutSubviews:)]) { + [self.delegate pickerCollectionViewWillLayoutSubviews:self]; + } + [super layoutSubviews]; +} + +#pragma mark - Atrributes of the View + +- (UIColor *)selfBackgroundColor +{ + return [UIColor whiteColor]; +} + +@end diff --git a/RSDayFlow/RSDFDatePickerCollectionViewLayout.h b/RSDayFlow/RSDFDatePickerCollectionViewLayout.h new file mode 100644 index 0000000..d9b38f3 --- /dev/null +++ b/RSDayFlow/RSDFDatePickerCollectionViewLayout.h @@ -0,0 +1,80 @@ +// +// RSDFDatePickerCollectionViewLayout.h +// +// Copyright (c) 2013 Evadne Wu, http://radi.ws/ +// Copyright (c) 2013-2015 Ruslan Skorb, http://ruslanskorb.com +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +#import + +/** + Types of supported directions of the layout of the collection view. + */ +typedef NS_ENUM(NSUInteger, RSDFDatePickerCollectionViewLayoutDirection) { + RSDFDatePickerCollectionViewLayoutDirectionLeftToRight, + RSDFDatePickerCollectionViewLayoutDirectionRightToLeft +}; + +/** + The `RSDFDatePickerCollectionViewLayout` is a layout of the collection view which used the date picker. + */ +@interface RSDFDatePickerCollectionViewLayout : UICollectionViewFlowLayout + +/** + Designated initializer. Initializes and returns a newly allocated layout object with the specified direction. + + @param frame The direction of the layout. + */ +- (instancetype)initWithDirection:(RSDFDatePickerCollectionViewLayoutDirection)direction; + +///----------------------------------------- +/// @name Accessing Attributes of the Layout +///----------------------------------------- + +/** + The default size to use for section headers. Default height is `64.0f`. Default width is calculated based on the width of the collection view. + + @discussion Can be overridden in subclasses for customization. + */ +- (CGSize)selfHeaderReferenceSize; + +/** + The default size to use for cells. Default height is `70.0f`. Default width is calculated based on the width of the collection view. + + @discussion Can be overridden in subclasses for customization. + */ +- (CGSize)selfItemSize; + +/** + The minimum spacing to use between lines of items in the grid. Default value is `2.0f`. + + @discussion Can be overridden in subclasses for customization. + */ +- (CGFloat)selfMinimumLineSpacing; + +/** + The minimum spacing to use between items in the same row. Default value is `2.0f`. + + @discussion Can be overridden in subclasses for customization. + */ +- (CGFloat)selfMinimumInteritemSpacing; + +@end diff --git a/RSDayFlow/RSDFDatePickerCollectionViewLayout.m b/RSDayFlow/RSDFDatePickerCollectionViewLayout.m new file mode 100644 index 0000000..87a92a5 --- /dev/null +++ b/RSDayFlow/RSDFDatePickerCollectionViewLayout.m @@ -0,0 +1,121 @@ +// +// RSDFDatePickerCollectionViewLayout.m +// +// Copyright (c) 2013 Evadne Wu, http://radi.ws/ +// Copyright (c) 2013-2015 Ruslan Skorb, http://ruslanskorb.com +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +#import "RSDFDatePickerCollectionViewLayout.h" + +@interface RSDFDatePickerCollectionViewLayout () + +@property (assign, nonatomic) RSDFDatePickerCollectionViewLayoutDirection direction; + +@end + +@implementation RSDFDatePickerCollectionViewLayout + +#pragma mark - Lifecycle + +- (instancetype)init +{ + self = [super init]; + if (self) { + [self commonInitializer]; + _direction = RSDFDatePickerCollectionViewLayoutDirectionLeftToRight; + } + return self; +} + +- (instancetype)initWithCoder:(NSCoder *)aDecoder +{ + self = [super initWithCoder:aDecoder]; + if (self) { + [self commonInitializer]; + _direction = RSDFDatePickerCollectionViewLayoutDirectionLeftToRight; + } + return self; +} + +- (instancetype)initWithDirection:(RSDFDatePickerCollectionViewLayoutDirection)direction +{ + self = [super init]; + if (self) { + [self commonInitializer]; + _direction = direction; + } + return self; +} + +- (void)commonInitializer +{ + self.minimumLineSpacing = [self selfMinimumLineSpacing]; + self.minimumInteritemSpacing = [self selfMinimumInteritemSpacing]; +} + +#pragma mark - Atrributes of the Layout + +- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect +{ + NSArray *supersAttributes = [super layoutAttributesForElementsInRect:rect]; + + if (self.direction == RSDFDatePickerCollectionViewLayoutDirectionRightToLeft) { + for (UICollectionViewLayoutAttributes *attributes in supersAttributes) { + CGRect frame = attributes.frame; + frame.origin.x = CGRectGetWidth(rect) - CGRectGetWidth(attributes.frame) - CGRectGetMinX(attributes.frame); + attributes.frame = frame; + } + } + + return supersAttributes; +} + +- (CGSize)selfHeaderReferenceSize +{ + CGFloat selfHeaderReferenceWidth = CGRectGetWidth(self.collectionView.frame); + CGFloat selfHeaderReferenceHeight = 64.0f; + + return (CGSize){ selfHeaderReferenceWidth, selfHeaderReferenceHeight }; +} + +- (CGSize)selfItemSize +{ + NSUInteger numberOfItemsInTheSameRow = 7; + CGFloat totalInteritemSpacing = [self minimumInteritemSpacing] * (numberOfItemsInTheSameRow - 1); + + CGFloat selfItemWidth = (CGRectGetWidth(self.collectionView.frame) - totalInteritemSpacing) / numberOfItemsInTheSameRow; + selfItemWidth = floor(selfItemWidth * 1000) / 1000; + CGFloat selfItemHeight = 70.0f; + + return (CGSize){ selfItemWidth, selfItemHeight }; +} + +- (CGFloat)selfMinimumLineSpacing +{ + return 2.0f; +} + +- (CGFloat)selfMinimumInteritemSpacing +{ + return 2.0f; +} + +@end diff --git a/RSDayFlow/RSDFDatePickerDayCell.h b/RSDayFlow/RSDFDatePickerDayCell.h new file mode 100644 index 0000000..e58390d --- /dev/null +++ b/RSDayFlow/RSDFDatePickerDayCell.h @@ -0,0 +1,227 @@ +// +// RSDFDatePickerDayCell.h +// +// Copyright (c) 2013 Evadne Wu, http://radi.ws/ +// Copyright (c) 2013-2015 Ruslan Skorb, http://ruslanskorb.com +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +#import +#import "RSDayFlow.h" + +/** + The `RSDFDatePickerDayCell` is a cell which used to display a day in the date picker view. + */ +@interface RSDFDatePickerDayCell : UICollectionViewCell + +///------------------------- +/// @name Accessing Subviews +///------------------------- + +/** + The label showing the cell's date. + */ +@property (nonatomic, readonly, strong) UILabel *dateLabel; + +///-------------------------------------- +/// @name Accessing Attributes of the Day +///-------------------------------------- + +/** + A date which corresponds to the current cell. + */ +@property (nonatomic, readwrite, assign) RSDFDatePickerDate date; + +/** + A Boolean value that determines whether the cell's day that's not this month. + + @discussion Cells with these days do not display the today image, the overlay image, the marks and the divider image. +*/ +@property (nonatomic, getter = isNotThisMonth) BOOL notThisMonth; + +/** + A Boolean value that determines whether the cell's day is day off. + */ +@property (nonatomic, getter = isDayOff) BOOL dayOff; + +/** + A Boolean value that determines whether the cell represents the current day. + */ +@property (nonatomic, getter = isToday) BOOL today; + +/** + A Boolean value that determines whether the cell have a mark. + */ +@property (nonatomic, getter = isMarked) BOOL marked; + +/** + The color of the default mark image for the cell of the day. Default value is [UIColor colorWithRed:184/255.0f green:184/255.0f blue:184/255.0f alpha:1.0f]. + + @discussion Will be used to create the mark image if `markImage` is equal to `nil`. + */ +@property (nonatomic, copy) UIColor *markImageColor; + +/** + The mark image for the cell of the day. The default mark image is a small round mark. + */ +@property (nonatomic, strong) UIImage *markImage; + +///--------------------------------------- +/// @name Accessing Attributes of the View +///--------------------------------------- + +/** + The view’s background color. Default value is `[UIColor clearColor]`. + + @discussion Can be overridden in subclasses for customization. + */ +- (UIColor *)selfBackgroundColor; + +///--------------------------------------- +/// @name Accessing Attributes of Subviews +///--------------------------------------- + +/** + The font of the text which displayed by the label of the day. Default value is [UIFont fontWithName:@"HelveticaNeue" size:18.0f]. + + @discussion Can be overridden in subclasses for customization. + */ +- (UIFont *)dayLabelFont; + +/** + The text color for the label of the day. Default value is [UIColor blackColor]. + + @discussion Can be overridden in subclasses for customization. + */ +- (UIColor *)dayLabelTextColor; + +/** + The text color for the label of the day off. Default value is [UIColor colorWithRed:184/255.0f green:184/255.0f blue:184/255.0f alpha:1.0f]. + + @discussion Can be overridden in subclasses for customization. + */ +- (UIColor *)dayOffLabelTextColor; + +/** + The text color for the label of the day that's not this month. Default value is [UIColor clearColor]. + + @discussion Can be overridden in subclasses for customization. + */ +- (UIColor *)notThisMonthLabelTextColor; + +/** + The font for the label of the current day. Default value is [UIFont fontWithName:@"HelveticaNeue" size:18.0f]. + + @discussion Can be overridden in subclasses for customization. + */ +- (UIFont *)todayLabelFont; + +/** + The text color for the label of the current day. Default value is [UIColor colorWithRed:0/255.0f green:121/255.0f blue:255/255.0f alpha:1.0f]. + + @discussion Can be overridden in subclasses for customization. + */ +- (UIColor *)todayLabelTextColor; + +/** + The font for the label of the current day when it is selected. Default value is [UIFont fontWithName:@"HelveticaNeue-Bold" size:19.0f]. + + @discussion Can be overridden in subclasses for customization. + */ +- (UIFont *)selectedTodayLabelFont; + +/** + The text color for the label of the current day when it is selected. Default value is [UIColor whiteColor]. + + @discussion Can be overridden in subclasses for customization. + */ +- (UIColor *)selectedTodayLabelTextColor; + +/** + The color of the background image for the cell of the current day. Default value is [UIColor colorWithRed:0/255.0f green:121/255.0f blue:255/255.0f alpha:1.0f]. + + @discussion Can be overridden in subclasses for customization. Ignored if `customSelectedTodayImage` is not equal to `nil`. + */ +- (UIColor *)selectedTodayImageColor; + +/** + The custom background image for the cell of the current day. Default value is `nil`. + + @discussion Can be overridden in subclasses for customization. + */ +- (UIImage *)customSelectedTodayImage; + +/** + The font for the label of the day when it is selected. Default value is [UIFont fontWithName:@"HelveticaNeue-Bold" size:19.0f]. + + @discussion Can be overridden in subclasses for customization. + */ +- (UIFont *)selectedDayLabelFont; + +/** + The text color for the label of the day when it is selected. Default value is [UIColor whiteColor]. + + @discussion Can be overridden in subclasses for customization. + */ +- (UIColor *)selectedDayLabelTextColor; + +/** + The color of the background image for the cell of the day when it is selected. Default value is [UIColor colorWithRed:255/255.0f green:59/255.0f blue:48/255.0f alpha:1.0f]. + + @discussion Can be overridden in subclasses for customization. Ignored if `customSelectedDayImage` is not equal to `nil`. + */ +- (UIColor *)selectedDayImageColor; + +/** + The custom background image for the cell of the day when it is selected. Default value is `nil`. + + @discussion Can be overridden in subclasses for customization. + */ +- (UIImage *)customSelectedDayImage; + +/** + The color of the overlay image for the cell of the day. Default value is [UIColor colorWithRed:184/255.0f green:184/255.0f blue:184/255.0f alpha:1.0f]. + + @discussion Can be overridden in subclasses for customization. Ignored if `customOverlayImage` is not equal to `nil`. + */ +- (UIColor *)overlayImageColor; + +/** + The custom overlay image for the cell of the current day. Default value is `nil`. + + @discussion Can be overridden in subclasses for customization. + */ +- (UIImage *)customOverlayImage; + +/** + The color of the divider image for the cell of the day. Default value is [UIColor colorWithRed:200/255.0f green:200/255.0f blue:200/255.0f alpha:1.0f]. + + @discussion Can be overridden in subclasses for customization. Ignored if `customDividerImage` is not equal to `nil`. + */ +- (UIColor *)dividerImageColor; + +/** + The custom divider image for the cell of the day. Default value is `nil`. + + @discussion Can be overridden in subclasses for customization. + */ +- (UIImage *)customDividerImage; + +@end diff --git a/RSDayFlow/RSDFDatePickerDayCell.m b/RSDayFlow/RSDFDatePickerDayCell.m new file mode 100644 index 0000000..a198887 --- /dev/null +++ b/RSDayFlow/RSDFDatePickerDayCell.m @@ -0,0 +1,453 @@ +// +// RSDFDatePickerDayCell.m +// +// Copyright (c) 2013 Evadne Wu, http://radi.ws/ +// Copyright (c) 2013-2015 Ruslan Skorb, http://ruslanskorb.com +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +#import "RSDFDatePickerDayCell.h" + +@interface RSDFDatePickerDayCell () + ++ (NSCache *)imageCache; ++ (id)fetchObjectForKey:(id)key withCreator:(id(^)(void))block; + +@property (nonatomic, readonly, strong) UIImageView *selectedDayImageView; +@property (nonatomic, readonly, strong) UIImageView *overlayImageView; +@property (nonatomic, readonly, strong) UIImageView *markImageView; +@property (nonatomic, readonly, strong) UIImageView *dividerImageView; + +@end + +@implementation RSDFDatePickerDayCell + +@synthesize dateLabel = _dateLabel; +@synthesize selectedDayImageView = _selectedDayImageView; +@synthesize overlayImageView = _overlayImageView; +@synthesize markImage = _markImage; +@synthesize markImageColor = _markImageColor; +@synthesize markImageView = _markImageView; +@synthesize dividerImageView = _dividerImageView; + +#pragma mark - Lifecycle + +- (instancetype)initWithFrame:(CGRect)frame +{ + self = [super initWithFrame:frame]; + if (self) { + [self commonInitializer]; + } + return self; +} + +- (instancetype)initWithCoder:(NSCoder *)aDecoder +{ + self = [super initWithCoder:aDecoder]; + if (self) { + [self commonInitializer]; + } + return self; +} + +- (void)commonInitializer +{ + self.backgroundColor = [self selfBackgroundColor]; + + [self addSubview:self.selectedDayImageView]; + [self addSubview:self.overlayImageView]; + [self addSubview:self.markImageView]; + [self addSubview:self.dividerImageView]; + [self addSubview:self.dateLabel]; + + [self updateSubviews]; +} + +- (void)layoutSubviews +{ + [super layoutSubviews]; + + self.dateLabel.frame = [self selectedImageViewFrame]; + self.selectedDayImageView.frame = [self selectedImageViewFrame]; + self.overlayImageView.frame = [self selectedImageViewFrame]; + self.markImageView.frame = [self markImageViewFrame]; + self.dividerImageView.frame = [self dividerImageViewFrame]; + self.dividerImageView.image = [self dividerImage]; +} + +- (void)drawRect:(CGRect)rect +{ + [self updateSubviews]; +} + +#pragma mark - Custom Accessors + +- (UILabel *)dateLabel +{ + if (!_dateLabel) { + _dateLabel = [[UILabel alloc] initWithFrame:[self selectedImageViewFrame]]; + _dateLabel.backgroundColor = [UIColor clearColor]; + _dateLabel.textAlignment = NSTextAlignmentCenter; + } + return _dateLabel; +} + +- (UIImageView *)dividerImageView +{ + if (!_dividerImageView) { + _dividerImageView = [[UIImageView alloc] initWithFrame:[self dividerImageViewFrame]]; + _dividerImageView.contentMode = UIViewContentModeCenter; + _dividerImageView.image = [self dividerImage]; + } + return _dividerImageView; +} + +- (CGRect)dividerImageViewFrame +{ + return CGRectMake(0.0f, 0.0f, CGRectGetWidth(self.frame) + 3.0f, 0.5f); +} + +- (CGRect)markImageViewFrame +{ + return CGRectMake(CGRectGetWidth(self.frame) / 2 - 4.5f, 45.5f, 9.0f, 9.0f); +} + +- (UIImage *)markImage +{ + if (!_markImage) { + NSString *markImageKey = [NSString stringWithFormat:@"img_mark_%@", [self.markImageColor description]]; + _markImage = [self ellipseImageWithKey:markImageKey frame:self.markImageView.frame color:self.markImageColor]; + } + return _markImage; +} + +- (UIColor *)markImageColor +{ + if (!_markImageColor) { + _markImageColor = [UIColor colorWithRed:184/255.0f green:184/255.0f blue:184/255.0f alpha:1.0f]; + } + return _markImageColor; +} + +- (UIImageView *)markImageView +{ + if (!_markImageView) { + _markImageView = [[UIImageView alloc] initWithFrame:[self markImageViewFrame]]; + _markImageView.backgroundColor = [UIColor clearColor]; + _markImageView.contentMode = UIViewContentModeCenter; + _markImageView.image = self.markImage; + } + return _markImageView; +} + +- (UIImageView *)overlayImageView +{ + if (!_overlayImageView) { + _overlayImageView = [[UIImageView alloc] initWithFrame:[self selectedImageViewFrame]]; + _overlayImageView.backgroundColor = [UIColor clearColor]; + _overlayImageView.opaque = NO; + _overlayImageView.alpha = 0.5f; + _overlayImageView.contentMode = UIViewContentModeCenter; + _overlayImageView.image = [self overlayImage]; + } + return _overlayImageView; +} + +- (UIImageView *)selectedDayImageView +{ + if (!_selectedDayImageView) { + _selectedDayImageView = [[UIImageView alloc] initWithFrame:[self selectedImageViewFrame]]; + _selectedDayImageView.backgroundColor = [UIColor clearColor]; + _selectedDayImageView.contentMode = UIViewContentModeCenter; + _selectedDayImageView.image = [self selectedDayImage]; + } + return _selectedDayImageView; +} + +- (CGRect)selectedImageViewFrame +{ + return CGRectMake(CGRectGetWidth(self.frame) / 2 - 17.5f, 5.5f, 35.0f, 35.0f); +} + +- (void)setMarkImage:(UIImage *)markImage +{ + if (![_markImage isEqual:markImage]) { + _markImage = markImage; + + [self setNeedsDisplay]; + } +} + +- (void)setMarkImageColor:(UIColor *)markImageColor +{ + if (![_markImageColor isEqual:markImageColor]) { + _markImageColor = markImageColor; + _markImage = nil; + + [self setNeedsDisplay]; + } +} + +#pragma mark - Private + +- (void)updateSubviews +{ + self.selectedDayImageView.hidden = !self.isSelected || self.isNotThisMonth; + self.overlayImageView.hidden = !self.isHighlighted || self.isNotThisMonth; + self.markImageView.hidden = !self.isMarked || self.isNotThisMonth; + self.dividerImageView.hidden = self.isNotThisMonth; + + if (self.isNotThisMonth) { + self.dateLabel.textColor = [self notThisMonthLabelTextColor]; + self.dateLabel.font = [self dayLabelFont]; + } else { + if (!self.isSelected) { + if (!self.isToday) { + self.dateLabel.font = [self dayLabelFont]; + if (!self.dayOff) { + self.dateLabel.textColor = [self dayLabelTextColor]; + } else { + self.dateLabel.textColor = [self dayOffLabelTextColor]; + } + } else { + self.dateLabel.font = [self todayLabelFont]; + self.dateLabel.textColor = [self todayLabelTextColor]; + } + } else { + if (!self.isToday) { + self.dateLabel.font = [self selectedDayLabelFont]; + self.dateLabel.textColor = [self selectedDayLabelTextColor]; + self.selectedDayImageView.image = [self selectedDayImage]; + } else { + self.dateLabel.font = [self selectedTodayLabelFont]; + self.dateLabel.textColor = [self selectedTodayLabelTextColor]; + self.selectedDayImageView.image = [self selectedTodayImage]; + } + } + + if (self.marked) { + self.markImageView.image = self.markImage; + } else { + self.markImageView.image = nil; + } + } +} + ++ (NSCache *)imageCache +{ + static NSCache *cache; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + cache = [NSCache new]; + }); + return cache; +} + ++ (id)fetchObjectForKey:(id)key withCreator:(id(^)(void))block +{ + id answer = [[self imageCache] objectForKey:key]; + if (!answer) { + answer = block(); + [[self imageCache] setObject:answer forKey:key]; + } + return answer; +} + +- (UIImage *)ellipseImageWithKey:(NSString *)key frame:(CGRect)frame color:(UIColor *)color +{ + UIImage *ellipseImage = [[self class] fetchObjectForKey:key withCreator:^id{ + UIGraphicsBeginImageContextWithOptions(frame.size, NO, self.window.screen.scale); + CGContextRef context = UIGraphicsGetCurrentContext(); + + CGRect rect = frame; + rect.origin = CGPointZero; + + CGContextSetFillColorWithColor(context, color.CGColor); + CGContextFillEllipseInRect(context, rect); + + UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); + UIGraphicsEndImageContext(); + + return image; + }]; + return ellipseImage; +} + +- (UIImage *)rectImageWithKey:(NSString *)key frame:(CGRect)frame color:(UIColor *)color +{ + UIImage *rectImage = [[self class] fetchObjectForKey:key withCreator:^id{ + UIGraphicsBeginImageContextWithOptions(frame.size, NO, self.window.screen.scale); + CGContextRef context = UIGraphicsGetCurrentContext(); + + CGContextSetFillColorWithColor(context, color.CGColor); + CGContextFillRect(context, frame); + + UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); + UIGraphicsEndImageContext(); + + return image; + }]; + return rectImage; +} + +#pragma mark - Atrributes of the View + +- (UIColor *)selfBackgroundColor +{ + return [UIColor clearColor]; +} + +#pragma mark - Attributes of Subviews + +- (UIFont *)dayLabelFont +{ + return [UIFont fontWithName:@"HelveticaNeue" size:18.0f]; +} + +- (UIColor *)dayLabelTextColor +{ + return [UIColor blackColor]; +} + +- (UIColor *)dayOffLabelTextColor +{ + return [UIColor colorWithRed:184/255.0f green:184/255.0f blue:184/255.0f alpha:1.0f]; +} + +- (UIColor *)notThisMonthLabelTextColor +{ + return [UIColor clearColor]; +} + +- (UIFont *)todayLabelFont +{ + return [UIFont fontWithName:@"HelveticaNeue" size:18.0f]; +} + +- (UIColor *)todayLabelTextColor +{ + return [UIColor colorWithRed:0/255.0f green:121/255.0f blue:255/255.0f alpha:1.0f]; +} + +- (UIFont *)selectedTodayLabelFont +{ + return [UIFont fontWithName:@"HelveticaNeue-Bold" size:19.0f]; +} + +- (UIColor *)selectedTodayLabelTextColor +{ + return [UIColor whiteColor]; +} + +- (UIColor *)selectedTodayImageColor +{ + return [UIColor colorWithRed:0/255.0f green:121/255.0f blue:255/255.0f alpha:1.0f]; +} + +- (UIImage *)customSelectedTodayImage +{ + return nil; +} + +- (UIImage *)selectedTodayImage +{ + UIImage *selectedTodayImage = [self customSelectedTodayImage]; + if (!selectedTodayImage) { + UIColor *selectedTodayImageColor = [self selectedTodayImageColor]; + NSString *selectedTodayImageKey = [NSString stringWithFormat:@"img_selected_today_%@", [selectedTodayImageColor description]]; + selectedTodayImage = [self ellipseImageWithKey:selectedTodayImageKey frame:self.selectedDayImageView.frame color:selectedTodayImageColor]; + } + return selectedTodayImage; +} + +- (UIFont *)selectedDayLabelFont +{ + return [UIFont fontWithName:@"HelveticaNeue-Bold" size:19.0f]; +} + +- (UIColor *)selectedDayLabelTextColor +{ + return [UIColor whiteColor]; +} + +- (UIColor *)selectedDayImageColor +{ + return [UIColor colorWithRed:255/255.0f green:59/255.0f blue:48/255.0f alpha:1.0f]; +} + +- (UIImage *)customSelectedDayImage +{ + return nil; +} + +- (UIImage *)selectedDayImage +{ + UIImage *selectedDayImage = [self customSelectedDayImage]; + if (!selectedDayImage) { + UIColor *selectedDayImageColor = [self selectedDayImageColor]; + NSString *selectedDayImageKey = [NSString stringWithFormat:@"img_selected_day_%@", [selectedDayImageColor description]]; + selectedDayImage = [self ellipseImageWithKey:selectedDayImageKey frame:self.selectedDayImageView.frame color:selectedDayImageColor]; + } + return selectedDayImage; +} + +- (UIColor *)overlayImageColor +{ + return [UIColor colorWithRed:184/255.0f green:184/255.0f blue:184/255.0f alpha:1.0f]; +} + +- (UIImage *)customOverlayImage +{ + return nil; +} + +- (UIImage *)overlayImage +{ + UIImage *overlayImage = [self customOverlayImage]; + if (!overlayImage) { + UIColor *overlayImageColor = [self overlayImageColor]; + NSString *overlayImageKey = [NSString stringWithFormat:@"img_overlay_%@", [overlayImageColor description]]; + overlayImage = [self ellipseImageWithKey:overlayImageKey frame:self.overlayImageView.frame color:overlayImageColor]; + } + return overlayImage; +} + +- (UIColor *)dividerImageColor +{ + return [UIColor colorWithRed:200/255.0f green:200/255.0f blue:200/255.0f alpha:1.0f]; +} + +- (UIImage *)customDividerImage +{ + return nil; +} + +- (UIImage *)dividerImage +{ + UIImage *dividerImage = [self customDividerImage]; + if (!dividerImage) { + UIColor *dividerImageColor = [self dividerImageColor]; + NSString *dividerImageKey = [NSString stringWithFormat:@"img_divider_%@_%g", [dividerImageColor description], CGRectGetWidth(self.dividerImageView.frame)]; + dividerImage = [self rectImageWithKey:dividerImageKey frame:self.dividerImageView.frame color:dividerImageColor]; + } + return dividerImage; +} + +@end diff --git a/RSDayFlow/RSDFDatePickerDaysOfWeekView.h b/RSDayFlow/RSDFDatePickerDaysOfWeekView.h new file mode 100644 index 0000000..f618a7f --- /dev/null +++ b/RSDayFlow/RSDFDatePickerDaysOfWeekView.h @@ -0,0 +1,98 @@ +// +// RSDFDatePickerDaysOfWeekView.h +// +// Copyright (c) 2013 Evadne Wu, http://radi.ws/ +// Copyright (c) 2013-2015 Ruslan Skorb, http://ruslanskorb.com +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +#import + +/** + The `RSDFDatePickerDaysOfWeekView` is a view with labels for each day of the week. + */ +@interface RSDFDatePickerDaysOfWeekView : UIView + +/** + Designated initializer. Initializes and returns a newly allocated view object with the specified frame rectangle and the specified calendar. + + @param frame The frame rectangle for the view, measured in points. + @param calendar The calendar for days of the week. + */ +- (instancetype)initWithFrame:(CGRect)frame calendar:(NSCalendar *)calendar; + +///--------------------------------------- +/// @name Accessing Attributes of the View +///--------------------------------------- + +/** + The view’s background color. Default value is `[UIColor colorWithRed:248.0/255 green:248.0/255 blue:248.0/255 alpha:1.0]`. + + @discussion Can be overridden in subclasses for customization. + */ +- (UIColor *)selfBackgroundColor; + +///----------------------------------------- +/// @name Accessing Attributes of the Layout +///----------------------------------------- + +/** + The size to use for labels of weekdays. Default size is calculated based on the size of the view. + + @discussion Can be overridden in subclasses for customization. + */ +- (CGSize)selfItemSize; + +/** + The spacing to use between labels. Default value is `2.0f`. + + @discussion Can be overridden in subclasses for customization. + */ +- (CGFloat)selfInteritemSpacing; + +///--------------------------------------- +/// @name Accessing Attributes of Subviews +///--------------------------------------- + +/** + The font for the label of the weekday. + Default value is [UIFont fontWithName:@"HelveticaNeue-Light" size:10.0] for user interface idiom 'Phone' with portrait interface orientation. + Default value is [UIFont fontWithName:@"HelveticaNeue-Light" size:12.0] for user interface idiom 'Phone' with landscape interface orientation. + Default value is [UIFont fontWithName:@"HelveticaNeue-Light" size:16.0] for user interface idiom 'Pad' with any interface orientation. + + @discussion Can be overridden in subclasses for customization. + */ +- (UIFont *)dayOfWeekLabelFont; + +/** + The text color for the label of the weekday. Default value is [UIColor blackColor]. + + @discussion Can be overridden in subclasses for customization. + */ +- (UIColor *)dayOfWeekLabelTextColor; + +/** + The text color for the label of the day off of the week. Default value is [UIColor colorWithRed:150.0/255 green:150.0/255 blue:150.0/255 alpha:1.0]. + + @discussion Can be overridden in subclasses for customization. + */ +- (UIColor *)dayOffOfWeekLabelTextColor; + +@end diff --git a/RSDayFlow/RSDFDatePickerDaysOfWeekView.m b/RSDayFlow/RSDFDatePickerDaysOfWeekView.m new file mode 100644 index 0000000..18052ba --- /dev/null +++ b/RSDayFlow/RSDFDatePickerDaysOfWeekView.m @@ -0,0 +1,296 @@ +// +// RSDFDatePickerDaysOfWeekView.m +// +// Copyright (c) 2013 Evadne Wu, http://radi.ws/ +// Copyright (c) 2013-2015 Ruslan Skorb, http://ruslanskorb.com +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +#import "RSDFDatePickerDaysOfWeekView.h" +#import "NSCalendar+RSDFAdditions.h" + +@interface RSDFDatePickerDaysOfWeekView () + +@property (strong, nonatomic) NSCalendar *calendar; +@property (strong, nonatomic) NSArray *weekdayLabels; +@property (strong, nonatomic) NSArray *veryShortStandaloneWeekdaySymbols; +@property (strong, nonatomic) NSArray *shortStandaloneWeekdaySymbols; +@property (strong, nonatomic) NSArray *standaloneWeekdaySymbols; + +@end + +@implementation RSDFDatePickerDaysOfWeekView + +#pragma mark - Lifecycle + +- (instancetype)initWithFrame:(CGRect)frame +{ + self = [super initWithFrame:frame]; + if (self) { + [self commonInitializer]; + } + return self; +} + +- (instancetype)initWithCoder:(NSCoder *)aDecoder +{ + self = [super initWithCoder:aDecoder]; + if (self) { + [self commonInitializer]; + } + return self; +} + +- (instancetype)initWithFrame:(CGRect)frame calendar:(NSCalendar *)calendar +{ + self = [super initWithFrame:frame]; + if (self) { + _calendar = calendar; + [self commonInitializer]; + } + return self; +} + +- (void)layoutSubviews +{ + [super layoutSubviews]; + + [self layoutWeekdayLabels]; + [self updateWeekdayLabels]; +} + +#pragma mark - Custom Accessors + +- (NSCalendar *)calendar +{ + if (!_calendar) { + _calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierGregorian]; + _calendar.locale = [NSLocale currentLocale]; + } + return _calendar; +} + +#pragma mark - Private + +- (void)commonInitializer +{ + self.backgroundColor = [self selfBackgroundColor]; + + UIColor *dayOfWeekLabelBackgroundColor = [UIColor clearColor]; + UIFont *dayOfWeekLabelFont = [self dayOfWeekLabelFont]; + UIColor *dayOfWeekLabelTextColor = [self dayOfWeekLabelTextColor]; + UIColor *dayOffOfWeekLabelTextColor = [self dayOffOfWeekLabelTextColor]; + + NSString *dateFormatterName = [NSString stringWithFormat:@"calendarDaysOfWeekView_%@_%@", [self.calendar calendarIdentifier], [[self.calendar locale] localeIdentifier]]; + NSDateFormatter *dateFormatter = [self.calendar df_dateFormatterNamed:dateFormatterName withConstructor:^{ + NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init]; + [dateFormatter setCalendar:self.calendar]; + [dateFormatter setLocale:[self.calendar locale]]; + return dateFormatter; + }]; + + BOOL isPhone = [self isPhone]; + BOOL isPortraitInterfaceOrientation = [self isPortraitInterfaceOrientation]; + + NSArray *weekdaySymbols = nil; + if (isPhone) { + self.veryShortStandaloneWeekdaySymbols = [dateFormatter veryShortStandaloneWeekdaySymbols]; + self.shortStandaloneWeekdaySymbols = [dateFormatter shortStandaloneWeekdaySymbols]; + + if (isPortraitInterfaceOrientation) { + weekdaySymbols = self.veryShortStandaloneWeekdaySymbols; + } else { + weekdaySymbols = self.shortStandaloneWeekdaySymbols; + } + } else { + self.shortStandaloneWeekdaySymbols = [dateFormatter shortStandaloneWeekdaySymbols]; + self.standaloneWeekdaySymbols = [dateFormatter standaloneWeekdaySymbols]; + + if (isPortraitInterfaceOrientation) { + weekdaySymbols = self.shortStandaloneWeekdaySymbols; + } else { + weekdaySymbols = self.standaloneWeekdaySymbols; + } + } + + NSArray *reorderedWeekdaySymbols = nil; + + // weekday start from 1 + NSUInteger firstWeekdayIndex = [self.calendar firstWeekday] - 1; + if (firstWeekdayIndex > 0) { + if (isPhone) { + self.veryShortStandaloneWeekdaySymbols = [self reorderedWeekdaySymbols:self.veryShortStandaloneWeekdaySymbols firstWeekdayIndex:firstWeekdayIndex]; + self.shortStandaloneWeekdaySymbols = [self reorderedWeekdaySymbols:self.shortStandaloneWeekdaySymbols firstWeekdayIndex:firstWeekdayIndex]; + + if (isPortraitInterfaceOrientation) { + reorderedWeekdaySymbols = self.veryShortStandaloneWeekdaySymbols; + } else { + reorderedWeekdaySymbols = self.shortStandaloneWeekdaySymbols; + } + } else { + self.shortStandaloneWeekdaySymbols = [self reorderedWeekdaySymbols:self.shortStandaloneWeekdaySymbols firstWeekdayIndex:firstWeekdayIndex]; + self.standaloneWeekdaySymbols = [self reorderedWeekdaySymbols:self.standaloneWeekdaySymbols firstWeekdayIndex:firstWeekdayIndex]; + + if (isPortraitInterfaceOrientation) { + reorderedWeekdaySymbols = self.shortStandaloneWeekdaySymbols; + } else { + reorderedWeekdaySymbols = self.standaloneWeekdaySymbols; + } + } + } else { + reorderedWeekdaySymbols = weekdaySymbols; + } + + NSMutableArray *weekdayLabels = [NSMutableArray arrayWithCapacity:[reorderedWeekdaySymbols count]]; + [reorderedWeekdaySymbols enumerateObjectsUsingBlock:^(NSString *weekdaySymbol, NSUInteger idx, BOOL *stop) { + UILabel *weekdayLabel = [[UILabel alloc] init]; + weekdayLabel.textAlignment = NSTextAlignmentCenter; + weekdayLabel.backgroundColor = dayOfWeekLabelBackgroundColor; + weekdayLabel.font = dayOfWeekLabelFont; + if ([weekdaySymbols indexOfObjectIdenticalTo:weekdaySymbol] != 0 && [weekdaySymbols indexOfObjectIdenticalTo:weekdaySymbol] != 6) { + weekdayLabel.textColor = dayOfWeekLabelTextColor; + } else { + weekdayLabel.textColor = dayOffOfWeekLabelTextColor; + } + weekdayLabel.text = weekdaySymbol; + [weekdayLabels addObject:weekdayLabel]; + [self addSubview:weekdayLabel]; + }]; + + self.weekdayLabels = [weekdayLabels copy]; +} + +- (NSArray *)reorderedWeekdaySymbols:(NSArray *)weekdaySymbols firstWeekdayIndex:(NSUInteger)firstWeekdayIndex +{ + return [[weekdaySymbols subarrayWithRange:NSMakeRange(firstWeekdayIndex, [weekdaySymbols count] - firstWeekdayIndex)] + arrayByAddingObjectsFromArray:[weekdaySymbols subarrayWithRange:NSMakeRange(0, firstWeekdayIndex)]]; +} + +- (void)layoutWeekdayLabels +{ + CGSize itemSize = [self selfItemSize]; + CGFloat interitemSpacing = [self selfInteritemSpacing]; + + CGFloat y = 0; + __block CGFloat x; + + NSLocaleLanguageDirection characterDirection = [NSLocale characterDirectionForLanguage:self.calendar.locale.localeIdentifier]; + if (characterDirection == NSLocaleLanguageDirectionRightToLeft) { + x = CGRectGetWidth(self.frame) - itemSize.width; + [self.weekdayLabels enumerateObjectsUsingBlock:^(UILabel *weekdayLabel, NSUInteger idx, BOOL *stop) { + CGRect weekdayLabelFrame = CGRectMake(x, y, itemSize.width, itemSize.height); + weekdayLabel.frame = weekdayLabelFrame; + x -= (itemSize.width + interitemSpacing); + }]; + } else { + x = 0; + [self.weekdayLabels enumerateObjectsUsingBlock:^(UILabel *weekdayLabel, NSUInteger idx, BOOL *stop) { + CGRect weekdayLabelFrame = CGRectMake(x, y, itemSize.width, itemSize.height); + weekdayLabel.frame = weekdayLabelFrame; + x += (itemSize.width + interitemSpacing); + }]; + } +} + +- (void)updateWeekdayLabels +{ + BOOL isPhone = [self isPhone]; + BOOL isPortraitInterfaceOrientation = [self isPortraitInterfaceOrientation]; + + [self.weekdayLabels enumerateObjectsUsingBlock:^(UILabel *weekdayLabel, NSUInteger idx, BOOL *stop) { + weekdayLabel.font = [self dayOfWeekLabelFont]; + if (isPhone) { + if (isPortraitInterfaceOrientation) { + weekdayLabel.text = self.veryShortStandaloneWeekdaySymbols[idx]; + } else { + weekdayLabel.text = self.shortStandaloneWeekdaySymbols[idx]; + } + } else { + if (isPortraitInterfaceOrientation) { + weekdayLabel.text = self.shortStandaloneWeekdaySymbols[idx]; + } else { + weekdayLabel.text = self.standaloneWeekdaySymbols[idx]; + } + } + }]; +} + +- (BOOL)isPhone +{ + return UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone; +} + +- (BOOL)isPortraitInterfaceOrientation +{ + return UIInterfaceOrientationIsPortrait([[UIApplication sharedApplication] statusBarOrientation]); +} + +#pragma mark - Attributes of the View + +- (UIColor *)selfBackgroundColor +{ + return [UIColor colorWithRed:248.0/255 green:248.0/255 blue:248.0/255 alpha:1.0]; +} + +#pragma mark - Attributes of the Layout + +- (CGSize)selfItemSize +{ + NSUInteger numberOfItems = 7; + CGFloat totalInteritemSpacing = [self selfInteritemSpacing] * (numberOfItems - 1); + + CGFloat selfItemWidth = (CGRectGetWidth(self.frame) - totalInteritemSpacing) / numberOfItems; + selfItemWidth = floor(selfItemWidth * 1000) / 1000; + CGFloat selfItemHeight = CGRectGetHeight(self.frame); + + return (CGSize){ selfItemWidth, selfItemHeight }; +} + +- (CGFloat)selfInteritemSpacing +{ + return 2.0f; +} + +#pragma mark - Attributes of Subviews + +- (UIFont *)dayOfWeekLabelFont +{ + if ([self isPhone]) { + if ([self isPortraitInterfaceOrientation]) { + return [UIFont fontWithName:@"HelveticaNeue-Light" size:10.0]; + } else { + return [UIFont fontWithName:@"HelveticaNeue-Light" size:12.0]; + } + } else { + return [UIFont fontWithName:@"HelveticaNeue-Light" size:16.0]; + } +} + +- (UIColor *)dayOfWeekLabelTextColor +{ + return [UIColor blackColor]; +} + +- (UIColor *)dayOffOfWeekLabelTextColor +{ + return [UIColor colorWithRed:150.0/255 green:150.0/255 blue:150.0/255 alpha:1.0]; +} + +@end diff --git a/RSDayFlow/RSDFDatePickerMonthHeader.h b/RSDayFlow/RSDFDatePickerMonthHeader.h new file mode 100644 index 0000000..eb5bbdf --- /dev/null +++ b/RSDayFlow/RSDFDatePickerMonthHeader.h @@ -0,0 +1,93 @@ +// +// RSDFDatePickerMonthHeader.h +// +// Copyright (c) 2013 Evadne Wu, http://radi.ws/ +// Copyright (c) 2013-2015 Ruslan Skorb, http://ruslanskorb.com +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +#import +#import "RSDayFlow.h" + +/** + The `RSDFDatePickerMonthHeader` is a reusable view which used to display a month and year in the date picker view. + */ +@interface RSDFDatePickerMonthHeader : UICollectionReusableView + +///------------------------- +/// @name Accessing Subviews +///------------------------- + +/** + The label showing the view's date. + */ +@property (nonatomic, readonly, strong) UILabel *dateLabel; + +///---------------------------------------- +/// @name Accessing Attributes of the Month +///---------------------------------------- + +/** + A date which corresponds to the current view. + */ +@property (nonatomic, readwrite, assign) RSDFDatePickerDate date; + +/** + A Boolean value that determines whether the view represents the current month. + */ +@property (nonatomic, getter = isCurrentMonth) BOOL currentMonth; + +///--------------------------------------- +/// @name Accessing Attributes of the View +///--------------------------------------- + +/** + The view’s background color. Default value is `[UIColor clearColor]`. + + @discussion Can be overridden in subclasses for customization. + */ +- (UIColor *)selfBackgroundColor; + +///--------------------------------------- +/// @name Accessing Attributes of Subviews +///--------------------------------------- + +/** + The font for the label of the month. Default value is [UIFont fontWithName:@"HelveticaNeue" size:16.0f]. + + @discussion Can be overridden in subclasses for customization. + */ +- (UIFont *)monthLabelFont; + +/** + The text color for the label of the month. Default value is [UIColor blackColor]. + + @discussion Can be overridden in subclasses for customization. + */ +- (UIColor *)monthLabelTextColor; + +/** + The text color for the label of the current month. Default value is [UIColor colorWithRed:32/255.0f green:135/255.0f blue:252/255.0f alpha:1.0f]. + + @discussion Can be overridden in subclasses for customization. + */ +- (UIColor *)currentMonthLabelTextColor; + +@end diff --git a/RSDayFlow/RSDFDatePickerMonthHeader.m b/RSDayFlow/RSDFDatePickerMonthHeader.m new file mode 100644 index 0000000..bc2ff6c --- /dev/null +++ b/RSDayFlow/RSDFDatePickerMonthHeader.m @@ -0,0 +1,112 @@ +// +// RSDFDatePickerMonthHeader.m +// +// Copyright (c) 2013 Evadne Wu, http://radi.ws/ +// Copyright (c) 2013-2015 Ruslan Skorb, http://ruslanskorb.com +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +#import "RSDFDatePickerMonthHeader.h" + +@implementation RSDFDatePickerMonthHeader + +@synthesize dateLabel = _dateLabel; + +#pragma mark - Lifecycle + +- (instancetype)initWithFrame:(CGRect)frame +{ + self = [super initWithFrame:frame]; + if (self) { + [self commonInitializer]; + } + return self; +} + +- (instancetype)initWithCoder:(NSCoder *)aDecoder +{ + self = [super initWithCoder:aDecoder]; + if (self) { + [self commonInitializer]; + } + return self; +} + +- (void)commonInitializer +{ + self.backgroundColor = [self selfBackgroundColor]; +} + +#pragma mark - Custom Accessors + +- (UILabel *)dateLabel +{ + if (!_dateLabel) { + _dateLabel = [[UILabel alloc] initWithFrame:self.bounds]; + _dateLabel.backgroundColor = [UIColor clearColor]; + _dateLabel.opaque = NO; + _dateLabel.textAlignment = NSTextAlignmentCenter; + _dateLabel.font = [self monthLabelFont]; + _dateLabel.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; + [self addSubview:_dateLabel]; + } + return _dateLabel; +} + +- (void)setDate:(RSDFDatePickerDate)date +{ + _date = date; +} + +- (void)setCurrentMonth:(BOOL)currentMonth +{ + _currentMonth = currentMonth; + if (!_currentMonth) { + self.dateLabel.textColor = [self monthLabelTextColor]; + } else { + self.dateLabel.textColor = [self currentMonthLabelTextColor]; + } +} + +#pragma mark - Attributes of the View + +- (UIColor *)selfBackgroundColor +{ + return [UIColor clearColor]; +} + +#pragma mark - Attributes of Subviews + +- (UIFont *)monthLabelFont +{ + return [UIFont fontWithName:@"HelveticaNeue" size:16.0f]; +} + +- (UIColor *)monthLabelTextColor +{ + return [UIColor blackColor]; +} + +- (UIColor *)currentMonthLabelTextColor +{ + return [UIColor colorWithRed:32/255.0f green:135/255.0f blue:252/255.0f alpha:1.0f]; +} + +@end diff --git a/RSDayFlow/RSDFDatePickerView.h b/RSDayFlow/RSDFDatePickerView.h new file mode 100644 index 0000000..f802da2 --- /dev/null +++ b/RSDayFlow/RSDFDatePickerView.h @@ -0,0 +1,258 @@ +// +// RSDFDatePickerView.h +// +// Copyright (c) 2013 Evadne Wu, http://radi.ws/ +// Copyright (c) 2013-2015 Ruslan Skorb, http://ruslanskorb.com +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +#import + +@protocol RSDFDatePickerViewDelegate; +@protocol RSDFDatePickerViewDataSource; + +/** + The `RSDFDatePickerView` is a calendar view with infinity scrolling. +*/ +@interface RSDFDatePickerView : UIView + +/** + Designated initializer. Initializes and returns a newly allocated view object with the specified frame rectangle and the specified calendar. + + @param frame The frame rectangle for the view, measured in points. + @param calendar The calendar for the date picker view. + */ +- (instancetype)initWithFrame:(CGRect)frame calendar:(NSCalendar *)calendar; + +///----------------------------- +/// @name Accessing the Delegate +///----------------------------- + +/** + The receiver's delegate. + + @discussion A `RSDFDatePickerView` delegate responds to message sent by tapping on date in the date picker view. + */ +@property (nonatomic, readwrite, weak) id delegate; + +///-------------------------------- +/// @name Accessing the Data Source +///-------------------------------- + +/** + The receiver's data source. + + @discussion A `RSDFDatePickerView` data source provides dates to mark in the date picker view. + */ + +@property (nonatomic, readwrite, weak) id dataSource; + +///------------------ +/// @name Paging Mode +/// ----------------- + +/** + A Boolean value that determines whether paging is enabled for the date picker view. Default value is `NO`. + + @discussion If `YES`, stop on the top of the month. + */ +@property (nonatomic, getter = isPagingEnabled) BOOL pagingEnabled; + +///---------------------------- +/// @name Scrolling to the Date +///---------------------------- + +/** + Scrolls the date picker view to the current day. + + @param animated YES if you want to animate the change in position, NO if it should be immediate. + */ + +- (void)scrollToToday:(BOOL)animated; + +/** + Scrolls the date picker view to the given date. + + @param animated YES if you want to animate the change in position, NO if it should be immediate. + */ + +- (void)scrollToDate:(NSDate *)date animated:(BOOL)animated; + +/// ------------------------ +/// @name Selecting the Date +/// ------------------------ + +/** + Selects the specified date. + + If there is an existing selection of a different date, calling this method replaces the previous selection. + + This method does not cause any selection-related delegate methods to be called. + + @param date The date to select. Specifying nil for this parameter clears the current selection. + */ + +- (void)selectDate:(NSDate *)date; + +///------------------------- +/// @name Reloading the Data +///------------------------- + +/** + Reloads all of the data for the date picker view. + + @discussion Discard the dataSource and delegate data and requery as necessary. + */ +- (void)reloadData; + +///------------------------------------ +/// @name Accessing Classes of Subviews +///------------------------------------ + +/** + The class of the view with labels for each day of the week. Default value is `RSDFDatePickerDaysOfWeekView`. + + @discussion Can be overridden in subclasses for customization. + */ +- (Class)daysOfWeekViewClass; + +/** + The class of the collection view which used to display days and months in the date picker view. Default value is `RSDFDatePickerCollectionView`. + + @discussion Can be overridden in subclasses for customization. + */ +- (Class)collectionViewClass; + +/** + The class of the layout of the collection view which used the date picker. Default value is `RSDFDatePickerCollectionViewLayout`. + + @discussion Can be overridden in subclasses for customization. + */ +- (Class)collectionViewLayoutClass; + +/** + The class of the reusable view which used to display a month and year in the date picker view. Default value is `RSDFDatePickerMonthHeader`. + + @discussion Can be overridden in subclasses for customization. + */ +- (Class)monthHeaderClass; + +/** + The class of the cell which used to display a day in the date picker view. Default value is `RSDFDatePickerDayCell`. + + @discussion Can be overridden in subclasses for customization. + */ +- (Class)dayCellClass; + +@end + +/** + The `RSDFDatePickerViewDelegate` protocol defines the message sent to a date picker view delegate when date is tapped. + */ +@protocol RSDFDatePickerViewDelegate + +///----------------------------------- +/// @name Responding to Date Selection +///----------------------------------- + +@optional + +/** + Asks the delegate if the date should be highlighted during tracking. + + As touch events arrive, the date picker view highlights dates in anticipation of the user selecting them. + As it processes those touch events, the date picker view calls this method to ask your delegate if a given cell should be highlighted. + + If you do not implement this method, the default return value is YES. + + @param view The date picker view object that is asking about the highlight change. + + @return YES if the date should be highlighted or NO if it should not. + */ +- (BOOL)datePickerView:(RSDFDatePickerView *)view shouldHighlightDate:(NSDate *)date; + +/** + Asks the delegate if the specified date should be selected. + + The date picker view calls this method when the user tries to select a date in the date picker view. + It does not call this method when you programmatically set the selection. + + If you do not implement this method, the default return value is YES. + + @param view The date picker view object that is asking whether the date should select. + + @return YES if the date should be selected or NO if it should not. + */ +- (BOOL)datePickerView:(RSDFDatePickerView *)view shouldSelectDate:(NSDate *)date; + +/** + Tells the delegate that the user did select a date. + + The date picker view calls this method when the user successfully selects a date in the date picker view. + It does not call this method when you programmatically set the selection. + + @param view The view whose date was selected. + @param date The selected date. + */ +- (void)datePickerView:(RSDFDatePickerView *)view didSelectDate:(NSDate *)date; + +@end + +/** + The `RSDFDatePickerViewDataSource` protocol is adopted by an object that provides dates to mark in the date picker view. + */ +@protocol RSDFDatePickerViewDataSource + +///------------------------------ +/// @name Providing Dates to Mark +///------------------------------ + +@optional + +/** + Asks the data source if the date should be marked. + + @param view The date picker view object that is asking whether the date should mark. + + @return YES if the date should be marked or NO if it should not. + */ +- (BOOL)datePickerView:(RSDFDatePickerView *)view shouldMarkDate:(NSDate *)date; + +/** + Asks the data source about the color of the default mark image for the specified date. + + @param view The date picker view object that is asking about the color of the default mark image for the specified date. + + @return The color of the default mark image for the specified date. + + @discussion Will be ignored if the method `datePickerView:markImageForDate:` is implemented. + */ +- (UIColor *)datePickerView:(RSDFDatePickerView *)view markImageColorForDate:(NSDate *)date; + +/** + Asks the data source about the mark image for the specified date. + + @param view The date picker view object that is asking about the mark image for the specified date. + + @return The mark image for the specified date. + */ +- (UIImage *)datePickerView:(RSDFDatePickerView *)view markImageForDate:(NSDate *)date; + +@end diff --git a/RSDayFlow/RSDFDatePickerView.m b/RSDayFlow/RSDFDatePickerView.m new file mode 100755 index 0000000..8644b10 --- /dev/null +++ b/RSDayFlow/RSDFDatePickerView.m @@ -0,0 +1,837 @@ +// +// RSDFDatePickerView.m +// +// Copyright (c) 2013 Evadne Wu, http://radi.ws/ +// Copyright (c) 2013-2015 Ruslan Skorb, http://ruslanskorb.com +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +#import +#import "RSDayFlow.h" +#import "RSDFDatePickerCollectionView.h" +#import "RSDFDatePickerCollectionViewLayout.h" +#import "RSDFDatePickerDayCell.h" +#import "RSDFDatePickerMonthHeader.h" +#import "RSDFDatePickerView.h" +#import "RSDFDatePickerDaysOfWeekView.h" +#import "NSCalendar+RSDFAdditions.h" + +static NSString * const RSDFDatePickerViewMonthHeaderIdentifier = @"RSDFDatePickerViewMonthHeaderIdentifier"; +static NSString * const RSDFDatePickerViewDayCellIdentifier = @"RSDFDatePickerViewDayCellIdentifier"; + +@interface RSDFDatePickerView () + +@property (nonatomic, readonly, strong) NSCalendar *calendar; +@property (nonatomic, readonly, assign) RSDFDatePickerDate fromDate; +@property (nonatomic, readonly, assign) RSDFDatePickerDate toDate; +@property (nonatomic, readonly, strong) RSDFDatePickerDaysOfWeekView *daysOfWeekView; +@property (nonatomic, readonly, strong) RSDFDatePickerCollectionView *collectionView; +@property (nonatomic, readonly, strong) RSDFDatePickerCollectionViewLayout *collectionViewLayout; +@property (nonatomic, readonly, strong) NSDate *today; +@property (nonatomic, readonly, assign) NSUInteger daysInWeek; +@property (nonatomic, readonly, strong) NSDate *selectedDate; + +@end + +@implementation RSDFDatePickerView + +@synthesize calendar = _calendar; +@synthesize fromDate = _fromDate; +@synthesize toDate = _toDate; +@synthesize daysOfWeekView = _daysOfWeekView; +@synthesize collectionView = _collectionView; +@synthesize collectionViewLayout = _collectionViewLayout; +@synthesize daysInWeek = _daysInWeek; + +#pragma mark - Lifecycle + +- (void)dealloc +{ + [[NSNotificationCenter defaultCenter] removeObserver:self]; +} + +- (instancetype)initWithCoder:(NSCoder *)aDecoder +{ + self = [super initWithCoder:aDecoder]; + if (self) { + [self commonInitializer]; + } + return self; +} + +- (instancetype)initWithFrame:(CGRect)frame +{ + self = [super initWithFrame:frame]; + if (self) { + [self commonInitializer]; + } + return self; +} + +- (instancetype)initWithFrame:(CGRect)frame calendar:(NSCalendar *)calendar +{ + self = [super initWithFrame:frame]; + if (self) { + _calendar = calendar; + [self commonInitializer]; + } + return self; +} + +- (void)layoutSubviews +{ + CGPoint beforeLayoutSubviewsContentOffset = self.collectionView.contentOffset; + + [super layoutSubviews]; + + self.daysOfWeekView.frame = [self daysOfWeekViewFrame]; + if (!self.daysOfWeekView.superview) { + [self addSubview:self.daysOfWeekView]; + } + + self.collectionView.frame = [self collectionViewFrame]; + if (!self.collectionView.superview) { + [self addSubview:self.collectionView]; + [self scrollToToday:NO]; + } else { + [self.collectionViewLayout invalidateLayout]; + [self.collectionViewLayout prepareLayout]; + self.collectionView.contentOffset = beforeLayoutSubviewsContentOffset; + } +} + +#pragma mark - Custom Accessors + +- (NSCalendar *)calendar +{ + if (!_calendar) { + _calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierGregorian]; + _calendar.locale = [NSLocale currentLocale]; + } + return _calendar; +} + +- (CGRect)daysOfWeekViewFrame +{ + BOOL isPhone = UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone; + BOOL isPortraitInterfaceOrientation = UIInterfaceOrientationIsPortrait([[UIApplication sharedApplication] statusBarOrientation]); + + CGRect namesOfDaysViewFrame = self.bounds; + if (isPhone) { + if (isPortraitInterfaceOrientation) { + namesOfDaysViewFrame.size.height = 22.0f; + } else { + namesOfDaysViewFrame.size.height = 26.0f; + } + } else { + namesOfDaysViewFrame.size.height = 36.0f; + } + + return namesOfDaysViewFrame; +} + +- (Class)daysOfWeekViewClass +{ + return [RSDFDatePickerDaysOfWeekView class]; +} + +- (RSDFDatePickerDaysOfWeekView *)daysOfWeekView +{ + if (!_daysOfWeekView) { + _daysOfWeekView = [[[self daysOfWeekViewClass] alloc] initWithFrame:[self daysOfWeekViewFrame] calendar:self.calendar]; + [_daysOfWeekView layoutIfNeeded]; + } + return _daysOfWeekView; +} + +- (Class)collectionViewClass +{ + return [RSDFDatePickerCollectionView class]; +} + +- (CGRect)collectionViewFrame +{ + CGFloat daysOfWeekViewHeight = CGRectGetHeight([self daysOfWeekViewFrame]); + + CGRect collectionViewFrame = self.bounds; + collectionViewFrame.origin.y += daysOfWeekViewHeight; + collectionViewFrame.size.height -= daysOfWeekViewHeight; + return collectionViewFrame; +} + +- (RSDFDatePickerCollectionView *)collectionView +{ + if (!_collectionView) { + _collectionView = [[[self collectionViewClass] alloc] initWithFrame:[self collectionViewFrame] collectionViewLayout:self.collectionViewLayout]; + _collectionView.dataSource = self; + _collectionView.delegate = self; + [_collectionView registerClass:[self monthHeaderClass] forSupplementaryViewOfKind:UICollectionElementKindSectionHeader withReuseIdentifier:RSDFDatePickerViewMonthHeaderIdentifier]; + [_collectionView registerClass:[self dayCellClass] forCellWithReuseIdentifier:RSDFDatePickerViewDayCellIdentifier]; + [_collectionView reloadData]; + [_collectionView layoutIfNeeded]; + } + return _collectionView; +} + +- (Class)collectionViewLayoutClass +{ + return [RSDFDatePickerCollectionViewLayout class]; +} + +- (RSDFDatePickerCollectionViewLayout *)collectionViewLayout +{ + if (!_collectionViewLayout) { + NSLocaleLanguageDirection characterDirection = [NSLocale characterDirectionForLanguage:self.calendar.locale.localeIdentifier]; + RSDFDatePickerCollectionViewLayoutDirection layoutDirection; + if (characterDirection == NSLocaleLanguageDirectionRightToLeft) { + layoutDirection = RSDFDatePickerCollectionViewLayoutDirectionRightToLeft; + } else { + layoutDirection = RSDFDatePickerCollectionViewLayoutDirectionLeftToRight; + } + _collectionViewLayout = [[[self collectionViewLayoutClass] alloc] initWithDirection:layoutDirection]; + } + return _collectionViewLayout; +} + +- (Class)monthHeaderClass +{ + return [RSDFDatePickerMonthHeader class]; +} + +- (Class)dayCellClass +{ + return [RSDFDatePickerDayCell class]; +} + +- (NSUInteger)daysInWeek +{ + if (_daysInWeek == 0) { + _daysInWeek = [self.calendar maximumRangeOfUnit:NSCalendarUnitWeekday].length; + } + return _daysInWeek; +} + +#pragma mark - Handling Notifications + +- (void)significantTimeChange:(NSNotification *)notification +{ + NSDateComponents *todayYearMonthDayComponents = [self.calendar components:(NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitDay) fromDate:[NSDate date]]; + _today = [self.calendar dateFromComponents:todayYearMonthDayComponents]; + + [self.collectionView reloadData]; + [self restoreSelection]; +} + +#pragma mark - Public + +- (void)scrollToToday:(BOOL)animated +{ + [self scrollToDate:self.today animated:animated]; +} + +- (void)scrollToDate:(NSDate *)date animated:(BOOL)animated +{ + RSDFDatePickerCollectionView *cv = self.collectionView; + RSDFDatePickerCollectionViewLayout *cvLayout = (RSDFDatePickerCollectionViewLayout *)self.collectionView.collectionViewLayout; + + NSDateComponents *dateYearMonthComponents = [self.calendar components:(NSCalendarUnitYear | NSCalendarUnitMonth) fromDate:date]; + NSDate *month = [self.calendar dateFromComponents:dateYearMonthComponents]; + + _fromDate = [self pickerDateFromDate:[self.calendar dateByAddingComponents:((^{ + NSDateComponents *components = [NSDateComponents new]; + components.month = -6; + return components; + })()) toDate:month options:0]]; + + _toDate = [self pickerDateFromDate:[self.calendar dateByAddingComponents:((^{ + NSDateComponents *components = [NSDateComponents new]; + components.month = 6; + return components; + })()) toDate:month options:0]]; + + [cv reloadData]; + [cvLayout invalidateLayout]; + [cvLayout prepareLayout]; + + [self restoreSelection]; + + NSIndexPath *dateItemIndexPath = [self indexPathForDate:date]; + NSInteger monthSection = [self sectionForDate:date]; + + CGRect dateItemRect = [self frameForItemAtIndexPath:dateItemIndexPath]; + CGRect monthSectionHeaderRect = [self frameForHeaderForSection:monthSection]; + + CGFloat delta = CGRectGetMaxY(dateItemRect) - CGRectGetMinY(monthSectionHeaderRect); + CGFloat actualViewHeight = CGRectGetHeight(cv.frame) - cv.contentInset.top - cv.contentInset.bottom; + + if (delta <= actualViewHeight) { + [self scrollToTopOfSection:monthSection animated:animated]; + } else { + [cv scrollToItemAtIndexPath:dateItemIndexPath atScrollPosition:UICollectionViewScrollPositionBottom animated:animated]; + } +} + +- (void)selectDate:(NSDate *)date +{ + if (![self.selectedDate isEqual:date]) { + if (self.selectedDate && + [self.selectedDate compare:[self dateFromPickerDate:self.fromDate]] != NSOrderedAscending && + [self.selectedDate compare:[self dateFromPickerDate:self.toDate]] != NSOrderedDescending) { + NSIndexPath *previousSelectedCellIndexPath = [self indexPathForDate:self.selectedDate]; + [self.collectionView deselectItemAtIndexPath:previousSelectedCellIndexPath animated:NO]; + UICollectionViewCell *previousSelectedCell = [self.collectionView cellForItemAtIndexPath:previousSelectedCellIndexPath]; + if (previousSelectedCell) { + [previousSelectedCell setNeedsDisplay]; + } + } + + _selectedDate = date; + + if (self.selectedDate && + [self.selectedDate compare:[self dateFromPickerDate:self.fromDate]] != NSOrderedAscending && + [self.selectedDate compare:[self dateFromPickerDate:self.toDate]] != NSOrderedDescending) { + NSIndexPath *indexPathForSelectedDate = [self indexPathForDate:self.selectedDate]; + [self.collectionView selectItemAtIndexPath:indexPathForSelectedDate animated:NO scrollPosition:UICollectionViewScrollPositionNone]; + UICollectionViewCell *selectedCell = [self.collectionView cellForItemAtIndexPath:indexPathForSelectedDate]; + if (selectedCell) { + [selectedCell setNeedsDisplay]; + } + } + } +} + +- (void)reloadData +{ + [self.collectionView reloadData]; +} + +#pragma mark - Private + +- (void)commonInitializer +{ + NSDateComponents *nowYearMonthComponents = [self.calendar components:(NSCalendarUnitYear | NSCalendarUnitMonth) fromDate:[NSDate date]]; + NSDate *now = [self.calendar dateFromComponents:nowYearMonthComponents]; + + _fromDate = [self pickerDateFromDate:[self.calendar dateByAddingComponents:((^{ + NSDateComponents *components = [NSDateComponents new]; + components.month = -6; + return components; + })()) toDate:now options:0]]; + + _toDate = [self pickerDateFromDate:[self.calendar dateByAddingComponents:((^{ + NSDateComponents *components = [NSDateComponents new]; + components.month = 6; + return components; + })()) toDate:now options:0]]; + + NSDateComponents *todayYearMonthDayComponents = [self.calendar components:(NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitDay) fromDate:[NSDate date]]; + _today = [self.calendar dateFromComponents:todayYearMonthDayComponents]; + + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(significantTimeChange:) + name:UIApplicationSignificantTimeChangeNotification + object:nil]; +} + +- (void)appendPastDates +{ + [self shiftDatesByComponents:((^{ + NSDateComponents *dateComponents = [NSDateComponents new]; + dateComponents.month = -6; + return dateComponents; + })())]; +} + +- (void)appendFutureDates +{ + [self shiftDatesByComponents:((^{ + NSDateComponents *dateComponents = [NSDateComponents new]; + dateComponents.month = 6; + return dateComponents; + })())]; +} + +- (void)shiftDatesByComponents:(NSDateComponents *)components +{ + RSDFDatePickerCollectionView *cv = self.collectionView; + RSDFDatePickerCollectionViewLayout *cvLayout = (RSDFDatePickerCollectionViewLayout *)self.collectionView.collectionViewLayout; + + NSArray *visibleCells = [cv visibleCells]; + if (![visibleCells count]) + return; + + NSIndexPath *fromIndexPath = [cv indexPathForCell:((UICollectionViewCell *)visibleCells[0]) ]; + NSInteger fromSection = fromIndexPath.section; + NSDate *fromSectionOfDate = [self dateForFirstDayInSection:fromSection]; + UICollectionViewLayoutAttributes *fromAttrs = [cvLayout layoutAttributesForItemAtIndexPath:[NSIndexPath indexPathForItem:0 inSection:fromSection]]; + CGPoint fromSectionOrigin = [self convertPoint:fromAttrs.frame.origin fromView:cv]; + + _fromDate = [self pickerDateFromDate:[self.calendar dateByAddingComponents:components toDate:[self dateFromPickerDate:self.fromDate] options:0]]; + _toDate = [self pickerDateFromDate:[self.calendar dateByAddingComponents:components toDate:[self dateFromPickerDate:self.toDate] options:0]]; + +#if 0 + + // This solution trips up the collection view a bit + // because our reload is reactionary, and happens before a relayout + // since we must do it to avoid flickering and to heckle the CA transaction (?) + // that could be a small red flag too + + [cv performBatchUpdates:^{ + + if (components.month < 0) { + + [cv deleteSections:[NSIndexSet indexSetWithIndexesInRange:(NSRange){ + cv.numberOfSections - abs(components.month), + abs(components.month) + }]]; + + [cv insertSections:[NSIndexSet indexSetWithIndexesInRange:(NSRange){ + 0, + abs(components.month) + }]]; + + } else { + + [cv insertSections:[NSIndexSet indexSetWithIndexesInRange:(NSRange){ + cv.numberOfSections, + abs(components.month) + }]]; + + [cv deleteSections:[NSIndexSet indexSetWithIndexesInRange:(NSRange){ + 0, + abs(components.month) + }]]; + + } + + } completion:^(BOOL finished) { + + NSLog(@"%s %x", __PRETTY_FUNCTION__, finished); + + }]; + + for (UIView *view in cv.subviews) + [view.layer removeAllAnimations]; + +#else + + [cv reloadData]; + [cvLayout invalidateLayout]; + [cvLayout prepareLayout]; + + [self restoreSelection]; + +#endif + + NSInteger toSection = [self sectionForDate:fromSectionOfDate]; + UICollectionViewLayoutAttributes *toAttrs = [cvLayout layoutAttributesForItemAtIndexPath:[NSIndexPath indexPathForItem:0 inSection:toSection]]; + CGPoint toSectionOrigin = [self convertPoint:toAttrs.frame.origin fromView:cv]; + + [cv setContentOffset:(CGPoint) { + cv.contentOffset.x, + cv.contentOffset.y + (toSectionOrigin.y - fromSectionOrigin.y) + }]; +} + +- (NSInteger)sectionForDate:(NSDate *)date; +{ + return [self.calendar components:NSCalendarUnitMonth fromDate:[self dateForFirstDayInSection:0] toDate:date options:0].month; +} + +- (NSDate *)dateForFirstDayInSection:(NSInteger)section +{ + return [self.calendar dateByAddingComponents:((^{ + NSDateComponents *dateComponents = [NSDateComponents new]; + dateComponents.month = section; + return dateComponents; + })()) toDate:[self dateFromPickerDate:self.fromDate] options:0]; +} + +- (NSUInteger)numberOfWeeksForMonthOfDate:(NSDate *)date +{ +#if 0 + + NSRange weekRange = [self.calendar rangeOfUnit:NSWeekCalendarUnit inUnit:NSMonthCalendarUnit forDate:date]; + + // Unknown Apple's bug with `NSRange.length` in NSIslamicCalendar + // Tested on iOS 7.0 (Simulator) / iOS 7.1.2 (Device) / iOS 8.0 (Simulator) + // Xcode Version 5.1.1 (5B1008), XCode Version 6.0 (6A267n) + // Monday, Shawwal 29, 1435 AH, 11:59:40 PM + + // Example: Friday, Muharram 29, 1436 AH at 12:00:00 AM GMT+03:00 + NSUInteger incorrectNSRangeLength1 = NSUIntegerMax - 44; // must be 5 + + // Example: Wednesday, Muharram 29, 1434 AH at 12:00:00 AM GMT+03:00 + NSUInteger incorrectNSRangeLength2 = NSUIntegerMax - 45; // must be 5 + + if ((weekRange.length == incorrectNSRangeLength1) || (weekRange.length == incorrectNSRangeLength2)) { + NSLog(@"%lu", (unsigned long)(weekRange.length)); + return 5; + } else { + return weekRange.length; + } + +#else + + NSDate *firstDayInMonth = [self.calendar dateFromComponents:[self.calendar components:NSCalendarUnitYear|NSCalendarUnitMonth fromDate:date]]; + + NSDate *lastDayInMonth = [self.calendar dateByAddingComponents:((^{ + NSDateComponents *dateComponents = [NSDateComponents new]; + dateComponents.month = 1; + dateComponents.day = -1; + return dateComponents; + })()) toDate:firstDayInMonth options:0]; + + NSDate *fromFirstWeekday = [self.calendar dateFromComponents:((^{ + NSDateComponents *dateComponents = [self.calendar components:NSCalendarUnitWeekOfYear|NSCalendarUnitYearForWeekOfYear fromDate:firstDayInMonth]; + dateComponents.weekday = self.calendar.firstWeekday; + return dateComponents; + })())]; + + NSDate *toFirstWeekday = [self.calendar dateFromComponents:((^{ + NSDateComponents *dateComponents = [self.calendar components:NSCalendarUnitWeekOfYear|NSCalendarUnitYearForWeekOfYear fromDate:lastDayInMonth]; + dateComponents.weekday = self.calendar.firstWeekday; + return dateComponents; + })())]; + + return 1 + [self.calendar components:NSCalendarUnitWeekOfYear fromDate:fromFirstWeekday toDate:toFirstWeekday options:0].weekOfYear; + +#endif +} + +- (NSDate *)dateFromPickerDate:(RSDFDatePickerDate)dateStruct +{ + return [self.calendar dateFromComponents:[self dateComponentsFromPickerDate:dateStruct]]; +} + +- (NSDateComponents *)dateComponentsFromPickerDate:(RSDFDatePickerDate)dateStruct +{ + NSDateComponents *components = [NSDateComponents new]; + components.year = dateStruct.year; + components.month = dateStruct.month; + components.day = dateStruct.day; + return components; +} + +- (RSDFDatePickerDate)pickerDateFromDate:(NSDate *)date +{ + NSDateComponents *components = [self.calendar components:NSCalendarUnitYear|NSCalendarUnitMonth|NSCalendarUnitDay fromDate:date]; + return (RSDFDatePickerDate) { + components.year, + components.month, + components.day + }; +} + +- (NSUInteger)reorderedWeekday:(NSUInteger)weekday +{ + NSInteger ordered = weekday - self.calendar.firstWeekday; + if (ordered < 0) { + ordered = self.daysInWeek + ordered; + } + + return ordered; +} + +- (NSIndexPath *)indexPathForDate:(NSDate *)date +{ + NSInteger monthSection = [self sectionForDate:date]; + NSDate *firstDayInMonth = [self dateForFirstDayInSection:monthSection]; + NSUInteger weekday = [self reorderedWeekday:[self.calendar components:NSCalendarUnitWeekday fromDate:firstDayInMonth].weekday]; + NSInteger dateItem = [self.calendar components:NSCalendarUnitDay fromDate:firstDayInMonth toDate:date options:0].day + weekday; + NSIndexPath *indexPath = [NSIndexPath indexPathForItem:dateItem inSection:monthSection]; + + return indexPath; +} + +- (CGRect)frameForHeaderForSection:(NSInteger)section +{ + NSIndexPath *indexPath = [NSIndexPath indexPathForItem:0 inSection:section]; + UICollectionViewLayoutAttributes *attributes = [self.collectionView layoutAttributesForSupplementaryElementOfKind:UICollectionElementKindSectionHeader atIndexPath:indexPath]; + + return attributes.frame; +} + +- (void)scrollToTopOfSection:(NSInteger)section animated:(BOOL)animated +{ + CGRect headerRect = [self frameForHeaderForSection:section]; + CGPoint topOfHeader = CGPointMake(0, headerRect.origin.y - _collectionView.contentInset.top); + [_collectionView setContentOffset:topOfHeader animated:animated]; +} + +- (CGRect)frameForItemAtIndexPath:(NSIndexPath *)indexPath +{ + UICollectionViewLayoutAttributes *attributes = [self.collectionView layoutAttributesForItemAtIndexPath:indexPath]; + + return attributes.frame; +} + +- (void)restoreSelection +{ + if (self.selectedDate && + [self.selectedDate compare:[self dateFromPickerDate:self.fromDate]] != NSOrderedAscending && + [self.selectedDate compare:[self dateFromPickerDate:self.toDate]] != NSOrderedDescending) { + NSIndexPath *indexPathForSelectedDate = [self indexPathForDate:self.selectedDate]; + [self.collectionView selectItemAtIndexPath:indexPathForSelectedDate animated:NO scrollPosition:UICollectionViewScrollPositionNone]; + UICollectionViewCell *selectedCell = [self.collectionView cellForItemAtIndexPath:indexPathForSelectedDate]; + if (selectedCell) { + [selectedCell setNeedsDisplay]; + } + } +} + +#pragma mark - UICollectionViewDataSource + +- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView +{ + return [self.calendar components:NSCalendarUnitMonth fromDate:[self dateFromPickerDate:self.fromDate] toDate:[self dateFromPickerDate:self.toDate] options:0].month; +} + +- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section +{ + return self.daysInWeek * [self numberOfWeeksForMonthOfDate:[self dateForFirstDayInSection:section]]; +} + +- (RSDFDatePickerDayCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath +{ + RSDFDatePickerDayCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:RSDFDatePickerViewDayCellIdentifier forIndexPath:indexPath]; + + NSDate *firstDayInMonth = [self dateForFirstDayInSection:indexPath.section]; + RSDFDatePickerDate firstDayPickerDate = [self pickerDateFromDate:firstDayInMonth]; + NSUInteger weekday = [self reorderedWeekday:[self.calendar components:NSCalendarUnitWeekday fromDate:firstDayInMonth].weekday]; + + NSDate *cellDate = [self.calendar dateByAddingComponents:((^{ + NSDateComponents *dateComponents = [NSDateComponents new]; + dateComponents.day = indexPath.item - weekday; + return dateComponents; + })()) toDate:firstDayInMonth options:0]; + RSDFDatePickerDate cellPickerDate = [self pickerDateFromDate:cellDate]; + + cell.date = cellPickerDate; + cell.dateLabel.text = [NSString stringWithFormat:@"%lu", (unsigned long)(cellPickerDate.day)]; + + cell.notThisMonth = !((firstDayPickerDate.year == cellPickerDate.year) && (firstDayPickerDate.month == cellPickerDate.month)); + if (!cell.isNotThisMonth) { + weekday = [self.calendar components:NSCalendarUnitWeekday fromDate:cellDate].weekday; + cell.dayOff = (weekday == 1) || (weekday == 7); + + if ([self.dataSource respondsToSelector:@selector(datePickerView:shouldMarkDate:)]) { + cell.marked = [self.dataSource datePickerView:self shouldMarkDate:cellDate]; + + if (cell.marked) { + if ([self.dataSource respondsToSelector:@selector(datePickerView:markImageForDate:)]) { + cell.markImage = [self.dataSource datePickerView:self markImageForDate:cellDate]; + } else if ([self.dataSource respondsToSelector:@selector(datePickerView:markImageColorForDate:)]) { + cell.markImageColor = [self.dataSource datePickerView:self markImageColorForDate:cellDate]; + } + } + } + + cell.today = ([cellDate compare:_today] == NSOrderedSame) ? YES : NO; + } + + [cell setNeedsDisplay]; + + return cell; +} + +- (UICollectionReusableView *)collectionView:(UICollectionView *)collectionView viewForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath +{ + if ([kind isEqualToString:UICollectionElementKindSectionHeader]) { + + RSDFDatePickerMonthHeader *monthHeader = [collectionView dequeueReusableSupplementaryViewOfKind:kind withReuseIdentifier:RSDFDatePickerViewMonthHeaderIdentifier forIndexPath:indexPath]; + + NSString *dateFormatterName = [NSString stringWithFormat:@"calendarMonthHeader_%@_%@", [self.calendar calendarIdentifier], [[self.calendar locale] localeIdentifier]]; + NSDateFormatter *dateFormatter = [self.calendar df_dateFormatterNamed:dateFormatterName withConstructor:^{ + NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init]; + [dateFormatter setCalendar:self.calendar]; + [dateFormatter setLocale:[self.calendar locale]]; + return dateFormatter; + }]; + + NSDate *formattedDate = [self dateForFirstDayInSection:indexPath.section]; + RSDFDatePickerDate date = [self pickerDateFromDate:formattedDate]; + + monthHeader.date = date; + + NSString *monthString = [dateFormatter shortStandaloneMonthSymbols][date.month - 1]; + monthHeader.dateLabel.text = [[NSString stringWithFormat:@"%@ %lu", monthString, (unsigned long)(date.year)] uppercaseString]; + + RSDFDatePickerDate today = [self pickerDateFromDate:_today]; + if ( (today.month == date.month) && (today.year == date.year) ) { + monthHeader.currentMonth = YES; + } else { + monthHeader.currentMonth = NO; + } + + return monthHeader; + + } + + return nil; +} + +#pragma mark - UICollectionViewDelegate + +// We are cheating by piggybacking on view state to avoid recalculation +// in -collectionView:shouldHighlightItemAtIndexPath: +// and -collectionView:shouldSelectItemAtIndexPath:. + +// A native refactoring process might introduce duplicate state which is bad too. + +- (BOOL)collectionView:(UICollectionView *)collectionView shouldHighlightItemAtIndexPath:(NSIndexPath *)indexPath +{ + if (((RSDFDatePickerDayCell *)[collectionView cellForItemAtIndexPath:indexPath]).isNotThisMonth) { + return NO; + } + + if ([self.delegate respondsToSelector:@selector(datePickerView:shouldHighlightDate:)]) { + RSDFDatePickerDayCell *cell = ((RSDFDatePickerDayCell *)[collectionView cellForItemAtIndexPath:indexPath]); + NSDate *date = cell ? [self dateFromPickerDate:cell.date] : nil; + return [self.delegate datePickerView:self shouldHighlightDate:date]; + } + + return YES; +} + +- (void)collectionView:(UICollectionView *)collectionView didHighlightItemAtIndexPath:(NSIndexPath *)indexPath +{ + UICollectionViewCell *cell = [collectionView cellForItemAtIndexPath:indexPath]; + [cell setNeedsDisplay]; +} + +- (void)collectionView:(UICollectionView *)collectionView didUnhighlightItemAtIndexPath:(NSIndexPath *)indexPath +{ + UICollectionViewCell *cell = [collectionView cellForItemAtIndexPath:indexPath]; + [cell setNeedsDisplay]; +} + +- (BOOL)collectionView:(UICollectionView *)collectionView shouldSelectItemAtIndexPath:(NSIndexPath *)indexPath +{ + if (((RSDFDatePickerDayCell *)[collectionView cellForItemAtIndexPath:indexPath]).isNotThisMonth) { + return NO; + } + + if ([self.delegate respondsToSelector:@selector(datePickerView:shouldSelectDate:)]) { + RSDFDatePickerDayCell *cell = ((RSDFDatePickerDayCell *)[collectionView cellForItemAtIndexPath:indexPath]); + NSDate *date = cell ? [self dateFromPickerDate:cell.date] : nil; + return [self.delegate datePickerView:self shouldSelectDate:date]; + } + + return YES; +} + +- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath +{ + RSDFDatePickerDayCell *cell = ((RSDFDatePickerDayCell *)[collectionView cellForItemAtIndexPath:indexPath]); + NSDate *date = cell ? [self dateFromPickerDate:cell.date] : nil; + + [self selectDate:date]; + + if ([self.delegate respondsToSelector:@selector(datePickerView:didSelectDate:)]) { + [self.delegate datePickerView:self didSelectDate:date]; + } +} + +#pragma mark - UICollectionViewDelegateFlowLayout + +- (CGSize)collectionView:(UICollectionView *)collectionView layout:(RSDFDatePickerCollectionViewLayout *)collectionViewLayout referenceSizeForHeaderInSection:(NSInteger)section +{ + return [collectionViewLayout selfHeaderReferenceSize]; +} + +- (CGSize)collectionView:(UICollectionView *)collectionView layout:(RSDFDatePickerCollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath +{ + return [collectionViewLayout selfItemSize]; +} + +#pragma mark - RSDFDatePickerCollectionViewDelegate + +- (void)pickerCollectionViewWillLayoutSubviews:(RSDFDatePickerCollectionView *)pickerCollectionView +{ + // Note: relayout is slower than calculating 3 or 6 months’ worth of data at a time + // So we punt 6 months at a time. + + // Running Time Self Symbol Name + // + // 1647.0ms 23.7% 1647.0 objc_msgSend + // 193.0ms 2.7% 193.0 -[NSIndexPath compare:] + // 163.0ms 2.3% 163.0 objc::DenseMap, objc::DenseMapInfo >::LookupBucketFor(objc_object* const&, std::pair*&) const + // 141.0ms 2.0% 141.0 DYLD-STUB$$-[_UIHostedTextServiceSession dismissTextServiceAnimated:] + // 138.0ms 1.9% 138.0 -[NSObject retain] + // 136.0ms 1.9% 136.0 -[NSIndexPath indexAtPosition:] + // 124.0ms 1.7% 124.0 -[_UICollectionViewItemKey isEqual:] + // 118.0ms 1.7% 118.0 _objc_rootReleaseWasZero + // 105.0ms 1.5% 105.0 DYLD-STUB$$CFDictionarySetValue$shim + + if (pickerCollectionView.contentOffset.y < 0.0f) { + [self appendPastDates]; + } + + if (pickerCollectionView.contentOffset.y > (pickerCollectionView.contentSize.height - CGRectGetHeight(pickerCollectionView.bounds))) { + [self appendFutureDates]; + } +} + +#pragma mark - UIScrollView + +- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView +{ + if (self.isPagingEnabled) { + if (scrollView.contentOffset.y < CGRectGetHeight(scrollView.bounds)) { + [self appendPastDates]; + } + + if (scrollView.contentOffset.y + CGRectGetHeight(scrollView.bounds) * 2 > scrollView.contentSize.height) { + [self appendFutureDates]; + } + } +} + +- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset +{ + if (self.isPagingEnabled) { + NSArray *sortedIndexPathsForVisibleItems = [[self.collectionView indexPathsForVisibleItems] sortedArrayUsingComparator:^NSComparisonResult(NSIndexPath *obj1, NSIndexPath * obj2) { + return obj1.section > obj2.section; + }]; + + NSUInteger visibleSection; + NSUInteger nextSection; + if (velocity.y > 0.0) { + visibleSection = [[sortedIndexPathsForVisibleItems firstObject] section]; + nextSection = visibleSection + 1; + } else if (velocity.y < 0.0) { + visibleSection = [[sortedIndexPathsForVisibleItems lastObject] section]; + nextSection = visibleSection - 1; + } else { + visibleSection = [sortedIndexPathsForVisibleItems[sortedIndexPathsForVisibleItems.count / 2] section]; + nextSection = visibleSection; + } + + CGRect headerRect = [self frameForHeaderForSection:nextSection]; + CGPoint topOfHeader = CGPointMake(0, headerRect.origin.y - self.collectionView.contentInset.top); + + *targetContentOffset = topOfHeader; + + scrollView.decelerationRate = UIScrollViewDecelerationRateFast; + } +} + +@end diff --git a/RSDayFlow/RSDayFlow.h b/RSDayFlow/RSDayFlow.h new file mode 100644 index 0000000..b36bd27 --- /dev/null +++ b/RSDayFlow/RSDayFlow.h @@ -0,0 +1,41 @@ +// +// RSDayFlow.h +// +// Copyright (c) 2013 Evadne Wu, http://radi.ws/ +// Copyright (c) 2013-2015 Ruslan Skorb, http://ruslanskorb.com +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +/** + `RSDayFlow` is an iOS 7 Calendar with Infinite Scrolling. + */ + +typedef struct { + NSUInteger year; + NSUInteger month; + NSUInteger day; +} RSDFDatePickerDate; + +#import "RSDFDatePickerView.h" +#import "RSDFDatePickerDaysOfWeekView.h" +#import "RSDFDatePickerCollectionView.h" +#import "RSDFDatePickerCollectionViewLayout.h" +#import "RSDFDatePickerMonthHeader.h" +#import "RSDFDatePickerDayCell.h" diff --git a/Screenshot.png b/Screenshot.png new file mode 100644 index 0000000..d2dce98 Binary files /dev/null and b/Screenshot.png differ