diff --git a/TimesSquare-iOS/Info.plist b/TimesSquare-iOS/Info.plist
new file mode 100644
index 0000000..f8785d5
--- /dev/null
+++ b/TimesSquare-iOS/Info.plist
@@ -0,0 +1,32 @@
+
+
+
+
+ CFBundleDevelopmentRegion
+ en
+ CFBundleExecutable
+ $(EXECUTABLE_NAME)
+ CFBundleIdentifier
+ $(PRODUCT_BUNDLE_IDENTIFIER)
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundleName
+ $(PRODUCT_NAME)
+ CFBundlePackageType
+ FMWK
+ CFBundleShortVersionString
+ 1.0
+ CFBundleSignature
+ ????
+ CFBundleVersion
+ $(CURRENT_PROJECT_VERSION)
+ LSApplicationCategoryType
+
+ NSPrincipalClass
+
+ UIAppFonts
+
+
+
+
+
diff --git a/TimesSquare.podspec b/TimesSquare.podspec
index bd01683..694f594 100644
--- a/TimesSquare.podspec
+++ b/TimesSquare.podspec
@@ -9,4 +9,4 @@ Pod::Spec.new do |s|
s.platform = :ios, '5.0'
s.source_files = 'TimesSquare/*.{h,m}'
s.requires_arc = true
-end
\ No newline at end of file
+end
diff --git a/TimesSquare.xcodeproj/project.pbxproj b/TimesSquare.xcodeproj/project.pbxproj
index bd77b35..acf3560 100644
--- a/TimesSquare.xcodeproj/project.pbxproj
+++ b/TimesSquare.xcodeproj/project.pbxproj
@@ -7,6 +7,26 @@
objects = {
/* Begin PBXBuildFile section */
+ 5C4733B21AC357A700269A66 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A806809E167012980071C71E /* CoreGraphics.framework */; };
+ 5C4733B31AC357AB00269A66 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A806805E16700FD70071C71E /* Foundation.framework */; };
+ 5C4733B41AC357B200269A66 /* TimesSquare.h in Headers */ = {isa = PBXBuildFile; fileRef = A806806316700FD70071C71E /* TimesSquare.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 5C4733B51AC357B200269A66 /* TSQCalendarCell.h in Headers */ = {isa = PBXBuildFile; fileRef = A8068086167010030071C71E /* TSQCalendarCell.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 5C4733B61AC357B200269A66 /* TSQCalendarMonthHeaderCell.h in Headers */ = {isa = PBXBuildFile; fileRef = A8068088167010030071C71E /* TSQCalendarMonthHeaderCell.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 5C4733B71AC357B200269A66 /* TSQCalendarRowCell.h in Headers */ = {isa = PBXBuildFile; fileRef = A806808A167010030071C71E /* TSQCalendarRowCell.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 5C4733B81AC357B200269A66 /* TSQCalendarView.h in Headers */ = {isa = PBXBuildFile; fileRef = A806808C167010030071C71E /* TSQCalendarView.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 5C4733B91AC357C000269A66 /* TSQCalendarCell.m in Sources */ = {isa = PBXBuildFile; fileRef = A8068087167010030071C71E /* TSQCalendarCell.m */; };
+ 5C4733BA1AC357C000269A66 /* TSQCalendarMonthHeaderCell.m in Sources */ = {isa = PBXBuildFile; fileRef = A8068089167010030071C71E /* TSQCalendarMonthHeaderCell.m */; };
+ 5C4733BB1AC357C000269A66 /* TSQCalendarRowCell.m in Sources */ = {isa = PBXBuildFile; fileRef = A806808B167010030071C71E /* TSQCalendarRowCell.m */; };
+ 5C4733BC1AC357C000269A66 /* TSQCalendarView.m in Sources */ = {isa = PBXBuildFile; fileRef = A806808D167010030071C71E /* TSQCalendarView.m */; };
+ 5CEF2FEE1ACB788F003E8F61 /* TimesSquare.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = A806806316700FD70071C71E /* TimesSquare.h */; };
+ 5CEF2FEF1ACB788F003E8F61 /* TSQCalendarCell.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = A8068086167010030071C71E /* TSQCalendarCell.h */; };
+ 5CEF2FF01ACB788F003E8F61 /* TSQCalendarMonthHeaderCell.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = A8068088167010030071C71E /* TSQCalendarMonthHeaderCell.h */; };
+ 5CEF2FF11ACB788F003E8F61 /* TSQCalendarRowCell.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = A806808A167010030071C71E /* TSQCalendarRowCell.h */; };
+ 5CEF2FF21ACB788F003E8F61 /* TSQCalendarView.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = A806808C167010030071C71E /* TSQCalendarView.h */; };
+ 87F195FB1B61B2D100EC341E /* TSQCalendarDayButton.h in Headers */ = {isa = PBXBuildFile; fileRef = 87F195F91B61B2D100EC341E /* TSQCalendarDayButton.h */; };
+ 87F195FC1B61B2D100EC341E /* TSQCalendarDayButton.h in Headers */ = {isa = PBXBuildFile; fileRef = 87F195F91B61B2D100EC341E /* TSQCalendarDayButton.h */; };
+ 87F195FD1B61B2D100EC341E /* TSQCalendarDayButton.m in Sources */ = {isa = PBXBuildFile; fileRef = 87F195FA1B61B2D100EC341E /* TSQCalendarDayButton.m */; };
+ 87F195FE1B61B2D100EC341E /* TSQCalendarDayButton.m in Sources */ = {isa = PBXBuildFile; fileRef = 87F195FA1B61B2D100EC341E /* TSQCalendarDayButton.m */; };
A806805F16700FD70071C71E /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A806805E16700FD70071C71E /* Foundation.framework */; };
A806808E167010030071C71E /* TSQCalendarCell.m in Sources */ = {isa = PBXBuildFile; fileRef = A8068087167010030071C71E /* TSQCalendarCell.m */; };
A8068090167010030071C71E /* TSQCalendarMonthHeaderCell.m in Sources */ = {isa = PBXBuildFile; fileRef = A8068089167010030071C71E /* TSQCalendarMonthHeaderCell.m */; };
@@ -21,6 +41,20 @@
/* End PBXBuildFile section */
/* Begin PBXCopyFilesBuildPhase section */
+ 5CEF2FED1ACB785C003E8F61 /* CopyFiles */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 2147483647;
+ dstPath = Headers;
+ dstSubfolderSpec = 1;
+ files = (
+ 5CEF2FEE1ACB788F003E8F61 /* TimesSquare.h in CopyFiles */,
+ 5CEF2FEF1ACB788F003E8F61 /* TSQCalendarCell.h in CopyFiles */,
+ 5CEF2FF01ACB788F003E8F61 /* TSQCalendarMonthHeaderCell.h in CopyFiles */,
+ 5CEF2FF11ACB788F003E8F61 /* TSQCalendarRowCell.h in CopyFiles */,
+ 5CEF2FF21ACB788F003E8F61 /* TSQCalendarView.h in CopyFiles */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
A806805916700FD70071C71E /* CopyFiles */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
@@ -33,6 +67,10 @@
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
+ 5C4733991AC3575B00269A66 /* TimesSquare.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = TimesSquare.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+ 5C47339C1AC3575C00269A66 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
+ 87F195F91B61B2D100EC341E /* TSQCalendarDayButton.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TSQCalendarDayButton.h; sourceTree = ""; };
+ 87F195FA1B61B2D100EC341E /* TSQCalendarDayButton.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TSQCalendarDayButton.m; sourceTree = ""; };
A806805B16700FD70071C71E /* libTimesSquare.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libTimesSquare.a; sourceTree = BUILT_PRODUCTS_DIR; };
A806805E16700FD70071C71E /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
A806806216700FD70071C71E /* TimesSquare-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "TimesSquare-Prefix.pch"; sourceTree = ""; };
@@ -51,6 +89,15 @@
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
+ 5C4733951AC3575B00269A66 /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 5C4733B31AC357AB00269A66 /* Foundation.framework in Frameworks */,
+ 5C4733B21AC357A700269A66 /* CoreGraphics.framework in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
A806805816700FD70071C71E /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
@@ -63,10 +110,27 @@
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
+ 5C47339A1AC3575C00269A66 /* TimesSquare-iOS */ = {
+ isa = PBXGroup;
+ children = (
+ 5C47339B1AC3575C00269A66 /* Supporting Files */,
+ );
+ path = "TimesSquare-iOS";
+ sourceTree = "";
+ };
+ 5C47339B1AC3575C00269A66 /* Supporting Files */ = {
+ isa = PBXGroup;
+ children = (
+ 5C47339C1AC3575C00269A66 /* Info.plist */,
+ );
+ name = "Supporting Files";
+ sourceTree = "";
+ };
A806805016700FD70071C71E = {
isa = PBXGroup;
children = (
A806806016700FD70071C71E /* TimesSquare */,
+ 5C47339A1AC3575C00269A66 /* TimesSquare-iOS */,
A806805D16700FD70071C71E /* Frameworks */,
A806805C16700FD70071C71E /* Products */,
);
@@ -76,6 +140,7 @@
isa = PBXGroup;
children = (
A806805B16700FD70071C71E /* libTimesSquare.a */,
+ 5C4733991AC3575B00269A66 /* TimesSquare.framework */,
);
name = Products;
sourceTree = "";
@@ -102,6 +167,8 @@
A806808A167010030071C71E /* TSQCalendarRowCell.h */,
A806808B167010030071C71E /* TSQCalendarRowCell.m */,
A806808C167010030071C71E /* TSQCalendarView.h */,
+ 87F195F91B61B2D100EC341E /* TSQCalendarDayButton.h */,
+ 87F195FA1B61B2D100EC341E /* TSQCalendarDayButton.m */,
A806808D167010030071C71E /* TSQCalendarView.m */,
A806806116700FD70071C71E /* Supporting Files */,
);
@@ -119,12 +186,26 @@
/* End PBXGroup section */
/* Begin PBXHeadersBuildPhase section */
+ 5C4733961AC3575B00269A66 /* Headers */ = {
+ isa = PBXHeadersBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 5C4733B41AC357B200269A66 /* TimesSquare.h in Headers */,
+ 5C4733B81AC357B200269A66 /* TSQCalendarView.h in Headers */,
+ 87F195FC1B61B2D100EC341E /* TSQCalendarDayButton.h in Headers */,
+ 5C4733B51AC357B200269A66 /* TSQCalendarCell.h in Headers */,
+ 5C4733B61AC357B200269A66 /* TSQCalendarMonthHeaderCell.h in Headers */,
+ 5C4733B71AC357B200269A66 /* TSQCalendarRowCell.h in Headers */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
EFD8DE6B167AF77100F87FBE /* Headers */ = {
isa = PBXHeadersBuildPhase;
buildActionMask = 2147483647;
files = (
EFD8DE6D167AF77B00F87FBE /* TimesSquare.h in Headers */,
EFD8DE6E167AF78100F87FBE /* TSQCalendarCell.h in Headers */,
+ 87F195FB1B61B2D100EC341E /* TSQCalendarDayButton.h in Headers */,
EFD8DE6F167AF78600F87FBE /* TSQCalendarMonthHeaderCell.h in Headers */,
EFD8DE70167AF78C00F87FBE /* TSQCalendarRowCell.h in Headers */,
EFD8DE71167AF79000F87FBE /* TSQCalendarView.h in Headers */,
@@ -151,6 +232,25 @@
/* End PBXLegacyTarget section */
/* Begin PBXNativeTarget section */
+ 5C4733981AC3575B00269A66 /* TimesSquare-iOS */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 5C4733B01AC3575C00269A66 /* Build configuration list for PBXNativeTarget "TimesSquare-iOS" */;
+ buildPhases = (
+ 5C4733941AC3575B00269A66 /* Sources */,
+ 5C4733951AC3575B00269A66 /* Frameworks */,
+ 5C4733961AC3575B00269A66 /* Headers */,
+ 5C4733971AC3575B00269A66 /* Resources */,
+ 5CEF2FED1ACB785C003E8F61 /* CopyFiles */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = "TimesSquare-iOS";
+ productName = "TimesSquare-iOS";
+ productReference = 5C4733991AC3575B00269A66 /* TimesSquare.framework */;
+ productType = "com.apple.product-type.framework";
+ };
A806805A16700FD70071C71E /* TimesSquare */ = {
isa = PBXNativeTarget;
buildConfigurationList = A806808016700FD80071C71E /* Build configuration list for PBXNativeTarget "TimesSquare" */;
@@ -175,14 +275,20 @@
A806805216700FD70071C71E /* Project object */ = {
isa = PBXProject;
attributes = {
- LastUpgradeCheck = 0640;
+ LastUpgradeCheck = 1320;
ORGANIZATIONNAME = Square;
+ TargetAttributes = {
+ 5C4733981AC3575B00269A66 = {
+ CreatedOnToolsVersion = 6.2;
+ };
+ };
};
buildConfigurationList = A806805516700FD70071C71E /* Build configuration list for PBXProject "TimesSquare" */;
compatibilityVersion = "Xcode 3.2";
developmentRegion = English;
hasScannedForEncodings = 0;
knownRegions = (
+ English,
en,
);
mainGroup = A806805016700FD70071C71E;
@@ -192,17 +298,41 @@
targets = (
A806805A16700FD70071C71E /* TimesSquare */,
A81E05F71682A0E000E79A2B /* TimesSquare Documentation */,
+ 5C4733981AC3575B00269A66 /* TimesSquare-iOS */,
);
};
/* End PBXProject section */
+/* Begin PBXResourcesBuildPhase section */
+ 5C4733971AC3575B00269A66 /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXResourcesBuildPhase section */
+
/* Begin PBXSourcesBuildPhase section */
+ 5C4733941AC3575B00269A66 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 5C4733B91AC357C000269A66 /* TSQCalendarCell.m in Sources */,
+ 5C4733BA1AC357C000269A66 /* TSQCalendarMonthHeaderCell.m in Sources */,
+ 87F195FE1B61B2D100EC341E /* TSQCalendarDayButton.m in Sources */,
+ 5C4733BB1AC357C000269A66 /* TSQCalendarRowCell.m in Sources */,
+ 5C4733BC1AC357C000269A66 /* TSQCalendarView.m in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
A806805716700FD70071C71E /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
A806808E167010030071C71E /* TSQCalendarCell.m in Sources */,
A8068090167010030071C71E /* TSQCalendarMonthHeaderCell.m in Sources */,
+ 87F195FD1B61B2D100EC341E /* TSQCalendarDayButton.m in Sources */,
A8068092167010030071C71E /* TSQCalendarRowCell.m in Sources */,
A8068094167010030071C71E /* TSQCalendarView.m in Sources */,
);
@@ -211,29 +341,135 @@
/* End PBXSourcesBuildPhase section */
/* Begin XCBuildConfiguration section */
+ 5C4733AC1AC3575C00269A66 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ 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_ENUM_CONVERSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
+ CURRENT_PROJECT_VERSION = 1;
+ DEFINES_MODULE = YES;
+ DYLIB_COMPATIBILITY_VERSION = 1;
+ DYLIB_CURRENT_VERSION = 1;
+ DYLIB_INSTALL_NAME_BASE = "@rpath";
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ GCC_PREPROCESSOR_DEFINITIONS = (
+ "DEBUG=1",
+ "$(inherited)",
+ );
+ 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;
+ INFOPLIST_FILE = "TimesSquare-iOS/Info.plist";
+ INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
+ IPHONEOS_DEPLOYMENT_TARGET = 12.0;
+ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
+ MTL_ENABLE_DEBUG_INFO = YES;
+ ONLY_ACTIVE_ARCH = YES;
+ PRODUCT_BUNDLE_IDENTIFIER = "com.square.$(PRODUCT_NAME:rfc1034identifier)";
+ PRODUCT_NAME = TimesSquare;
+ SKIP_INSTALL = YES;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ VERSIONING_SYSTEM = "apple-generic";
+ VERSION_INFO_PREFIX = "";
+ };
+ name = Debug;
+ };
+ 5C4733AD1AC3575C00269A66 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ 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_ENUM_CONVERSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
+ COPY_PHASE_STRIP = NO;
+ CURRENT_PROJECT_VERSION = 1;
+ DEFINES_MODULE = YES;
+ DYLIB_COMPATIBILITY_VERSION = 1;
+ DYLIB_CURRENT_VERSION = 1;
+ DYLIB_INSTALL_NAME_BASE = "@rpath";
+ ENABLE_NS_ASSERTIONS = NO;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ 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;
+ INFOPLIST_FILE = "TimesSquare-iOS/Info.plist";
+ INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
+ IPHONEOS_DEPLOYMENT_TARGET = 12.0;
+ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
+ MTL_ENABLE_DEBUG_INFO = NO;
+ PRODUCT_BUNDLE_IDENTIFIER = "com.square.$(PRODUCT_NAME:rfc1034identifier)";
+ PRODUCT_NAME = TimesSquare;
+ SKIP_INSTALL = YES;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ VERSIONING_SYSTEM = "apple-generic";
+ VERSION_INFO_PREFIX = "";
+ };
+ name = Release;
+ };
A806807E16700FD80071C71E /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
+ BUILD_LIBRARY_FOR_DISTRIBUTION = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_COMMA = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ ENABLE_TESTABILITY = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO;
+ GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_SYMBOLS_PRIVATE_EXTERN = NO;
- GCC_TREAT_WARNINGS_AS_ERRORS = YES;
+ GCC_TREAT_WARNINGS_AS_ERRORS = NO;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES;
GCC_WARN_PEDANTIC = YES;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES;
+ GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
- IPHONEOS_DEPLOYMENT_TARGET = 5.0;
+ IPHONEOS_DEPLOYMENT_TARGET = 12.1;
ONLY_ACTIVE_ARCH = YES;
PUBLIC_HEADERS_FOLDER_PATH = "include/$(PRODUCT_NAME)";
RUN_CLANG_STATIC_ANALYZER = YES;
@@ -245,18 +481,40 @@
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
+ BUILD_LIBRARY_FOR_DISTRIBUTION = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_COMMA = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = YES;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
- GCC_TREAT_WARNINGS_AS_ERRORS = YES;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_TREAT_WARNINGS_AS_ERRORS = NO;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES;
GCC_WARN_PEDANTIC = YES;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES;
+ GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
- IPHONEOS_DEPLOYMENT_TARGET = 5.0;
+ IPHONEOS_DEPLOYMENT_TARGET = 12.1;
PUBLIC_HEADERS_FOLDER_PATH = "include/$(PRODUCT_NAME)";
RUN_CLANG_STATIC_ANALYZER = YES;
SDKROOT = iphoneos;
@@ -293,6 +551,7 @@
A81E05F91682A0E000E79A2B /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
+ CLANG_ENABLE_OBJC_WEAK = YES;
DEBUGGING_SYMBOLS = YES;
GCC_ENABLE_OBJC_EXCEPTIONS = YES;
GCC_GENERATE_DEBUGGING_SYMBOLS = YES;
@@ -309,6 +568,7 @@
A81E05FA1682A0E000E79A2B /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
+ CLANG_ENABLE_OBJC_WEAK = YES;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
GCC_ENABLE_OBJC_EXCEPTIONS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
@@ -323,6 +583,15 @@
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
+ 5C4733B01AC3575C00269A66 /* Build configuration list for PBXNativeTarget "TimesSquare-iOS" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 5C4733AC1AC3575C00269A66 /* Debug */,
+ 5C4733AD1AC3575C00269A66 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
A806805516700FD70071C71E /* Build configuration list for PBXProject "TimesSquare" */ = {
isa = XCConfigurationList;
buildConfigurations = (
diff --git a/TimesSquare.xcodeproj/xcshareddata/xcschemes/TimesSquare Documentation.xcscheme b/TimesSquare.xcodeproj/xcshareddata/xcschemes/TimesSquare Documentation.xcscheme
index 9366e66..b461875 100644
--- a/TimesSquare.xcodeproj/xcshareddata/xcschemes/TimesSquare Documentation.xcscheme
+++ b/TimesSquare.xcodeproj/xcshareddata/xcschemes/TimesSquare Documentation.xcscheme
@@ -1,6 +1,6 @@
+ shouldUseLaunchSchemeArgsEnv = "YES">
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/TimesSquare.xcodeproj/xcshareddata/xcschemes/TimesSquare.xcscheme b/TimesSquare.xcodeproj/xcshareddata/xcschemes/TimesSquare.xcscheme
index ad1cc5d..99fc9d6 100644
--- a/TimesSquare.xcodeproj/xcshareddata/xcschemes/TimesSquare.xcscheme
+++ b/TimesSquare.xcodeproj/xcshareddata/xcschemes/TimesSquare.xcscheme
@@ -1,6 +1,6 @@
+ shouldUseLaunchSchemeArgsEnv = "YES">
@@ -41,13 +41,14 @@
-
-
+
+typedef NS_ENUM(NSInteger, CalendarButtonType) {
+ CalendarButtonTypeNormalDay = 0,
+ CalendarButtonTypeOtherMonth = 1,
+ CalendarButtonTypeSelected = 2,
+ CalendarButtonTypeInitialDay = 3,
+};
+
+@interface TSQCalendarDayButton : UIButton
+
+@property (nonatomic, assign) CalendarButtonType type;
+@property (nonatomic, strong) NSDate *day;
+
+@property (nonatomic, strong) UILabel *tsqSubtitleLabel;
+@property (nonatomic, strong) UILabel *subtitleSymbolLabel;
+@property (nonatomic, strong) UIImageView *iconImageView;
+
+@property (nonatomic, assign) BOOL isInitialDay;
+
+- (BOOL)isForToday;
+- (BOOL)isForDay:(NSDate *)date;
+
+@end
diff --git a/TimesSquare/TSQCalendarDayButton.m b/TimesSquare/TSQCalendarDayButton.m
new file mode 100644
index 0000000..d10a034
--- /dev/null
+++ b/TimesSquare/TSQCalendarDayButton.m
@@ -0,0 +1,208 @@
+//
+// TSQCalendarDayButton.m
+// TimesSquare
+//
+// Created by Loretta Chan on 7/23/15.
+// Copyright (c) 2015 Square. All rights reserved.
+//
+
+#import "TSQCalendarDayButton.h"
+
+@implementation TSQCalendarDayButton
+
+static const CGFloat TSQCalendarRowCellMaxSubtitleHeight = 18.0f;
+static const CGFloat TSQCalendarRowCellSubtitleBuffer = 15.0f;
+
+- (instancetype)initWithFrame:(CGRect)frame
+{
+ self = [super initWithFrame:frame];
+ if (self) {
+ // default button type to normal day
+ self.type = CalendarButtonTypeNormalDay;
+
+ [self setTitleEdgeInsets:UIEdgeInsetsMake(-10, 0, 0, 0)];
+ self.tsqSubtitleLabel = [[UILabel alloc] initWithFrame:CGRectZero];
+ self.tsqSubtitleLabel.textAlignment = NSTextAlignmentCenter;
+ self.tsqSubtitleLabel.userInteractionEnabled = NO;
+ self.tsqSubtitleLabel.adjustsFontSizeToFitWidth = NO;
+ self.tsqSubtitleLabel.lineBreakMode = NSLineBreakByTruncatingTail;
+ [self addSubview:self.tsqSubtitleLabel];
+
+ self.subtitleSymbolLabel = [[UILabel alloc] initWithFrame:CGRectZero];
+ self.subtitleSymbolLabel.userInteractionEnabled = NO;
+ [self addSubview:self.subtitleSymbolLabel];
+
+ self.iconImageView = [[UIImageView alloc] initWithFrame:CGRectZero];
+ self.iconImageView.userInteractionEnabled = NO;
+ [self addSubview:self.iconImageView];
+
+ [self registerForNotifications];
+ }
+ return self;
+}
+
+- (void)dealloc
+{
+ [self unregisterForNotifications];
+}
+
+- (void)layoutSubviews
+{
+ [super layoutSubviews];
+
+ CGFloat midX = CGRectGetMidX(self.bounds);
+ CGFloat subtitleCenterY = 0.75f * CGRectGetMaxY(self.bounds);
+
+ self.tsqSubtitleLabel.hidden = !([self.tsqSubtitleLabel.text length] > 0);
+ self.subtitleSymbolLabel.hidden = !([self.subtitleSymbolLabel.text length] > 0);
+ self.iconImageView.hidden = (self.iconImageView.image == nil);
+
+ CGFloat iconWidth = self.iconImageView.image.size.width;
+ CGFloat iconHeight = self.iconImageView.image.size.height;
+
+ if (! self.tsqSubtitleLabel.hidden)
+ {
+ CGSize maxSubtitleSize = [self maxSubtitleSize];
+ CGSize sizeThatFits = [self.tsqSubtitleLabel sizeThatFits:maxSubtitleSize];
+
+ CGFloat subtitleWidth = fminf(sizeThatFits.width, maxSubtitleSize.width);
+ CGFloat subtitleHeight = fminf(sizeThatFits.height, maxSubtitleSize.height);
+ CGFloat originX = midX - subtitleWidth/2.0f;
+ CGFloat originY = subtitleCenterY - subtitleHeight/2.0f;
+
+ CGRect subtitleFrame = CGRectMake(floorf(originX),
+ floorf(originY),
+ subtitleWidth,
+ subtitleHeight);
+ self.tsqSubtitleLabel.frame = CGRectIntegral(subtitleFrame);
+ }
+
+ if (! self.subtitleSymbolLabel.hidden)
+ {
+ CGSize maxSymbolSize = [self maxSubtitleSymbolSize];
+ CGSize sizeThatFits = [self.subtitleSymbolLabel sizeThatFits:maxSymbolSize];
+
+ CGFloat symbolWidth = fminf(sizeThatFits.width, maxSymbolSize.width);
+ CGFloat symbolHeight = fminf(sizeThatFits.height, maxSymbolSize.height);
+ CGFloat originX = CGRectGetMaxX(self.bounds) - TSQCalendarRowCellSubtitleBuffer;
+ CGFloat originY = subtitleCenterY - symbolHeight/2.0f;
+
+ if (! self.tsqSubtitleLabel.hidden)
+ {
+ // when subtitle is showing, shift symbol to right of subtitle
+ CGFloat symbolBuffer = (TSQCalendarRowCellSubtitleBuffer - symbolWidth)/2.0f;
+ originX = CGRectGetMaxX(self.tsqSubtitleLabel.frame) + symbolBuffer;
+ }
+
+ CGRect symbolFrame = CGRectMake(floorf(originX),
+ floorf(originY),
+ symbolWidth,
+ symbolHeight);
+ self.subtitleSymbolLabel.frame = CGRectIntegral(symbolFrame);
+ }
+
+ if (! self.iconImageView.hidden)
+ {
+ CGFloat midX = CGRectGetMidX(self.bounds);
+
+ CGFloat originX = midX - iconWidth/2.0f;
+ CGFloat originY = subtitleCenterY - iconHeight/2.0f;
+
+ if (! self.tsqSubtitleLabel.hidden)
+ {
+ // when subtitle is showing, shift icon to left of subtitle
+ CGFloat iconBuffer = (TSQCalendarRowCellSubtitleBuffer - iconWidth)/2.0f;
+ originX = self.tsqSubtitleLabel.frame.origin.x - iconWidth - iconBuffer;
+ }
+
+ CGRect iconFrame = CGRectMake(floorf(originX),
+ floorf(originY),
+ iconWidth,
+ iconHeight);
+ self.iconImageView.frame = CGRectIntegral(iconFrame);
+ }
+}
+
+#pragma mark - Observations
+
+- (void)registerForNotifications
+{
+ [self.tsqSubtitleLabel addObserver:self
+ forKeyPath:@"font"
+ options:NSKeyValueObservingOptionNew
+ context:nil];
+
+ [self.tsqSubtitleLabel addObserver:self
+ forKeyPath:@"text"
+ options:NSKeyValueObservingOptionNew
+ context:nil];
+
+ [self.subtitleSymbolLabel addObserver:self
+ forKeyPath:@"font"
+ options:NSKeyValueObservingOptionNew
+ context:nil];
+
+ [self.subtitleSymbolLabel addObserver:self
+ forKeyPath:@"text"
+ options:NSKeyValueObservingOptionNew
+ context:nil];
+
+ [self.iconImageView addObserver:self
+ forKeyPath:@"image"
+ options:NSKeyValueObservingOptionNew
+ context:nil];
+}
+
+- (void)unregisterForNotifications
+{
+ [self.tsqSubtitleLabel removeObserver:self forKeyPath:@"font"];
+ [self.tsqSubtitleLabel removeObserver:self forKeyPath:@"text"];
+ [self.subtitleSymbolLabel removeObserver:self forKeyPath:@"font"];
+ [self.subtitleSymbolLabel removeObserver:self forKeyPath:@"text"];
+ [self.iconImageView removeObserver:self forKeyPath:@"image"];
+}
+
+- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
+{
+ // relayout subviews when certain properties of the subtitle label, subtitle symbol label, or icon image view changes
+ [self layoutSubviews];
+}
+
+#pragma mark - Helper Methods
+
+- (CGSize)maxSubtitleSize
+{
+ CGFloat maxWidth = self.bounds.size.width - 2 * TSQCalendarRowCellSubtitleBuffer;
+ return CGSizeMake(maxWidth, TSQCalendarRowCellMaxSubtitleHeight);
+}
+
+- (CGSize)maxSubtitleSymbolSize
+{
+ return CGSizeMake(8.0f, TSQCalendarRowCellMaxSubtitleHeight);
+}
+
+- (BOOL)isForToday
+{
+ NSDate *today = [NSDate date];
+ return [self isForDayIgnoringTime:today];
+}
+
+- (BOOL)isForDay:(NSDate *)day
+{
+ return [self isForDayIgnoringTime:day];
+}
+
+- (BOOL)isForDayIgnoringTime:(NSDate *)aDay
+{
+ if (aDay == nil || self.day == nil) {
+ return NO;
+ }
+
+ NSDateComponents *components1 = [[NSCalendar currentCalendar] components:(NSCalendarUnitYear| NSCalendarUnitMonth | NSCalendarUnitDay) fromDate:self.day];
+ NSDateComponents *components2 = [[NSCalendar currentCalendar] components:(NSCalendarUnitYear| NSCalendarUnitMonth | NSCalendarUnitDay) fromDate:aDay];
+ return ((components1.year == components2.year) &&
+ (components1.month == components2.month) &&
+ (components1.day == components2.day));
+}
+
+@end
diff --git a/TimesSquare/TSQCalendarMonthHeaderCell.h b/TimesSquare/TSQCalendarMonthHeaderCell.h
index d51838b..7a7e3f3 100644
--- a/TimesSquare/TSQCalendarMonthHeaderCell.h
+++ b/TimesSquare/TSQCalendarMonthHeaderCell.h
@@ -15,6 +15,8 @@
*/
@interface TSQCalendarMonthHeaderCell : TSQCalendarCell
+@property (nonatomic, strong) NSDateFormatter *monthDateFormatter;
+
/** @name Day Labels */
/** The day header labels.
diff --git a/TimesSquare/TSQCalendarMonthHeaderCell.m b/TimesSquare/TSQCalendarMonthHeaderCell.m
index 776f692..763051d 100644
--- a/TimesSquare/TSQCalendarMonthHeaderCell.m
+++ b/TimesSquare/TSQCalendarMonthHeaderCell.m
@@ -15,14 +15,14 @@
@interface TSQCalendarMonthHeaderCell ()
-@property (nonatomic, strong) NSDateFormatter *monthDateFormatter;
+
@end
@implementation TSQCalendarMonthHeaderCell
-- (id)initWithCalendar:(NSCalendar *)calendar reuseIdentifier:(NSString *)reuseIdentifier;
+- (id)initWithCalendar:(NSCalendar *)calendar reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithCalendar:calendar reuseIdentifier:reuseIdentifier];
if (!self) {
@@ -68,9 +68,9 @@ - (void)createHeaderLabels;
}
for (NSUInteger index = 0; index < self.daysInWeek; index++) {
- NSInteger ordinality = [self.calendar ordinalityOfUnit:NSDayCalendarUnit inUnit:NSWeekCalendarUnit forDate:referenceDate];
+ NSInteger ordinality = [self.calendar ordinalityOfUnit:NSCalendarUnitDay inUnit:NSCalendarUnitWeekOfMonth forDate:referenceDate];
UILabel *label = [[UILabel alloc] initWithFrame:self.frame];
- label.textAlignment = UITextAlignmentCenter;
+ label.textAlignment = NSTextAlignmentCenter;
label.text = [dayFormatter stringFromDate:referenceDate];
label.font = [UIFont boldSystemFontOfSize:12.f];
label.backgroundColor = self.backgroundColor;
@@ -85,7 +85,7 @@ - (void)createHeaderLabels;
}
self.headerLabels = headerLabels;
- self.textLabel.textAlignment = UITextAlignmentCenter;
+ self.textLabel.textAlignment = NSTextAlignmentCenter;
self.textLabel.textColor = self.textColor;
self.textLabel.shadowColor = [UIColor whiteColor];
self.textLabel.shadowOffset = self.shadowOffset;
diff --git a/TimesSquare/TSQCalendarRowCell.h b/TimesSquare/TSQCalendarRowCell.h
index 0fad589..e272a5e 100644
--- a/TimesSquare/TSQCalendarRowCell.h
+++ b/TimesSquare/TSQCalendarRowCell.h
@@ -15,6 +15,45 @@
*/
@interface TSQCalendarRowCell : TSQCalendarCell
+/** @name Text */
+
+/** The font used to display each day of the month.
+
+ This is the 19 point bold system font by default.
+ */
+@property (nonatomic, weak, readonly) UIFont *dayOfMonthFont;
+
+
+@property (nonatomic, weak, readonly) UIFont *subtitleFont;
+
+
+/** The text color for a day that's "today".
+
+This is white by default.
+*/
+@property (nonatomic, weak, readonly) UIColor *todayTextColor;
+@property (nonatomic, weak, readonly) UIColor *todayTextShadowColor;
+@property (nonatomic, weak, readonly) UIColor *todaySubtitleTextColor;
+
+@property (nonatomic, weak, readonly) UIColor *textShadowColor;
+@property (nonatomic, weak, readonly) UIColor *subtitleTextColor;
+
+/** The text color for a day that's selected
+
+ This is white by default.
+ */
+@property (nonatomic, weak, readonly) UIColor *selectedTextColor;
+@property (nonatomic, weak, readonly) UIColor *selectedTextShadowColor;
+@property (nonatomic, weak, readonly) UIColor *selectedSubtitleTextColor;
+
+/** The text color for the initial day
+
+ This uses the default text colors.
+ */
+@property (nonatomic, weak, readonly) UIColor *initialDayTextColor;
+@property (nonatomic, weak, readonly) UIColor *initialDayTextShadowColor;
+@property (nonatomic, weak, readonly) UIColor *initialDaySubtitleTextColor;
+
/** @name Images */
/** The background image for the entire row.
@@ -37,12 +76,22 @@
*/
@property (nonatomic, weak, readonly) UIImage *todayBackgroundImage;
+/** The background image for the initial date.
+
+ This is dark gray in the system's built-in Calendar app. You probably want to use a stretchable image.
+ */
+@property (nonatomic, weak, readonly) UIImage *initialDayBackgroundImage;
+
/** The background image for a day that's not this month.
These are the trailing days from the previous month or the leading days from the following month. This can be `nil`.
*/
@property (nonatomic, weak, readonly) UIImage *notThisMonthBackgroundImage;
+/** A small icon that appears below the day number to indicate today */
+
+@property (nonatomic, weak, readonly) UIImage *todayIcon;
+
/** @name State Properties Set by Calendar View */
/** The date at the beginning of the week for this cell.
@@ -57,6 +106,7 @@
*/
@property (nonatomic, getter = isBottomRow) BOOL bottomRow;
+
/** Method to select a specific date within the week.
This is funneled through and called by the calendar view, to facilitate deselection of other rows.
@@ -65,4 +115,14 @@
*/
- (void)selectColumnForDate:(NSDate *)date;
+/** Method to select the initial date within the week.
+
+ This is funneled through and called by the calendar view, to facilitate deselection of other rows.
+
+ @param date The initial date to select, or nil to deselect all columns.
+ */
+- (void)selectColumnForInitialDate:(NSDate *)date;
+
+- (void)deselectColumnForDate:(NSDate *)date;
+
@end
diff --git a/TimesSquare/TSQCalendarRowCell.m b/TimesSquare/TSQCalendarRowCell.m
index 0cf087b..1d4538b 100644
--- a/TimesSquare/TSQCalendarRowCell.m
+++ b/TimesSquare/TSQCalendarRowCell.m
@@ -9,22 +9,19 @@
#import "TSQCalendarRowCell.h"
#import "TSQCalendarView.h"
-
+#import "TSQCalendarDayButton.h"
@interface TSQCalendarRowCell ()
@property (nonatomic, strong) NSArray *dayButtons;
@property (nonatomic, strong) NSArray *notThisMonthButtons;
-@property (nonatomic, strong) UIButton *todayButton;
-@property (nonatomic, strong) UIButton *selectedButton;
+@property (nonatomic, strong) TSQCalendarDayButton *selectedButton;
-@property (nonatomic, assign) NSInteger indexOfTodayButton;
@property (nonatomic, assign) NSInteger indexOfSelectedButton;
@property (nonatomic, strong) NSDateFormatter *dayFormatter;
@property (nonatomic, strong) NSDateFormatter *accessibilityFormatter;
-@property (nonatomic, strong) NSDateComponents *todayDateComponents;
@property (nonatomic) NSInteger monthOfBeginningDate;
@end
@@ -32,7 +29,7 @@ @interface TSQCalendarRowCell ()
@implementation TSQCalendarRowCell
-- (id)initWithCalendar:(NSCalendar *)calendar reuseIdentifier:(NSString *)reuseIdentifier;
+- (id)initWithCalendar:(NSCalendar *)calendar reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithCalendar:calendar reuseIdentifier:reuseIdentifier];
if (!self) {
@@ -42,25 +39,98 @@ - (id)initWithCalendar:(NSCalendar *)calendar reuseIdentifier:(NSString *)reuseI
return self;
}
-- (void)configureButton:(UIButton *)button;
+#pragma mark - Fonts
+
+- (UIFont *)dayOfMonthFont
+{
+ return [UIFont boldSystemFontOfSize:19.0f];
+}
+
+- (UIFont *)subtitleFont
+{
+ return [UIFont boldSystemFontOfSize:12.0f];
+}
+
+#pragma mark - Colors
+
+- (UIColor *)todayTextColor
+{
+ return [UIColor whiteColor];
+}
+
+- (UIColor *)subtitleTextColor
+{
+ return [UIColor blackColor];
+}
+
+- (UIColor *)selectedTextColor
+{
+ return [UIColor whiteColor];
+}
+
+- (UIColor *)selectedSubtitleTextColor
+{
+ return [UIColor whiteColor];
+}
+
+- (UIColor *)textShadowColor
+{
+ return [UIColor whiteColor];
+}
+
+- (UIColor *)todayTextShadowColor
+{
+ return [UIColor colorWithWhite:0.0f alpha:0.75f];
+}
+
+- (UIColor *)selectedTextShadowColor
+{
+ return [UIColor colorWithWhite:0.0f alpha:0.75f];
+}
+
+- (UIColor *)todaySubtitleTextColor
+{
+ return [self subtitleTextColor];
+}
+
+- (UIColor *)initialDayTextColor
{
- button.titleLabel.font = [UIFont boldSystemFontOfSize:19.f];
+ return [self textColor];
+}
+
+- (UIColor *)initialDayTextShadowColor
+{
+ return [self textShadowColor];
+}
+
+- (UIColor *)initialDaySubtitleTextColor
+{
+ return [self subtitleTextColor];
+}
+
+#pragma mark - Buttons
+
+- (void)configureButton:(TSQCalendarDayButton *)button
+{
+ [self updateAppearanceForButton:button];
+
+ button.titleLabel.font = [self dayOfMonthFont];
+ button.tsqSubtitleLabel.font = [self subtitleFont];
+ button.subtitleSymbolLabel.font = [self subtitleFont];
button.titleLabel.shadowOffset = self.shadowOffset;
button.adjustsImageWhenDisabled = NO;
- [button setTitleColor:self.textColor forState:UIControlStateNormal];
- [button setTitleShadowColor:[UIColor whiteColor] forState:UIControlStateNormal];
}
- (void)createDayButtons;
{
NSMutableArray *dayButtons = [NSMutableArray arrayWithCapacity:self.daysInWeek];
for (NSUInteger index = 0; index < self.daysInWeek; index++) {
- UIButton *button = [[UIButton alloc] initWithFrame:self.contentView.bounds];
- [button addTarget:self action:@selector(dateButtonPressed:) forControlEvents:UIControlEventTouchDown];
+ TSQCalendarDayButton *button = [[TSQCalendarDayButton alloc] initWithFrame:self.contentView.bounds];
+ button.type = CalendarButtonTypeNormalDay;
+ [button addTarget:self action:@selector(dateButtonPressed:) forControlEvents:UIControlEventTouchUpInside];
[dayButtons addObject:button];
[self.contentView addSubview:button];
[self configureButton:button];
- [button setTitleColor:[self.textColor colorWithAlphaComponent:0.5f] forState:UIControlStateDisabled];
}
self.dayButtons = dayButtons;
}
@@ -69,48 +139,352 @@ - (void)createNotThisMonthButtons;
{
NSMutableArray *notThisMonthButtons = [NSMutableArray arrayWithCapacity:self.daysInWeek];
for (NSUInteger index = 0; index < self.daysInWeek; index++) {
- UIButton *button = [[UIButton alloc] initWithFrame:self.contentView.bounds];
+ TSQCalendarDayButton *button = [[TSQCalendarDayButton alloc] initWithFrame:self.contentView.bounds];
+ button.type = CalendarButtonTypeOtherMonth;
[notThisMonthButtons addObject:button];
[self.contentView addSubview:button];
[self configureButton:button];
-
button.enabled = NO;
UIColor *backgroundPattern = [UIColor colorWithPatternImage:[self notThisMonthBackgroundImage]];
button.backgroundColor = backgroundPattern;
- button.titleLabel.backgroundColor = backgroundPattern;
}
self.notThisMonthButtons = notThisMonthButtons;
}
-- (void)createTodayButton;
+- (void)createSelectedButton;
{
- self.todayButton = [[UIButton alloc] initWithFrame:self.contentView.bounds];
- [self.contentView addSubview:self.todayButton];
- [self configureButton:self.todayButton];
- [self.todayButton addTarget:self action:@selector(todayButtonPressed:) forControlEvents:UIControlEventTouchDown];
-
- [self.todayButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
- [self.todayButton setBackgroundImage:[self todayBackgroundImage] forState:UIControlStateNormal];
- [self.todayButton setTitleShadowColor:[UIColor colorWithWhite:0.0f alpha:0.75f] forState:UIControlStateNormal];
+ TSQCalendarDayButton *button = [[TSQCalendarDayButton alloc] initWithFrame:self.contentView.bounds];
+ button.type = CalendarButtonTypeSelected;
+ [button addTarget:self action:@selector(dateButtonPressed:) forControlEvents:UIControlEventTouchUpInside];
+ [self.contentView addSubview:button];
+ [self configureButton:button];
+ [button setBackgroundImage:[self selectedBackgroundImage] forState:UIControlStateNormal];
+ [button setAccessibilityTraits:UIAccessibilityTraitSelected|button.accessibilityTraits];
+ button.enabled = NO;
+ button.hidden = YES;
+ self.indexOfSelectedButton = -1;
- self.todayButton.titleLabel.shadowOffset = CGSizeMake(0.0f, -1.0f / [UIScreen mainScreen].scale);
+ self.selectedButton = button;
}
-- (void)createSelectedButton;
+- (void)updateAppearanceForButton:(TSQCalendarDayButton *)button
+{
+ UIColor *dateColor = nil;
+ UIColor *disabledDateColor = nil;
+ UIColor *dateShadowColor = nil;
+
+ NSDate *date = button.day;
+
+ BOOL dateIsSelectable = YES;
+ if ([self.calendarView.delegate respondsToSelector:@selector(calendarView:shouldSelectDate:)]) {
+ dateIsSelectable = [self.calendarView.delegate calendarView:self.calendarView shouldSelectDate:date];
+ }
+
+ // ** DISABLED DATE COLOR **/
+
+ if ([self.calendarView.delegate respondsToSelector:@selector(calendarView:disabledDateColorForDate:)]) {
+ // prefer the delegate disabledDate color over everything else; this will
+ // always be used if the delegate returns a color (except for other month
+ // buttons)
+ disabledDateColor = [self.calendarView.delegate calendarView:self.calendarView disabledDateColorForDate:date];
+ }
+
+ // if the delegate doesn't return a disabled date color, fall back to a sane
+ // default. Other month buttons will always get this disabled color.
+ if ((! disabledDateColor) || (button.type == CalendarButtonTypeOtherMonth))
+ {
+ disabledDateColor = [self.textColor colorWithAlphaComponent:0.5f];
+ }
+
+ // ** DATE COLOR **/
+
+ // prefer the delegate date color over everything else; this will always be
+ // used if the delegate returns a color
+ UIColor *delegateDateColor = nil;
+ if ([self.calendarView.delegate respondsToSelector:@selector(calendarView:dateColorForDate:)]) {
+ delegateDateColor = [self.calendarView.delegate calendarView:self.calendarView dateColorForDate:date];
+ }
+
+ UIColor *delegateSelectedDateColor = nil;
+ if ([self.calendarView.delegate respondsToSelector:@selector(calendarView:selectedDateColorForDate:)]) {
+ delegateSelectedDateColor = [self.calendarView.delegate calendarView:self.calendarView selectedDateColorForDate:date];
+ }
+
+ // if the delegate doesn't return a date color, fall back to some sane defaults,
+ // which can still be overridden in subclasses
+ switch (button.type)
+ {
+ case CalendarButtonTypeNormalDay:
+ if (delegateDateColor) {
+ dateColor = delegateDateColor;
+ } else if ([button isForToday]) {
+ dateColor = [self todayTextColor];
+ } else {
+ dateColor = self.textColor;
+ }
+ break;
+
+ case CalendarButtonTypeOtherMonth:
+ dateColor = [self.textColor colorWithAlphaComponent:0.5f];
+ break;
+
+ case CalendarButtonTypeSelected:
+ if (delegateSelectedDateColor) {
+ dateColor = delegateSelectedDateColor;
+ } else {
+ dateColor = [self selectedTextColor];
+ }
+ break;
+
+ case CalendarButtonTypeInitialDay:
+ if (dateIsSelectable) {
+ if (delegateDateColor) {
+ dateColor = delegateDateColor;
+ } else if ([button isForToday]) {
+ dateColor = [self todayTextColor];
+ } else {
+ dateColor = [self initialDayTextColor];
+ }
+ } else {
+ dateColor = disabledDateColor;
+ }
+ }
+
+ // ** DATE SHADOW COLOR **/
+
+ // prefer the delegate date shadow color over everything else; this will
+ // always be used if the delegate returns a color
+ UIColor *delegateDateShadowColor = nil;
+ if ([self.calendarView.delegate respondsToSelector:@selector(calendarView:dateShadowColorForDate:)]) {
+ delegateDateShadowColor = [self.calendarView.delegate calendarView:self.calendarView dateShadowColorForDate:date];
+ }
+
+ switch (button.type)
+ {
+ case CalendarButtonTypeNormalDay:
+ if (delegateDateShadowColor) {
+ dateShadowColor = delegateDateShadowColor;
+ } else if ([button isForToday]) {
+ dateShadowColor = [self todayTextShadowColor];
+ } else {
+ dateShadowColor = [self textShadowColor];
+ }
+ break;
+
+ case CalendarButtonTypeOtherMonth:
+ break;
+
+ case CalendarButtonTypeSelected:
+ dateShadowColor = [self selectedTextShadowColor];
+ break;
+
+ case CalendarButtonTypeInitialDay:
+ if (dateIsSelectable) {
+ if (delegateDateShadowColor) {
+ dateShadowColor = delegateDateShadowColor;
+ } else if ([button isForToday]) {
+ dateShadowColor = [self todayTextShadowColor];
+ } else {
+ dateShadowColor = [self initialDayTextShadowColor];
+ }
+ }
+ break;
+ }
+
+ [button setTitleColor:dateColor forState:UIControlStateNormal];
+ [button setTitleColor:disabledDateColor forState:UIControlStateDisabled];
+ [button setTitleShadowColor:dateShadowColor forState:UIControlStateNormal];
+
+ // ** ICON **/
+
+ UIImage *icon = nil;
+ UIColor *iconTintColor = nil;
+
+ if ([button isForToday] && button.type != CalendarButtonTypeOtherMonth)
+ {
+ icon = [self todayIcon];
+
+ if (button.type == CalendarButtonTypeSelected)
+ {
+ // when selected, tint the icon the same as selected text
+ icon = [icon imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
+ iconTintColor = dateColor;
+ }
+ }
+ // can extend later to support other icons
+ button.iconImageView.image = icon;
+ button.iconImageView.tintColor = iconTintColor;
+}
+
+- (void)updateBackgroundImageForButton:(TSQCalendarDayButton *)button isSelected:(BOOL)isSelected
{
- self.selectedButton = [[UIButton alloc] initWithFrame:self.contentView.bounds];
- [self.contentView addSubview:self.selectedButton];
- [self configureButton:self.selectedButton];
+ NSDate *date = button.day;
- [self.selectedButton setAccessibilityTraits:UIAccessibilityTraitSelected|self.selectedButton.accessibilityTraits];
+ UIImage *delegateBackgroundImage = nil;
+ if ([self.calendarView.delegate respondsToSelector:@selector(calendarView:backgroundImageForDate:size:isInThisMonth:isSelected:)]) {
+ BOOL thisMonth = button.type != CalendarButtonTypeOtherMonth;
+
+ delegateBackgroundImage = [self.calendarView.delegate calendarView:self.calendarView backgroundImageForDate:date size:button.bounds.size isInThisMonth:thisMonth isSelected:isSelected];
+ }
- self.selectedButton.enabled = NO;
- [self.selectedButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
- [self.selectedButton setBackgroundImage:[self selectedBackgroundImage] forState:UIControlStateNormal];
- [self.selectedButton setTitleShadowColor:[UIColor colorWithWhite:0.0f alpha:0.75f] forState:UIControlStateNormal];
+
+ [button setBackgroundImage:delegateBackgroundImage forState:UIControlStateNormal];
+}
+
+- (void)updateTitleForButton:(TSQCalendarDayButton *)button
+{
+ NSDate *date = button.day;
+
+ if (date == nil) {
+ return;
+ }
+
+ [self updateBackgroundImageForButton:button isSelected:[self.calendarView.selectedDate isEqualToDate:button.day]];
+ NSString *title = [self.dayFormatter stringFromDate:date];
+ [button setTitle:title forState:UIControlStateNormal];
+ [button setTitle:title forState:UIControlStateDisabled];
+
+ // add accessibility label
+ NSString *accessibilityLabel = [self.accessibilityFormatter stringFromDate:date];
+ if (button.type == 1) {
+ [button setAccessibilityLabel:[NSString stringWithFormat:@"%@ Disabled", accessibilityLabel]];
+ } else {
+ [button setAccessibilityLabel:accessibilityLabel];
+ }
+
+ // check if we should use an attributed string
+ NSDictionary *additionalAttributes = nil;
+ if ([self.calendarView.delegate respondsToSelector:@selector(calendarView:additionalDateTextAttributesForDate:)]) {
+ additionalAttributes = [self.calendarView.delegate calendarView:self.calendarView additionalDateTextAttributesForDate:date];
+ }
+
+ if (additionalAttributes) {
+ // create text attributes for normal button
+ NSMutableDictionary *normalAttributes = [additionalAttributes mutableCopy];
+
+ if ([button titleColorForState:UIControlStateNormal]) {
+ normalAttributes[NSForegroundColorAttributeName] = [button titleColorForState:UIControlStateNormal];
+ }
+
+ if ([button titleShadowColorForState:UIControlStateNormal]) {
+ NSShadow *shadow = [NSShadow new];
+ shadow.shadowOffset = button.titleLabel.shadowOffset;
+ shadow.shadowColor = [button titleShadowColorForState:UIControlStateNormal];
+ normalAttributes[NSShadowAttributeName] = shadow;
+ }
+
+ // update button title with normal attributes
+ NSAttributedString *normalTitle = [[NSAttributedString alloc] initWithString:title attributes:normalAttributes];
+ [button setAttributedTitle:normalTitle forState:UIControlStateNormal];
+
+ // create text attributes for disabled button
+ NSMutableDictionary *disabledAttributes = [normalAttributes mutableCopy];
+
+ if ([button titleColorForState:UIControlStateDisabled]) {
+ disabledAttributes[NSForegroundColorAttributeName] = [button titleColorForState:UIControlStateDisabled];
+ }
+
+ if ([button titleShadowColorForState:UIControlStateDisabled]) {
+ NSShadow *shadow = [NSShadow new];
+ shadow.shadowOffset = button.titleLabel.shadowOffset;
+ shadow.shadowColor = [button titleShadowColorForState:UIControlStateDisabled];
+ disabledAttributes[NSShadowAttributeName] = shadow;
+ }
+
+ // update button title with normal attributes
+ NSAttributedString *disabledTitle = [[NSAttributedString alloc] initWithString:title attributes:disabledAttributes];
+ [button setAttributedTitle:disabledTitle forState:UIControlStateDisabled];
+ }
+}
+
+- (void)updateSubtitlesForButton:(TSQCalendarDayButton *)button
+{
+ NSDate *date = button.day;
+
+ NSString *subtitle = nil;
+ NSString *subtitleSymbol = nil;
+ UIColor *subtitleColor = nil;
+
+ BOOL dateIsSelectable = YES;
+ if ([self.calendarView.delegate respondsToSelector:@selector(calendarView:shouldSelectDate:)]) {
+ dateIsSelectable = [self.calendarView.delegate calendarView:self.calendarView shouldSelectDate:date];
+ }
+
+ // ** DISABLED DATE COLOR **/
+ UIColor *disabledDateColor = nil;
+ if ([self.calendarView.delegate respondsToSelector:@selector(calendarView:disabledDateColorForDate:)]) {
+ // prefer the delegate disabledDate color over everything else; this will
+ // always be used if the delegate returns a color (except for other month
+ // buttons)
+ disabledDateColor = [self.calendarView.delegate calendarView:self.calendarView disabledDateColorForDate:date];
+ }
+
+ // if the delegate doesn't return a disabled date color, fall back to a sane
+ // default. Other month buttons will always get this disabled color.
+ if ((! disabledDateColor) || (button.type == CalendarButtonTypeOtherMonth))
+ {
+ disabledDateColor = [self.textColor colorWithAlphaComponent:0.5f];
+ }
- self.selectedButton.titleLabel.shadowOffset = CGSizeMake(0.0f, -1.0f / [UIScreen mainScreen].scale);
- self.indexOfSelectedButton = -1;
+ if ([self.calendarView.delegate respondsToSelector:@selector(calendarView:subtitleForDate:)])
+ {
+ subtitle = [self.calendarView.delegate calendarView:self.calendarView subtitleForDate:date];
+
+ // only check the color if the delegate also responds to the subtitle
+ // delegate method. Prefer this subtitle color returned by the delegate,
+ // except for other month buttons.
+ UIColor *delegateSubtitleColor = nil;
+ if ([self.calendarView.delegate respondsToSelector:@selector(calendarView:subtitleColorForDate:)]) {
+ delegateSubtitleColor = [self.calendarView.delegate calendarView:self.calendarView subtitleColorForDate:date];
+ }
+
+ switch (button.type)
+ {
+ case CalendarButtonTypeNormalDay:
+ if (delegateSubtitleColor) {
+ subtitleColor = delegateSubtitleColor;
+ } else if ([button isForToday]) {
+ subtitleColor = [self todaySubtitleTextColor];
+ } else {
+ subtitleColor = [self subtitleTextColor];
+ }
+ break;
+
+ case CalendarButtonTypeOtherMonth:
+ // prefer a disabled color for other month buttons, even if the delegate
+ // returned a color
+ subtitleColor = disabledDateColor;
+ break;
+
+ case CalendarButtonTypeSelected:
+ subtitleColor = [self selectedSubtitleTextColor];
+ break;
+
+ case CalendarButtonTypeInitialDay:
+ if (dateIsSelectable) {
+ if (delegateSubtitleColor) {
+ subtitleColor = delegateSubtitleColor;
+ } else if ([button isForToday]) {
+ subtitleColor = [self todaySubtitleTextColor];
+ } else {
+ subtitleColor = [self initialDaySubtitleTextColor];
+ }
+ } else {
+ subtitleColor = disabledDateColor;
+ }
+ break;
+ }
+
+ if ([self.calendarView.delegate respondsToSelector:@selector(calendarView:subtitleTrailingSymbolForDate:)])
+ {
+ subtitleSymbol = [self.calendarView.delegate calendarView:self.calendarView subtitleTrailingSymbolForDate:date];
+ }
+ }
+
+ button.tsqSubtitleLabel.text = subtitle;
+ button.subtitleSymbolLabel.text = subtitleSymbol;
+ button.tsqSubtitleLabel.textColor = subtitleColor;
+ button.subtitleSymbolLabel.textColor = subtitleColor;
}
- (void)setBeginningDate:(NSDate *)date;
@@ -120,49 +494,54 @@ - (void)setBeginningDate:(NSDate *)date;
if (!self.dayButtons) {
[self createDayButtons];
[self createNotThisMonthButtons];
- [self createTodayButton];
[self createSelectedButton];
}
NSDateComponents *offset = [NSDateComponents new];
offset.day = 1;
- self.todayButton.hidden = YES;
- self.indexOfTodayButton = -1;
- self.selectedButton.hidden = YES;
- self.indexOfSelectedButton = -1;
-
for (NSUInteger index = 0; index < self.daysInWeek; index++) {
- NSString *title = [self.dayFormatter stringFromDate:date];
- NSString *accessibilityLabel = [self.accessibilityFormatter stringFromDate:date];
- [self.dayButtons[index] setTitle:title forState:UIControlStateNormal];
- [self.dayButtons[index] setAccessibilityLabel:accessibilityLabel];
- [self.notThisMonthButtons[index] setTitle:title forState:UIControlStateNormal];
- [self.notThisMonthButtons[index] setTitle:title forState:UIControlStateDisabled];
- [self.notThisMonthButtons[index] setAccessibilityLabel:accessibilityLabel];
-
- NSDateComponents *thisDateComponents = [self.calendar components:NSDayCalendarUnit|NSMonthCalendarUnit|NSYearCalendarUnit fromDate:date];
+
+ TSQCalendarDayButton *currentDayButton = self.dayButtons[index];
+ TSQCalendarDayButton *currentNotThisMonthButton = self.notThisMonthButtons[index];
+ currentDayButton.day = date;
+ currentNotThisMonthButton.day = date;
+
+ NSDateComponents *thisDateComponents = [self.calendar components:NSCalendarUnitDay|NSCalendarUnitMonth|NSCalendarUnitYear fromDate:date];
- [self.dayButtons[index] setHidden:YES];
- [self.notThisMonthButtons[index] setHidden:YES];
+ [currentDayButton setHidden:YES];
+ [currentNotThisMonthButton setHidden:YES];
NSInteger thisDayMonth = thisDateComponents.month;
- if (self.monthOfBeginningDate != thisDayMonth) {
- [self.notThisMonthButtons[index] setHidden:NO];
- } else {
-
- if ([self.todayDateComponents isEqual:thisDateComponents]) {
- self.todayButton.hidden = NO;
- [self.todayButton setTitle:title forState:UIControlStateNormal];
- [self.todayButton setAccessibilityLabel:accessibilityLabel];
- self.indexOfTodayButton = index;
- } else {
- UIButton *button = self.dayButtons[index];
- button.enabled = ![self.calendarView.delegate respondsToSelector:@selector(calendarView:shouldSelectDate:)] || [self.calendarView.delegate calendarView:self.calendarView shouldSelectDate:date];
- button.hidden = NO;
+ if (self.monthOfBeginningDate != thisDayMonth)
+ {
+ [currentNotThisMonthButton setHidden:NO];
+ }
+ else
+ {
+ BOOL buttonEnabled = YES;
+ if ([self.calendarView.delegate respondsToSelector:@selector(calendarView:shouldSelectDate:)])
+ {
+ buttonEnabled = [self.calendarView.delegate calendarView:self.calendarView shouldSelectDate:date];
}
+
+ UIButton *button = self.dayButtons[index];
+ button.enabled = buttonEnabled;
+ button.hidden = NO;
}
+ // update button appearance
+ [self updateAppearanceForButton:currentDayButton];
+ [self updateAppearanceForButton:currentNotThisMonthButton];
+
+ // update button title
+ [self updateTitleForButton:currentDayButton];
+ [self updateTitleForButton:currentNotThisMonthButton];
+
+ // update button subtitles
+ [self updateSubtitlesForButton:currentDayButton];
+ [self updateSubtitlesForButton:currentNotThisMonthButton];
+
date = [self.calendar dateByAddingComponents:offset toDate:date options:0];
}
}
@@ -183,18 +562,10 @@ - (void)setBottomRow:(BOOL)bottomRow;
- (IBAction)dateButtonPressed:(id)sender;
{
- NSDateComponents *offset = [NSDateComponents new];
- offset.day = [self.dayButtons indexOfObject:sender];
- NSDate *selectedDate = [self.calendar dateByAddingComponents:offset toDate:self.beginningDate options:0];
- self.calendarView.selectedDate = selectedDate;
-}
-
-- (IBAction)todayButtonPressed:(id)sender;
-{
- NSDateComponents *offset = [NSDateComponents new];
- offset.day = self.indexOfTodayButton;
- NSDate *selectedDate = [self.calendar dateByAddingComponents:offset toDate:self.beginningDate options:0];
+ TSQCalendarDayButton *dayButton = (TSQCalendarDayButton *)sender;
+ NSDate *selectedDate = dayButton.day;
self.calendarView.selectedDate = selectedDate;
+ [self updateBackgroundImageForButton:dayButton isSelected:YES];
}
- (void)layoutSubviews;
@@ -205,26 +576,60 @@ - (void)layoutSubviews;
[super layoutSubviews];
- self.backgroundView.frame = self.bounds;
+ // Size the background view with horizontal insets
+ CGRect bounds = self.bounds;
+ UIEdgeInsets insets = self.calendarView.contentInset;
+ CGRect insetRect = UIEdgeInsetsInsetRect(bounds, insets);
+ insetRect.origin.y = bounds.origin.y;
+ insetRect.size.height = bounds.size.height;
+ self.backgroundView.frame = insetRect;
}
- (void)layoutViewsForColumnAtIndex:(NSUInteger)index inRect:(CGRect)rect;
{
- UIButton *dayButton = self.dayButtons[index];
- UIButton *notThisMonthButton = self.notThisMonthButtons[index];
-
- dayButton.frame = rect;
- notThisMonthButton.frame = rect;
-
- if (self.indexOfTodayButton == (NSInteger)index) {
- self.todayButton.frame = rect;
+ // find buttons that we need to update the frame
+ NSMutableArray *buttons = [NSMutableArray new];
+ if (index < self.dayButtons.count) {
+ [buttons addObject:self.dayButtons[index]];
}
- if (self.indexOfSelectedButton == (NSInteger)index) {
- self.selectedButton.frame = rect;
+ if (index < self.notThisMonthButtons.count) {
+ [buttons addObject:self.notThisMonthButtons[index]];
+ }
+ if (self.indexOfSelectedButton == (NSInteger)index && self.selectedButton) {
+ [buttons addObject:self.selectedButton];
+ }
+
+ for (TSQCalendarDayButton *button in buttons) {
+ if (CGRectEqualToRect(button.frame, rect) == NO) {
+ button.frame = rect;
+ // image views are dependant on button size so they need to be regenerated
+ [self updateBackgroundImageForButton:button isSelected:[self.calendarView.selectedDate isEqualToDate:button.day]];
+ }
}
}
- (void)selectColumnForDate:(NSDate *)date;
+{
+ [self selectColumnForDate:date isInitialDay:NO];
+}
+
+- (void)deselectColumnForDate:(NSDate *)date
+{
+ for (TSQCalendarDayButton *button in self.dayButtons) {
+ if ([button.day isEqualToDate:date])
+ {
+ [self updateBackgroundImageForButton:button isSelected:NO];
+ break;
+ }
+ }
+}
+
+- (void)selectColumnForInitialDate:(NSDate *)date
+{
+ [self selectColumnForDate:date isInitialDay:YES];
+}
+
+- (void)selectColumnForDate:(NSDate *)date isInitialDay:(BOOL)isInitialDay
{
if (!date && self.indexOfSelectedButton == -1) {
return;
@@ -232,9 +637,9 @@ - (void)selectColumnForDate:(NSDate *)date;
NSInteger newIndexOfSelectedButton = -1;
if (date) {
- NSInteger thisDayMonth = [self.calendar components:NSMonthCalendarUnit fromDate:date].month;
+ NSInteger thisDayMonth = [self.calendar components:NSCalendarUnitMonth fromDate:date].month;
if (self.monthOfBeginningDate == thisDayMonth) {
- newIndexOfSelectedButton = [self.calendar components:NSDayCalendarUnit fromDate:self.beginningDate toDate:date options:0].day;
+ newIndexOfSelectedButton = [self.calendar components:NSCalendarUnitDay fromDate:self.beginningDate toDate:date options:0].day;
if (newIndexOfSelectedButton >= (NSInteger)self.daysInWeek) {
newIndexOfSelectedButton = -1;
}
@@ -244,15 +649,29 @@ - (void)selectColumnForDate:(NSDate *)date;
self.indexOfSelectedButton = newIndexOfSelectedButton;
if (newIndexOfSelectedButton >= 0) {
+ // update selected button colors
+ TSQCalendarDayButton *dayButton = self.dayButtons[newIndexOfSelectedButton];
+ self.selectedButton.day = dayButton.day;
+ self.selectedButton.type = isInitialDay ? CalendarButtonTypeInitialDay : CalendarButtonTypeSelected;
+ self.selectedButton.enabled = isInitialDay;
+ self.selectedButton.isInitialDay = isInitialDay;
+ [self updateAppearanceForButton:self.selectedButton];
+ [self updateSubtitlesForButton:self.selectedButton];
+
+ // update background image
+ [self updateBackgroundImageForButton:self.selectedButton isSelected:NO];
+
+ // update selected button text
self.selectedButton.hidden = NO;
- NSString *newTitle = [self.dayButtons[newIndexOfSelectedButton] currentTitle];
- [self.selectedButton setTitle:newTitle forState:UIControlStateNormal];
- [self.selectedButton setTitle:newTitle forState:UIControlStateDisabled];
- [self.selectedButton setAccessibilityLabel:[self.dayButtons[newIndexOfSelectedButton] accessibilityLabel]];
+ self.selectedButton.enabled = YES;
+ [self updateTitleForButton:self.selectedButton];
+ self.selectedButton.tsqSubtitleLabel.text = dayButton.tsqSubtitleLabel.text;
+ self.selectedButton.subtitleSymbolLabel.text = dayButton.subtitleSymbolLabel.text;
} else {
self.selectedButton.hidden = YES;
+ self.selectedButton.enabled = NO;
}
-
+
[self setNeedsLayout];
}
@@ -279,7 +698,7 @@ - (NSDateFormatter *)accessibilityFormatter;
- (NSInteger)monthOfBeginningDate;
{
if (!_monthOfBeginningDate) {
- _monthOfBeginningDate = [self.calendar components:NSMonthCalendarUnit fromDate:self.firstOfMonth].month;
+ _monthOfBeginningDate = [self.calendar components:NSCalendarUnitMonth fromDate:self.firstOfMonth].month;
}
return _monthOfBeginningDate;
}
@@ -290,12 +709,4 @@ - (void)setFirstOfMonth:(NSDate *)firstOfMonth;
self.monthOfBeginningDate = 0;
}
-- (NSDateComponents *)todayDateComponents;
-{
- if (!_todayDateComponents) {
- self.todayDateComponents = [self.calendar components:NSDayCalendarUnit|NSMonthCalendarUnit|NSYearCalendarUnit fromDate:[NSDate date]];
- }
- return _todayDateComponents;
-}
-
@end
diff --git a/TimesSquare/TSQCalendarView.h b/TimesSquare/TSQCalendarView.h
index 3a1c025..2dd54ff 100644
--- a/TimesSquare/TSQCalendarView.h
+++ b/TimesSquare/TSQCalendarView.h
@@ -42,6 +42,15 @@
*/
@property (nonatomic, strong) NSDate *selectedDate;
+
+/** The initial date to be highlighted on the calendar.
+
+ Set this property to any `NSDate`; `TSQCalendarView` will only look at the month, day, and year.
+ You can read and write this property
+ */
+
+@property (nonatomic, strong) NSDate *initialDate;
+
/** @name Calendar Configuration */
/** The calendar type to use when displaying.
@@ -83,6 +92,12 @@
*/
@property (nonatomic) CGPoint contentOffset;
+/** The size of the calendar content view
+
+ This property is equivalent to the one defined on `UIScrollView`.
+ */
+@property (nonatomic) CGSize contentSize;
+
/** The cell class to use for month headers.
Since there's very little configuration to be done for each cell, this can be set as a shortcut to implementing a data source.
@@ -97,6 +112,14 @@
*/
@property (nonatomic, strong) Class rowCellClass;
+// Returns and modifies the scrollEnabled on the table view
+
+@property (nonatomic) BOOL scrollEnabled;
+
+// Returns and modifies the showsVerticalScrollIndicator on the table view
+
+@property (nonatomic) BOOL showsVerticalScrollIndicator;
+
/** Scrolls the receiver until the specified date month is completely visible.
@param date A date that identifies the month that will be visible.
@@ -134,6 +157,24 @@
*/
- (BOOL)calendarView:(TSQCalendarView *)calendarView shouldSelectDate:(NSDate *)date;
+- (NSString*)calendarView: (TSQCalendarView *)calendarView subtitleForDate: (NSDate*) date;
+
+- (NSString*)calendarView: (TSQCalendarView *)calendarView subtitleTrailingSymbolForDate: (NSDate*) date;
+
+- (UIColor*)calendarView: (TSQCalendarView *)calendarView dateColorForDate: (NSDate*) date;
+
+- (UIColor*)calendarView: (TSQCalendarView *)calendarView dateShadowColorForDate: (NSDate*) date;
+
+- (UIColor*)calendarView: (TSQCalendarView *)calendarView disabledDateColorForDate: (NSDate*) date;
+
+- (UIColor*)calendarView: (TSQCalendarView *)calendarView subtitleColorForDate: (NSDate*) date;
+
+- (UIColor*)calendarView: (TSQCalendarView *)calendarView selectedDateColorForDate: (NSDate*) date;
+
+- (UIImage*)calendarView: (TSQCalendarView *)calendarView backgroundImageForDate: (NSDate*) date size:(CGSize)size isInThisMonth:(BOOL)thisMonth isSelected:(BOOL)isSelected;
+
+- (NSDictionary*)calendarView: (TSQCalendarView *)calendarView additionalDateTextAttributesForDate: (NSDate*) date;
+
/** Tells the delegate that a particular date was selected.
@param calendarView The calendar view that is selecting a date.
diff --git a/TimesSquare/TSQCalendarView.m b/TimesSquare/TSQCalendarView.m
index 7bd565f..5c499f5 100644
--- a/TimesSquare/TSQCalendarView.m
+++ b/TimesSquare/TSQCalendarView.m
@@ -52,7 +52,12 @@ - (void)_TSQCalendarView_commonInit;
_tableView.delegate = self;
_tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
_tableView.autoresizingMask = UIViewAutoresizingFlexibleHeight|UIViewAutoresizingFlexibleWidth;
- [self addSubview:_tableView];
+
+ // WORKAROUND: In iOS8, scrolling randomly doesn't work in the calendar view when dragging on enabled buttons.
+ // The correct solution is to enable delaysContentTouches but this has been broken since iOS7: ( https://devforums.apple.com/thread/199755?start=0&tstart=0 ).
+ // The workaround here is to set delaysTouchesBegan on the panGestureRecognizer of the table itself.
+ _tableView.panGestureRecognizer.delaysTouchesBegan = YES;
+ [self addSubview:_tableView];
}
- (void)dealloc;
@@ -85,15 +90,6 @@ - (Class)rowCellClass;
return _rowCellClass;
}
-- (Class)cellClassForRowAtIndexPath:(NSIndexPath *)indexPath;
-{
- if (indexPath.row == 0 && !self.pinsHeaderToTop) {
- return [self headerCellClass];
- } else {
- return [self rowCellClass];
- }
-}
-
- (void)setBackgroundColor:(UIColor *)backgroundColor;
{
[super setBackgroundColor:backgroundColor];
@@ -109,13 +105,13 @@ - (void)setPinsHeaderToTop:(BOOL)pinsHeaderToTop;
- (void)setFirstDate:(NSDate *)firstDate;
{
// clamp to the beginning of its month
- _firstDate = [self clampDate:firstDate toComponents:NSMonthCalendarUnit|NSYearCalendarUnit];
+ _firstDate = [self clampDate:firstDate toComponents:NSCalendarUnitMonth|NSCalendarUnitYear];
}
- (void)setLastDate:(NSDate *)lastDate;
{
// clamp to the end of its month
- NSDate *firstOfMonth = [self clampDate:lastDate toComponents:NSMonthCalendarUnit|NSYearCalendarUnit];
+ NSDate *firstOfMonth = [self clampDate:lastDate toComponents:NSCalendarUnitMonth|NSCalendarUnitYear];
NSDateComponents *offsetComponents = [[NSDateComponents alloc] init];
offsetComponents.month = 1;
@@ -126,15 +122,62 @@ - (void)setLastDate:(NSDate *)lastDate;
- (void)setSelectedDate:(NSDate *)newSelectedDate;
{
// clamp to beginning of its day
- NSDate *startOfDay = [self clampDate:newSelectedDate toComponents:NSDayCalendarUnit|NSMonthCalendarUnit|NSYearCalendarUnit];
+ NSDate *startOfDay = [self clampDate:newSelectedDate toComponents:NSCalendarUnitDay|NSCalendarUnitMonth|NSCalendarUnitYear];
if ([self.delegate respondsToSelector:@selector(calendarView:shouldSelectDate:)] && ![self.delegate calendarView:self shouldSelectDate:startOfDay]) {
return;
}
+ [self updateSelectedDate:startOfDay isInitialDate:NO];
+
+ _selectedDate = startOfDay;
+
+ if (startOfDay == nil && self.initialDate != nil) {
+ [self updateSelectedDate:self.initialDate isInitialDate:YES];
+ }
+
+ if ([self.delegate respondsToSelector:@selector(calendarView:didSelectDate:)]) {
+ [self.delegate calendarView:self didSelectDate:startOfDay];
+ }
+}
+
+- (void)setInitialDate:(NSDate *)initialDate
+{
+ // clamp to beginning of its day
+ NSDate *startOfDay = [self clampDate:initialDate toComponents:NSCalendarUnitDay|NSCalendarUnitMonth|NSCalendarUnitYear];
+
+ if (self.selectedDate == nil) {
+ // only show initial date if user hasn't already selected a date
+ [self updateSelectedDate:startOfDay isInitialDate:YES];
+ }
+
+ _initialDate = startOfDay;
+}
+
+- (void)updateSelectedDate:(NSDate *)date isInitialDate:(BOOL)isInitialDate
+{
+ [[self cellForRowAtDate:_selectedDate] deselectColumnForDate:self.selectedDate];
+ // clear existing selected cells
[[self cellForRowAtDate:_selectedDate] selectColumnForDate:nil];
- [[self cellForRowAtDate:startOfDay] selectColumnForDate:startOfDay];
- NSIndexPath *newIndexPath = [self indexPathForRowAtDate:startOfDay];
+ [[self cellForRowAtDate:_initialDate] selectColumnForDate:nil];
+
+ // update new selected cell
+ TSQCalendarRowCell *dateRowCell = [self cellForRowAtDate:date];
+ if (dateRowCell == nil) {
+ return;
+ }
+
+ if (isInitialDate) {
+ [dateRowCell selectColumnForInitialDate:date];
+ } else {
+ [dateRowCell selectColumnForDate:date];
+ }
+
+ NSIndexPath *newIndexPath = [self indexPathForRowAtDate:date];
+ if (newIndexPath == nil) {
+ return;
+ }
+
CGRect newIndexPathRect = [self.tableView rectForRowAtIndexPath:newIndexPath];
CGRect scrollBounds = self.tableView.bounds;
@@ -148,12 +191,16 @@ - (void)setSelectedDate:(NSDate *)newSelectedDate;
[self.tableView scrollToRowAtIndexPath:newIndexPath atScrollPosition:UITableViewScrollPositionBottom animated:YES];
}
}
-
- _selectedDate = startOfDay;
-
- if ([self.delegate respondsToSelector:@selector(calendarView:didSelectDate:)]) {
- [self.delegate calendarView:self didSelectDate:startOfDay];
- }
+}
+
+- (BOOL)scrollEnabled
+{
+ return self.tableView.scrollEnabled;
+}
+
+- (void)setScrollEnabled:(BOOL)scrollEnabled
+{
+ self.tableView.scrollEnabled = scrollEnabled;
}
- (void)scrollToDate:(NSDate *)date animated:(BOOL)animated
@@ -182,7 +229,17 @@ - (TSQCalendarMonthHeaderCell *)makeHeaderCellWithIdentifier:(NSString *)identif
return cell;
}
-#pragma mark Calendar calculations
+- (void)setShowsVerticalScrollIndicator:(BOOL)showsVerticalScrollIndicator
+{
+ self.tableView.showsVerticalScrollIndicator = showsVerticalScrollIndicator;
+}
+
+- (BOOL)showsVerticalScrollIndicator
+{
+ return self.tableView.showsVerticalScrollIndicator;
+}
+
+#pragma mark - Calendar calculations
- (NSDate *)firstOfMonthForSection:(NSInteger)section;
{
@@ -198,7 +255,7 @@ - (TSQCalendarRowCell *)cellForRowAtDate:(NSDate *)date;
- (NSInteger)sectionForDate:(NSDate *)date;
{
- return [self.calendar components:NSMonthCalendarUnit fromDate:self.firstDate toDate:date options:0].month;
+ return [self.calendar components:NSCalendarUnitMonth fromDate:self.firstDate toDate:date options:0].month;
}
- (NSIndexPath *)indexPathForRowAtDate:(NSDate *)date;
@@ -210,13 +267,13 @@ - (NSIndexPath *)indexPathForRowAtDate:(NSDate *)date;
NSInteger section = [self sectionForDate:date];
NSDate *firstOfMonth = [self firstOfMonthForSection:section];
- NSInteger firstWeek = [self.calendar components:NSWeekOfMonthCalendarUnit fromDate:firstOfMonth].weekOfMonth;
- NSInteger targetWeek = [self.calendar components:NSWeekOfMonthCalendarUnit fromDate:date].weekOfMonth;
+ NSInteger firstWeek = [self.calendar components:NSCalendarUnitWeekOfMonth fromDate:firstOfMonth].weekOfMonth;
+ NSInteger targetWeek = [self.calendar components:NSCalendarUnitWeekOfMonth fromDate:date].weekOfMonth;
- return [NSIndexPath indexPathForRow:(self.pinsHeaderToTop ? 0 : 1) + targetWeek - firstWeek inSection:section];
+ return [NSIndexPath indexPathForRow:targetWeek - firstWeek inSection:section];
}
-#pragma mark UIView
+#pragma mark - UIView
- (void)layoutSubviews;
{
@@ -245,67 +302,87 @@ - (void)layoutSubviews;
}
}
-#pragma mark UITableViewDataSource
+#pragma mark - UITableViewDataSource
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView;
{
- return 1 + [self.calendar components:NSMonthCalendarUnit fromDate:self.firstDate toDate:self.lastDate options:0].month;
+ return 1 + [self.calendar components:NSCalendarUnitMonth fromDate:self.firstDate toDate:self.lastDate options:0].month;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section;
{
NSDate *firstOfMonth = [self firstOfMonthForSection:section];
- NSRange rangeOfWeeks = [self.calendar rangeOfUnit:NSWeekCalendarUnit inUnit:NSMonthCalendarUnit forDate:firstOfMonth];
- return (self.pinsHeaderToTop ? 0 : 1) + rangeOfWeeks.length;
+
+ // using the new calendar units, even though they are available in iOS 7.0, don't actually work for this calculation
+ // on iOS 7. So we can't use the new units here.
+ NSRange rangeOfWeeks = [self.calendar rangeOfUnit:NSCalendarUnitWeekOfMonth inUnit:NSCalendarUnitMonth forDate:firstOfMonth];
+ return rangeOfWeeks.length;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath;
{
- if (indexPath.row == 0 && !self.pinsHeaderToTop) {
- // month header
- static NSString *identifier = @"header";
- TSQCalendarMonthHeaderCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier];
- if (!cell) {
- cell = [self makeHeaderCellWithIdentifier:identifier];
- }
- return cell;
- } else {
- static NSString *identifier = @"row";
- TSQCalendarRowCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier];
- if (!cell) {
- cell = [[[self rowCellClass] alloc] initWithCalendar:self.calendar reuseIdentifier:identifier];
- cell.backgroundColor = self.backgroundColor;
- cell.calendarView = self;
- }
- return cell;
+ static NSString *identifier = @"row";
+ TSQCalendarRowCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier];
+ if (!cell) {
+ cell = [[[self rowCellClass] alloc] initWithCalendar:self.calendar reuseIdentifier:identifier];
+ cell.backgroundColor = self.backgroundColor;
+ cell.calendarView = self;
}
+ return cell;
}
-#pragma mark UITableViewDelegate
+#pragma mark - UITableViewDelegate
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath;
{
NSDate *firstOfMonth = [self firstOfMonthForSection:indexPath.section];
[(TSQCalendarCell *)cell setFirstOfMonth:firstOfMonth];
- if (indexPath.row > 0 || self.pinsHeaderToTop) {
- NSInteger ordinalityOfFirstDay = [self.calendar ordinalityOfUnit:NSDayCalendarUnit inUnit:NSWeekCalendarUnit forDate:firstOfMonth];
- NSDateComponents *dateComponents = [NSDateComponents new];
- dateComponents.day = 1 - ordinalityOfFirstDay;
- dateComponents.week = indexPath.row - (self.pinsHeaderToTop ? 0 : 1);
- [(TSQCalendarRowCell *)cell setBeginningDate:[self.calendar dateByAddingComponents:dateComponents toDate:firstOfMonth options:0]];
+ NSInteger ordinalityOfFirstDay = [self.calendar ordinalityOfUnit:NSCalendarUnitDay inUnit:NSCalendarUnitWeekOfMonth forDate:firstOfMonth];
+ NSDateComponents *dateComponents = [NSDateComponents new];
+ dateComponents.day = 1 - ordinalityOfFirstDay;
+ dateComponents.weekOfMonth = indexPath.row;
+ [(TSQCalendarRowCell *)cell setBeginningDate:[self.calendar dateByAddingComponents:dateComponents toDate:firstOfMonth options:0]];
+
+ if (self.selectedDate) {
[(TSQCalendarRowCell *)cell selectColumnForDate:self.selectedDate];
-
- BOOL isBottomRow = (indexPath.row == [self tableView:tableView numberOfRowsInSection:indexPath.section] - (self.pinsHeaderToTop ? 0 : 1));
- [(TSQCalendarRowCell *)cell setBottomRow:isBottomRow];
+ } else if (self.initialDate) {
+ [(TSQCalendarRowCell *)cell selectColumnForInitialDate:self.initialDate];
}
+
+ BOOL isBottomRow = indexPath.row == [self tableView:tableView numberOfRowsInSection:indexPath.section] - 1;
+ [(TSQCalendarRowCell *)cell setBottomRow:isBottomRow];
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath;
{
- return [[self cellClassForRowAtIndexPath:indexPath] cellHeight];
+ return [[self rowCellClass] cellHeight];
+}
+
+- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section
+{
+ if (self.pinsHeaderToTop) {
+ return 0;
+ }
+ return [[self headerCellClass] cellHeight];
+}
+
+- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
+{
+ if (self.pinsHeaderToTop) {
+ return nil;
+ }
+
+ static NSString *identifier = @"header";
+ TSQCalendarMonthHeaderCell *cell = (TSQCalendarMonthHeaderCell *)[tableView dequeueReusableHeaderFooterViewWithIdentifier:identifier];
+ if (!cell) {
+ cell = [self makeHeaderCellWithIdentifier:identifier];
+ cell.firstOfMonth = [self firstOfMonthForSection:section];
+ }
+ return cell;
}
-#pragma mark UIScrollViewDelegate
+
+#pragma mark - UIScrollViewDelegate
- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset;
{
@@ -331,8 +408,18 @@ - (void)scrollViewDidScroll:(UIScrollView *)scrollView;
- (NSDate *)clampDate:(NSDate *)date toComponents:(NSUInteger)unitFlags
{
+ if (date == nil) {
+ // [__NSCFCalendar components:fromDate:]: date cannot be nil
+ return nil;
+ }
+
NSDateComponents *components = [self.calendar components:unitFlags fromDate:date];
return [self.calendar dateFromComponents:components];
}
+- (CGSize)contentSize
+{
+ return self.tableView.contentSize;
+}
+
@end