├── .gitignore ├── .travis.yml.bak ├── LICENSE ├── README.md ├── ZBJCalendar.podspec ├── ZBJCalendar.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcuserdata │ │ └── wanggang.xcuserdatad │ │ └── UserInterfaceState.xcuserstate ├── xcshareddata │ └── xcschemes │ │ └── ZBJCalendar.xcscheme └── xcuserdata │ └── wanggang.xcuserdatad │ ├── xcdebugger │ └── Breakpoints_v2.xcbkptlist │ └── xcschemes │ └── xcschememanagement.plist ├── ZBJCalendar ├── AppDelegate.h ├── AppDelegate.m ├── Assets.xcassets │ ├── AppIcon.appiconset │ │ ├── Contents.json │ │ ├── logo120.png │ │ └── logo180.png │ ├── Contents.json │ ├── cal_selected.imageset │ │ ├── Contents.json │ │ └── selected@2x.png │ ├── cal_selected_left.imageset │ │ ├── Contents.json │ │ └── left@2x.png │ ├── cal_selected_middle.imageset │ │ ├── Contents.json │ │ └── middle@2x.png │ ├── cal_selected_right.imageset │ │ ├── Contents.json │ │ └── right@2x.png │ ├── cancel.imageset │ │ ├── Contents.json │ │ └── 关闭.png │ ├── unav.imageset │ │ ├── Contents.json │ │ └── 可租.png │ └── unavaible.imageset │ │ ├── Contents.json │ │ └── 订单页.png ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard ├── CommonViews │ ├── ZBJCalendarSectionFooter.h │ ├── ZBJCalendarSectionFooter.m │ ├── ZBJCalendarSectionHeader.h │ └── ZBJCalendarSectionHeader.m ├── Info.plist ├── Model │ ├── ZBJCalendarDates.h │ ├── ZBJCalendarDates.m │ └── calendar_dates.json ├── MutipleSelection │ ├── ZBJMutipleSelectionSectionHeaderView.h │ ├── ZBJMutipleSelectionSectionHeaderView.m │ ├── ZBJSimpleMutipleSelectionCell.h │ ├── ZBJSimpleMutipleSelectionCell.m │ ├── ZBJSimpleMutipleSelectionViewController.h │ └── ZBJSimpleMutipleSelectionViewController.m ├── RangeSelection │ ├── complex │ │ ├── ZBJComplexRangeSelectionCell.h │ │ ├── ZBJComplexRangeSelectionCell.m │ │ ├── ZBJComplexRangeSelectionController.h │ │ └── ZBJComplexRangeSelectionController.m │ └── simple │ │ ├── ZBJSimpleRangeSelectionCell.h │ │ ├── ZBJSimpleRangeSelectionCell.m │ │ ├── ZBJSimpleRangeSelectionController.h │ │ └── ZBJSimpleRangeSelectionController.m ├── Show │ ├── ZBJCalendarShowCell.h │ ├── ZBJCalendarShowCell.m │ ├── ZBJCalendarShowView.h │ └── ZBJCalendarShowView.m ├── SingleSelection │ ├── UINavigationBar+ZBJAddition.h │ ├── UINavigationBar+ZBJAddition.m │ ├── ZBJSingleSelectionCell.h │ ├── ZBJSingleSelectionCell.m │ ├── ZBJSingleSelectionController.h │ ├── ZBJSingleSelectionController.m │ ├── ZBJSingleSelectionHeaderView.h │ └── ZBJSingleSelectionHeaderView.m ├── ViewController.h ├── ViewController.m ├── ZBJCalendar │ ├── NSDate+IndexPath.h │ ├── NSDate+IndexPath.m │ ├── NSDate+ZBJAddition.h │ ├── NSDate+ZBJAddition.m │ ├── ZBJCalendar.h │ ├── ZBJCalendarView.h │ ├── ZBJCalendarView.m │ ├── ZBJCalendarWeekView.h │ └── ZBJCalendarWeekView.m └── main.m ├── ZBJCalendarTests ├── Info.plist └── ZBJCalendarTests.m ├── ZBJCalendarUITests ├── Info.plist └── ZBJCalendarUITests.m ├── calendar_dates.json └── screenshots └── 00.png /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | build/ 4 | *.pbxuser 5 | !default.pbxuser 6 | *.mode1v3 7 | !default.mode1v3 8 | *.mode2v3 9 | !default.mode2v3 10 | *.perspectivev3 11 | !default.perspectivev3 12 | xcuserdata 13 | *.xccheckout 14 | *.moved-aside 15 | DerivedData 16 | *.hmap 17 | *.ipa 18 | *.xcuserstate 19 | 20 | # CocoaPods 21 | # 22 | # We recommend against adding the Pods directory to your .gitignore. However 23 | # you should judge for yourself, the pros and cons are mentioned at: 24 | # http://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control 25 | # 26 | #Pods/ -------------------------------------------------------------------------------- /.travis.yml.bak: -------------------------------------------------------------------------------- 1 | # language: objective-c #语言种类 2 | 3 | # xcode_project: ZBJCalendar.xcodeproj # 项目文件相对于.travis.yml文件的路径 4 | 5 | # xcode_scheme: ZBJCalendar #你需要集成测试的scheme 6 | 7 | language: objective-c 8 | osx_image: xcode7.3 9 | xcode_project: ZBJCalendar.xcodeproj 10 | xcode_scheme: ZBJCalendar 11 | xcode_sdk: iphonesimulator9.2 12 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Copyright (c) 2015-2016 gumpwangg 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | ## ZBJCalendar [![GitHub license](https://img.shields.io/badge/license-MIT-lightgrey.svg)](https://raw.githubusercontent.com/wanggang316/ZBJCalendar/master/LICENSE) [![Cocoapods](https://img.shields.io/cocoapods/v/ZBJCalendar.svg)](https://cocoapods.org/?q=zbjcALENDAR) [![Codewake](https://www.codewake.com/badges/ask_question.svg)](https://www.codewake.com/p/zbjcalendar) 3 | 4 | > ZBJCalendar is a simple way to create a calendar view. The concept of ZBJCalendar is UICollectionView or UITableView. 5 | 6 | > As we know, UICollectionView and UITableView set up the corresponding relations between cell and indexPath. Differently, ZBJCalendar set up the corresponding relations between cell and date, this is important to understand ZBJCalendar. 7 | 8 | ## Requirements 9 | 10 | * iOS7 or later 11 | 12 | ## Installation with CocoaPods 13 | `pod 'ZBJCalendar'` 14 | 15 | ## Installation with Carthage 16 | `github "wanggang316/ZBJCalendar"` 17 | 18 | ## Architecture 19 | 20 | ### ZBJCalendarView 21 | 22 | This is the main class of ZBJCalendar, an instance of this is a calendar view. 23 | 24 | You can find some familiar properties and new properties: 25 | 26 | * `dataSource` is `` protocol property which used to custom cells or headers 27 | * `delegate` is `` protocol property which used to handle the display and behaviour of the day cells. 28 | * `firstDate` and `lastDate` use to set the calendar's start date and end date. 29 | * `selectionMode` is a enum of `ZBJSelectionMode` which express the single selection or mutable selection or can't be select. 30 | 31 | 32 | Also, it contains some familiar methods like: 33 | * `- (void)registerCellClass:(id)clazz withReuseIdentifier:(NSString *)identifier;` 34 | * `- (void)reloadData;` 35 | * `- (id)cellAtDate:(NSDate *)date;` 36 | * `- (void)reloadCellsAtDates:(NSSet *)dates;` 37 | 38 | #### 39 | 40 | A protocol to custom the cell data model object. It supplies the cell and supplementary view's information about appearance and data. 41 | 42 | * `- (void)calendarView:(ZBJCalendarView *)calendarView configureCell:(id)cell forDate:(NSDate *)date;` 43 | * `- (void)calendarView:(ZBJCalendarView *)calendarView configureSectionHeaderView:(id)headerView firstDateOfMonth:(NSDate *)firstDateOfMonth;` 44 | * `- (void)calendarView:(ZBJCalendarView *)calendarView configureSectionFooterView:(id)headerView lastDateOfMonth:(NSDate *)lastDateOfMonth;` 45 | * `- (void)calendarView:(ZBJCalendarView *)calendarView configureWeekDayLabel:(UILabel *)dayLabel atWeekDay:(NSInteger)weekDay;` 46 | 47 | #### 48 | 49 | A protocol use to handle the display and behaviour of ZBJCalendar. 50 | 51 | * `- (BOOL)calendarView:(ZBJCalendarView *)calendarView shouldSelectDate:(NSDate *)date;` 52 | * `- (void)calendarView:(ZBJCalendarView *)calendarView didSelectDate:(NSDate *)date ofCell:(id)cell;` 53 | 54 | ### ZBJCalendarWeekView 55 | 56 | ZBJCalendar provider a week view by this class, you can change it's style by implement 57 | 58 | ### NSDate+ZBJAddition 59 | 60 | This category provide utility methods for `NSDate`. 61 | 62 | ### NSDate+IndexPath 63 | 64 | This category provide methods used to corresponding relations between indexpath and date. 65 | 66 | 67 | ## Examples 68 | 69 | ![show](./screenshots/00.png) 70 | 71 | ## License 72 | 73 | ZBJCalendar is released under the MIT license. 74 | -------------------------------------------------------------------------------- /ZBJCalendar.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | 3 | s.name = "ZBJCalendar" 4 | s.version = "0.0.9" 5 | s.summary = "ZBJCalendar is a simple calendar framework." 6 | 7 | s.homepage = "https://github.com/wanggang316/ZBJCalendar" 8 | s.license = "MIT" 9 | s.authors = { "Gump" => "gummpwang@gmail.com" } 10 | s.social_media_url = "https://twitter.com/wgang316" 11 | s.platform = :ios, "7.0" 12 | s.source = { :git => "https://github.com/wanggang316/ZBJCalendar.git", :tag => s.version } 13 | 14 | s.source_files = "ZBJCalendar/ZBJCalendar/*.{h,m}" 15 | s.public_header_files = "ZBJCalendar/ZBJCalendar/*.h" 16 | 17 | 18 | end 19 | -------------------------------------------------------------------------------- /ZBJCalendar.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ZBJCalendar.xcodeproj/project.xcworkspace/xcuserdata/wanggang.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wanggang316/ZBJCalendar/bbe2ebcbdfc35eb01271242c1546d9e9aa00ad8c/ZBJCalendar.xcodeproj/project.xcworkspace/xcuserdata/wanggang.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /ZBJCalendar.xcodeproj/xcshareddata/xcschemes/ZBJCalendar.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 29 | 35 | 36 | 37 | 38 | 39 | 44 | 45 | 47 | 53 | 54 | 55 | 57 | 63 | 64 | 65 | 66 | 67 | 73 | 74 | 75 | 76 | 77 | 78 | 88 | 90 | 96 | 97 | 98 | 99 | 100 | 101 | 107 | 109 | 115 | 116 | 117 | 118 | 120 | 121 | 124 | 125 | 126 | -------------------------------------------------------------------------------- /ZBJCalendar.xcodeproj/xcuserdata/wanggang.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 8 | 20 | 21 | 22 | 24 | 36 | 37 | 38 | 40 | 52 | 53 | 54 | 56 | 68 | 69 | 70 | 72 | 84 | 85 | 86 | 88 | 100 | 101 | 102 | 104 | 116 | 117 | 118 | 120 | 132 | 133 | 134 | 136 | 148 | 149 | 150 | 152 | 164 | 165 | 166 | 168 | 180 | 181 | 182 | 184 | 196 | 197 | 198 | 200 | 212 | 213 | 214 | 216 | 228 | 229 | 230 | 231 | 232 | -------------------------------------------------------------------------------- /ZBJCalendar.xcodeproj/xcuserdata/wanggang.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | ZBJCalendar.xcscheme_^#shared#^_ 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | 32F7EBE51C16779F00ED2DA4 16 | 17 | primary 18 | 19 | 20 | 32F7EBFE1C16779F00ED2DA4 21 | 22 | primary 23 | 24 | 25 | 32F7EC091C16779F00ED2DA4 26 | 27 | primary 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /ZBJCalendar/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // ZBJCalendar 4 | // 5 | // Created by gumpwang on 15/12/8. 6 | // Copyright © 2015年 ZBJ. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface AppDelegate : UIResponder 12 | 13 | @property (strong, nonatomic) UIWindow *window; 14 | 15 | 16 | @end 17 | 18 | -------------------------------------------------------------------------------- /ZBJCalendar/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // ZBJCalendar 4 | // 5 | // Created by gumpwang on 15/12/8. 6 | // Copyright © 2015年 ZBJ. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | 11 | @interface AppDelegate () 12 | 13 | @end 14 | 15 | @implementation AppDelegate 16 | 17 | 18 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 19 | // Override point for customization after application launch. 20 | 21 | // [[UINavigationBar appearance] setBarTintColor:[UIColor colorWithRed:9.0/255.0 green:9.0/255.0 blue:26.0/255.0 alpha:0.8]]; 22 | // [[UINavigationBar appearance] setTintColor:[UIColor whiteColor]]; 23 | // [[UINavigationBar appearance] setTitleTextAttributes:@{NSForegroundColorAttributeName : [UIColor whiteColor]}]; 24 | return YES; 25 | } 26 | 27 | - (void)applicationWillResignActive:(UIApplication *)application { 28 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 29 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. 30 | } 31 | 32 | - (void)applicationDidEnterBackground:(UIApplication *)application { 33 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 34 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 35 | } 36 | 37 | - (void)applicationWillEnterForeground:(UIApplication *)application { 38 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. 39 | } 40 | 41 | - (void)applicationDidBecomeActive:(UIApplication *)application { 42 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 43 | } 44 | 45 | - (void)applicationWillTerminate:(UIApplication *)application { 46 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 47 | } 48 | 49 | @end 50 | -------------------------------------------------------------------------------- /ZBJCalendar/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "29x29", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "40x40", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "40x40", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "size" : "60x60", 25 | "idiom" : "iphone", 26 | "filename" : "logo120.png", 27 | "scale" : "2x" 28 | }, 29 | { 30 | "size" : "60x60", 31 | "idiom" : "iphone", 32 | "filename" : "logo180.png", 33 | "scale" : "3x" 34 | }, 35 | { 36 | "idiom" : "ipad", 37 | "size" : "29x29", 38 | "scale" : "1x" 39 | }, 40 | { 41 | "idiom" : "ipad", 42 | "size" : "29x29", 43 | "scale" : "2x" 44 | }, 45 | { 46 | "idiom" : "ipad", 47 | "size" : "40x40", 48 | "scale" : "1x" 49 | }, 50 | { 51 | "idiom" : "ipad", 52 | "size" : "40x40", 53 | "scale" : "2x" 54 | }, 55 | { 56 | "idiom" : "ipad", 57 | "size" : "76x76", 58 | "scale" : "1x" 59 | }, 60 | { 61 | "idiom" : "ipad", 62 | "size" : "76x76", 63 | "scale" : "2x" 64 | }, 65 | { 66 | "idiom" : "ipad", 67 | "size" : "83.5x83.5", 68 | "scale" : "2x" 69 | } 70 | ], 71 | "info" : { 72 | "version" : 1, 73 | "author" : "xcode" 74 | } 75 | } -------------------------------------------------------------------------------- /ZBJCalendar/Assets.xcassets/AppIcon.appiconset/logo120.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wanggang316/ZBJCalendar/bbe2ebcbdfc35eb01271242c1546d9e9aa00ad8c/ZBJCalendar/Assets.xcassets/AppIcon.appiconset/logo120.png -------------------------------------------------------------------------------- /ZBJCalendar/Assets.xcassets/AppIcon.appiconset/logo180.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wanggang316/ZBJCalendar/bbe2ebcbdfc35eb01271242c1546d9e9aa00ad8c/ZBJCalendar/Assets.xcassets/AppIcon.appiconset/logo180.png -------------------------------------------------------------------------------- /ZBJCalendar/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /ZBJCalendar/Assets.xcassets/cal_selected.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "selected@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /ZBJCalendar/Assets.xcassets/cal_selected.imageset/selected@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wanggang316/ZBJCalendar/bbe2ebcbdfc35eb01271242c1546d9e9aa00ad8c/ZBJCalendar/Assets.xcassets/cal_selected.imageset/selected@2x.png -------------------------------------------------------------------------------- /ZBJCalendar/Assets.xcassets/cal_selected_left.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "left@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /ZBJCalendar/Assets.xcassets/cal_selected_left.imageset/left@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wanggang316/ZBJCalendar/bbe2ebcbdfc35eb01271242c1546d9e9aa00ad8c/ZBJCalendar/Assets.xcassets/cal_selected_left.imageset/left@2x.png -------------------------------------------------------------------------------- /ZBJCalendar/Assets.xcassets/cal_selected_middle.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "middle@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /ZBJCalendar/Assets.xcassets/cal_selected_middle.imageset/middle@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wanggang316/ZBJCalendar/bbe2ebcbdfc35eb01271242c1546d9e9aa00ad8c/ZBJCalendar/Assets.xcassets/cal_selected_middle.imageset/middle@2x.png -------------------------------------------------------------------------------- /ZBJCalendar/Assets.xcassets/cal_selected_right.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "right@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /ZBJCalendar/Assets.xcassets/cal_selected_right.imageset/right@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wanggang316/ZBJCalendar/bbe2ebcbdfc35eb01271242c1546d9e9aa00ad8c/ZBJCalendar/Assets.xcassets/cal_selected_right.imageset/right@2x.png -------------------------------------------------------------------------------- /ZBJCalendar/Assets.xcassets/cancel.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "关闭.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /ZBJCalendar/Assets.xcassets/cancel.imageset/关闭.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wanggang316/ZBJCalendar/bbe2ebcbdfc35eb01271242c1546d9e9aa00ad8c/ZBJCalendar/Assets.xcassets/cancel.imageset/关闭.png -------------------------------------------------------------------------------- /ZBJCalendar/Assets.xcassets/unav.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "可租.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /ZBJCalendar/Assets.xcassets/unav.imageset/可租.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wanggang316/ZBJCalendar/bbe2ebcbdfc35eb01271242c1546d9e9aa00ad8c/ZBJCalendar/Assets.xcassets/unav.imageset/可租.png -------------------------------------------------------------------------------- /ZBJCalendar/Assets.xcassets/unavaible.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "订单页.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /ZBJCalendar/Assets.xcassets/unavaible.imageset/订单页.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wanggang316/ZBJCalendar/bbe2ebcbdfc35eb01271242c1546d9e9aa00ad8c/ZBJCalendar/Assets.xcassets/unavaible.imageset/订单页.png -------------------------------------------------------------------------------- /ZBJCalendar/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /ZBJCalendar/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /ZBJCalendar/CommonViews/ZBJCalendarSectionFooter.h: -------------------------------------------------------------------------------- 1 | // 2 | // ZBJCalendarSectionFooter.h 3 | // ZBJCalendar 4 | // 5 | // Created by gumpwang on 3/15/16. 6 | // Copyright © 2016 ZBJ. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ZBJCalendarSectionFooter : UICollectionReusableView 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /ZBJCalendar/CommonViews/ZBJCalendarSectionFooter.m: -------------------------------------------------------------------------------- 1 | // 2 | // ZBJCalendarSectionFooter.m 3 | // ZBJCalendar 4 | // 5 | // Created by gumpwang on 3/15/16. 6 | // Copyright © 2016 ZBJ. All rights reserved. 7 | // 8 | 9 | #import "ZBJCalendarSectionFooter.h" 10 | 11 | @implementation ZBJCalendarSectionFooter 12 | 13 | - (instancetype)initWithFrame:(CGRect)frame { 14 | if (self = [super initWithFrame:frame]) { 15 | CALayer *layer = [CALayer layer]; 16 | layer.frame = CGRectMake(0, CGRectGetHeight(self.frame) - 0.5, CGRectGetWidth(self.frame), 0.5); 17 | layer.backgroundColor = [UIColor colorWithRed:230.0/255.0 green:230.0/255.0 blue:233.0/255.0 alpha:1.0].CGColor; 18 | [self.layer addSublayer:layer]; 19 | } 20 | return self; 21 | } 22 | 23 | @end 24 | -------------------------------------------------------------------------------- /ZBJCalendar/CommonViews/ZBJCalendarSectionHeader.h: -------------------------------------------------------------------------------- 1 | // 2 | // ZBJCalendarSectionHeader.h 3 | // ZBJCalendar 4 | // 5 | // Created by gumpwang on 2/24/16. 6 | // Copyright © 2016 ZBJ. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ZBJCalendarSectionHeader : UICollectionReusableView 12 | 13 | @property (nonatomic, assign) NSInteger year; 14 | @property (nonatomic, assign) NSInteger month; 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /ZBJCalendar/CommonViews/ZBJCalendarSectionHeader.m: -------------------------------------------------------------------------------- 1 | // 2 | // ZBJCalendarSectionHeader.m 3 | // ZBJCalendar 4 | // 5 | // Created by gumpwang on 2/24/16. 6 | // Copyright © 2016 ZBJ. All rights reserved. 7 | // 8 | 9 | #import "ZBJCalendarSectionHeader.h" 10 | 11 | @interface ZBJCalendarSectionHeader() 12 | 13 | @property (nonatomic, strong) UILabel *yearLabel; 14 | @property (nonatomic, strong) UILabel *monthLabel; 15 | @property (nonatomic, strong) UIView *bottomLine; 16 | 17 | @end 18 | 19 | @implementation ZBJCalendarSectionHeader 20 | 21 | - (id)initWithFrame:(CGRect)frame { 22 | self = [super initWithFrame:frame]; 23 | if (self) { 24 | [self addSubview:self.yearLabel]; 25 | [self addSubview:self.monthLabel]; 26 | [self addSubview:self.bottomLine]; 27 | } 28 | return self; 29 | } 30 | 31 | - (void)layoutSubviews { 32 | [super layoutSubviews]; 33 | self.monthLabel.frame = CGRectMake(13, 25, CGRectGetWidth(self.monthLabel.frame), 20); 34 | self.yearLabel.frame = CGRectMake(CGRectGetMaxX(self.monthLabel.frame) + 3, CGRectGetMaxY(self.monthLabel.frame) - 2 - 9, 100, 9); 35 | self.bottomLine.frame = CGRectMake(CGRectGetMinX(self.monthLabel.frame), CGRectGetMaxY(self.monthLabel.frame) + 11, 30, 2); 36 | } 37 | 38 | #pragma mark - setters 39 | - (void)setYear:(NSInteger)year { 40 | self.yearLabel.text = [NSString stringWithFormat:@"%ld", year]; 41 | } 42 | 43 | - (void)setMonth:(NSInteger)month { 44 | self.monthLabel.text = [NSString stringWithFormat:@"%ld月", month]; 45 | [self.monthLabel sizeToFit]; 46 | } 47 | 48 | 49 | #pragma mark - getters 50 | - (UILabel *)yearLabel { 51 | if (!_yearLabel) { 52 | _yearLabel = [[UILabel alloc] init]; 53 | _yearLabel.font = [UIFont systemFontOfSize:10]; 54 | _yearLabel.textColor = [UIColor darkTextColor]; 55 | _yearLabel.textAlignment = NSTextAlignmentLeft; 56 | } 57 | return _yearLabel; 58 | } 59 | 60 | - (UILabel *)monthLabel { 61 | if (!_monthLabel) { 62 | _monthLabel = [[UILabel alloc] init]; 63 | _monthLabel.font = [UIFont systemFontOfSize:22]; 64 | _monthLabel.textColor = [UIColor darkTextColor]; 65 | } 66 | return _monthLabel; 67 | } 68 | 69 | - (UIView *)bottomLine { 70 | if (!_bottomLine) { 71 | _bottomLine = [[UIView alloc] init]; 72 | _bottomLine.backgroundColor = [UIColor colorWithRed:230.0/255.0 green:230.0/255.0 blue:233.0/255.0 alpha:1.0]; 73 | _bottomLine.layer.cornerRadius = 1.0; 74 | _bottomLine.clipsToBounds = YES; 75 | } 76 | 77 | return _bottomLine; 78 | } 79 | 80 | 81 | @end 82 | -------------------------------------------------------------------------------- /ZBJCalendar/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 0.0.1 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UIStatusBarStyle 34 | UIStatusBarStyleDefault 35 | UISupportedInterfaceOrientations 36 | 37 | UIInterfaceOrientationPortrait 38 | 39 | UISupportedInterfaceOrientations~ipad 40 | 41 | UIInterfaceOrientationPortrait 42 | UIInterfaceOrientationPortraitUpsideDown 43 | UIInterfaceOrientationLandscapeLeft 44 | UIInterfaceOrientationLandscapeRight 45 | 46 | UIViewControllerBasedStatusBarAppearance 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /ZBJCalendar/Model/ZBJCalendarDates.h: -------------------------------------------------------------------------------- 1 | // 2 | // ZBJCalendarDates.h 3 | // ZBJCalendar 4 | // 5 | // Created by gumpwang on 3/10/16. 6 | // Copyright © 2016 ZBJ. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ZBJCalendarDates : NSObject 12 | 13 | @property (nonatomic, strong) NSNumber *offerId; 14 | @property (nonatomic, strong) NSDate *startDate; 15 | @property (nonatomic, strong) NSDate *endDate; 16 | 17 | @property (nonatomic, strong, readonly) NSSet *dates; 18 | 19 | - (instancetype)initWithDictionary:(NSDictionary *)dictionary; 20 | 21 | @end 22 | 23 | @interface ZBJCalendarDate : NSObject 24 | 25 | @property (nonatomic, strong) NSDate *date; 26 | @property (nonatomic, strong) NSNumber *available; 27 | @property (nonatomic, strong) NSNumber *price; 28 | 29 | - (instancetype)initWithDictionary:(NSDictionary *)dictionary; 30 | 31 | @end 32 | -------------------------------------------------------------------------------- /ZBJCalendar/Model/ZBJCalendarDates.m: -------------------------------------------------------------------------------- 1 | // 2 | // ZBJCalendarDates.m 3 | // ZBJCalendar 4 | // 5 | // Created by gumpwang on 3/10/16. 6 | // Copyright © 2016 ZBJ. All rights reserved. 7 | // 8 | 9 | #import "ZBJCalendarDates.h" 10 | 11 | @interface ZBJCalendarDates() 12 | 13 | @property (nonatomic, strong, readwrite) NSMutableSet *adates; 14 | 15 | @end 16 | 17 | @implementation ZBJCalendarDates 18 | 19 | - (instancetype)initWithDictionary:(NSDictionary *)dictionary { 20 | if (self = [super init]) { 21 | 22 | NSDateFormatter *format = [[NSDateFormatter alloc] init]; 23 | [format setTimeZone:[NSTimeZone timeZoneWithAbbreviation:@"UTC"]]; 24 | [format setDateFormat:@"yyyy-MM-dd"]; 25 | 26 | self.offerId = [dictionary objectForKey:@"offer_id"]; 27 | self.startDate = [format dateFromString:[dictionary objectForKey:@"start_date"]]; 28 | self.endDate = [format dateFromString:[dictionary objectForKey:@"end_date"]]; 29 | 30 | self.adates = [NSMutableSet new]; 31 | NSArray *datesArr = [dictionary objectForKey:@"dates"]; 32 | for (NSDictionary *dic in datesArr) { 33 | [self.adates addObject:[[ZBJCalendarDate alloc] initWithDictionary:dic]]; 34 | } 35 | } 36 | return self; 37 | } 38 | 39 | - (NSArray *)dates { 40 | return [self.adates copy]; 41 | } 42 | 43 | @end 44 | 45 | 46 | @implementation ZBJCalendarDate 47 | 48 | - (instancetype)initWithDictionary:(NSDictionary *)dictionary { 49 | if (self = [super init]) { 50 | NSDateFormatter *format = [[NSDateFormatter alloc] init]; 51 | [format setTimeZone:[NSTimeZone timeZoneWithAbbreviation:@"UTC"]]; 52 | [format setDateFormat:@"yyyy-MM-dd"]; 53 | 54 | self.date = [format dateFromString:[dictionary objectForKey:@"date"]]; 55 | self.available = [dictionary objectForKey:@"available"]; 56 | self.price = [dictionary objectForKey:@"price"]; 57 | } 58 | return self; 59 | } 60 | 61 | @end -------------------------------------------------------------------------------- /ZBJCalendar/Model/calendar_dates.json: -------------------------------------------------------------------------------- 1 | {"start_date":"2016-03-10","end_date":"2017-03-10","dates":[{"date":"2016-03-10","available":false,"price":1000},{"date":"2016-03-11","available":false,"price":1099},{"date":"2016-03-12","available":false,"price":1092},{"date":"2016-03-13","available":false,"price":1092},{"date":"2016-03-14","available":false,"price":992},{"date":"2016-03-15","available":false,"price":992},{"date":"2016-03-16","available":true,"price":1096},{"date":"2016-03-17","available":false,"price":1130},{"date":"2016-03-18","available":false,"price":1130},{"date":"2016-03-19","available":false,"price":1130},{"date":"2016-03-20","available":false,"price":809},{"date":"2016-03-21","available":false,"price":809},{"date":"2016-03-22","available":false,"price":809},{"date":"2016-03-23","available":false,"price":910},{"date":"2016-03-24","available":false,"price":1021},{"date":"2016-03-25","available":false,"price":1021},{"date":"2016-03-26","available":false,"price":1021},{"date":"2016-03-27","available":false,"price":1021},{"date":"2016-03-28","available":false,"price":1074},{"date":"2016-03-29","available":false,"price":810},{"date":"2016-03-30","available":false,"price":810},{"date":"2016-03-31","available":false,"price":810},{"date":"2016-04-01","available":true,"price":1096},{"date":"2016-04-02","available":false,"price":809},{"date":"2016-04-03","available":false,"price":809},{"date":"2016-04-04","available":false,"price":809},{"date":"2016-04-05","available":false,"price":809},{"date":"2016-04-06","available":false,"price":809},{"date":"2016-04-07","available":false,"price":809},{"date":"2016-04-08","available":false,"price":809},{"date":"2016-04-09","available":false,"price":809},{"date":"2016-04-10","available":false,"price":809},{"date":"2016-04-11","available":false,"price":809},{"date":"2016-04-12","available":true,"price":1298},{"date":"2016-04-13","available":false,"price":1305},{"date":"2016-04-14","available":false,"price":1305},{"date":"2016-04-15","available":false,"price":1638},{"date":"2016-04-16","available":false,"price":1638},{"date":"2016-04-17","available":false,"price":1229},{"date":"2016-04-18","available":false,"price":1210},{"date":"2016-04-19","available":false,"price":1210},{"date":"2016-04-20","available":false,"price":807},{"date":"2016-04-21","available":false,"price":807},{"date":"2016-04-22","available":true,"price":1096},{"date":"2016-04-23","available":false,"price":1132},{"date":"2016-04-24","available":false,"price":1132},{"date":"2016-04-25","available":false,"price":1132},{"date":"2016-04-26","available":false,"price":1132},{"date":"2016-04-27","available":false,"price":1132},{"date":"2016-04-28","available":true,"price":1096},{"date":"2016-04-29","available":true,"price":1096},{"date":"2016-04-30","available":false,"price":816},{"date":"2016-05-01","available":false,"price":1440},{"date":"2016-05-02","available":false,"price":1440},{"date":"2016-05-03","available":true,"price":1096},{"date":"2016-05-04","available":true,"price":1096},{"date":"2016-05-05","available":true,"price":1096},{"date":"2016-05-06","available":true,"price":1096},{"date":"2016-05-07","available":true,"price":1096},{"date":"2016-05-08","available":true,"price":1096},{"date":"2016-05-09","available":true,"price":1096},{"date":"2016-05-10","available":true,"price":1096},{"date":"2016-05-11","available":true,"price":1096},{"date":"2016-05-12","available":true,"price":1096},{"date":"2016-05-13","available":true,"price":1096},{"date":"2016-05-14","available":true,"price":1096},{"date":"2016-05-15","available":true,"price":1096},{"date":"2016-05-16","available":true,"price":1096},{"date":"2016-05-17","available":true,"price":1096},{"date":"2016-05-18","available":true,"price":1096},{"date":"2016-05-19","available":true,"price":1096},{"date":"2016-05-20","available":true,"price":1096},{"date":"2016-05-21","available":true,"price":1096},{"date":"2016-05-22","available":true,"price":1096},{"date":"2016-05-23","available":true,"price":1096},{"date":"2016-05-24","available":true,"price":1096},{"date":"2016-05-25","available":true,"price":1096},{"date":"2016-05-26","available":true,"price":1096},{"date":"2016-05-27","available":true,"price":1096},{"date":"2016-05-28","available":true,"price":1096},{"date":"2016-05-29","available":true,"price":1096},{"date":"2016-05-30","available":true,"price":1096},{"date":"2016-05-31","available":true,"price":1096},{"date":"2016-06-01","available":true,"price":1096},{"date":"2016-06-02","available":true,"price":1096},{"date":"2016-06-03","available":true,"price":1096},{"date":"2016-06-04","available":true,"price":1096},{"date":"2016-06-05","available":true,"price":1096},{"date":"2016-06-06","available":true,"price":1096},{"date":"2016-06-07","available":true,"price":1096},{"date":"2016-06-08","available":true,"price":1096},{"date":"2016-06-09","available":true,"price":1096},{"date":"2016-06-10","available":true,"price":1096},{"date":"2016-06-11","available":true,"price":1096},{"date":"2016-06-12","available":true,"price":1096},{"date":"2016-06-13","available":true,"price":1096},{"date":"2016-06-14","available":true,"price":1096},{"date":"2016-06-15","available":true,"price":1096},{"date":"2016-06-16","available":true,"price":1096},{"date":"2016-06-17","available":false,"price":1095},{"date":"2016-06-18","available":false,"price":1095},{"date":"2016-06-19","available":false,"price":1095},{"date":"2016-06-20","available":true,"price":1096},{"date":"2016-06-21","available":true,"price":1096},{"date":"2016-06-22","available":false,"price":1464},{"date":"2016-06-23","available":false,"price":1464},{"date":"2016-06-24","available":false,"price":1464},{"date":"2016-06-25","available":true,"price":1096},{"date":"2016-06-26","available":true,"price":1096},{"date":"2016-06-27","available":true,"price":1096},{"date":"2016-06-28","available":true,"price":1096},{"date":"2016-06-29","available":true,"price":1096},{"date":"2016-06-30","available":true,"price":1096},{"date":"2016-07-01","available":true,"price":2061},{"date":"2016-07-02","available":true,"price":2061},{"date":"2016-07-03","available":true,"price":2061},{"date":"2016-07-04","available":true,"price":2061},{"date":"2016-07-05","available":true,"price":2061},{"date":"2016-07-06","available":true,"price":2061},{"date":"2016-07-07","available":true,"price":2061},{"date":"2016-07-08","available":true,"price":2061},{"date":"2016-07-09","available":true,"price":2061},{"date":"2016-07-10","available":true,"price":2061},{"date":"2016-07-11","available":true,"price":2061},{"date":"2016-07-12","available":true,"price":2061},{"date":"2016-07-13","available":true,"price":2061},{"date":"2016-07-14","available":true,"price":2061},{"date":"2016-07-15","available":true,"price":2061},{"date":"2016-07-16","available":true,"price":2061},{"date":"2016-07-17","available":true,"price":2061},{"date":"2016-07-18","available":true,"price":2061},{"date":"2016-07-19","available":true,"price":2061},{"date":"2016-07-20","available":true,"price":2061},{"date":"2016-07-21","available":true,"price":2061},{"date":"2016-07-22","available":true,"price":2061},{"date":"2016-07-23","available":true,"price":2061},{"date":"2016-07-24","available":true,"price":2061},{"date":"2016-07-25","available":true,"price":2061},{"date":"2016-07-26","available":true,"price":2061},{"date":"2016-07-27","available":true,"price":2061},{"date":"2016-07-28","available":true,"price":2061},{"date":"2016-07-29","available":true,"price":2061},{"date":"2016-07-30","available":true,"price":2061},{"date":"2016-07-31","available":true,"price":2061},{"date":"2016-08-01","available":false,"price":2093},{"date":"2016-08-02","available":false,"price":2093},{"date":"2016-08-03","available":false,"price":2093},{"date":"2016-08-04","available":false,"price":2093},{"date":"2016-08-05","available":false,"price":2093},{"date":"2016-08-06","available":false,"price":2093},{"date":"2016-08-07","available":false,"price":2093},{"date":"2016-08-08","available":false,"price":2093},{"date":"2016-08-09","available":false,"price":2093},{"date":"2016-08-10","available":false,"price":2093},{"date":"2016-08-11","available":false,"price":2093},{"date":"2016-08-12","available":false,"price":2093},{"date":"2016-08-13","available":false,"price":2093},{"date":"2016-08-14","available":false,"price":2093},{"date":"2016-08-15","available":false,"price":2093},{"date":"2016-08-16","available":false,"price":2093},{"date":"2016-08-17","available":false,"price":2093},{"date":"2016-08-18","available":false,"price":2093},{"date":"2016-08-19","available":false,"price":2093},{"date":"2016-08-20","available":false,"price":2094},{"date":"2016-08-21","available":true,"price":2061},{"date":"2016-08-22","available":true,"price":2061},{"date":"2016-08-23","available":true,"price":2061},{"date":"2016-08-24","available":true,"price":2061},{"date":"2016-08-25","available":true,"price":2061},{"date":"2016-08-26","available":true,"price":2061},{"date":"2016-08-27","available":true,"price":2061},{"date":"2016-08-28","available":true,"price":2061},{"date":"2016-08-29","available":true,"price":2061},{"date":"2016-08-30","available":true,"price":2061},{"date":"2016-08-31","available":true,"price":2061},{"date":"2016-09-01","available":true,"price":1096},{"date":"2016-09-02","available":true,"price":1096},{"date":"2016-09-03","available":true,"price":1096},{"date":"2016-09-04","available":true,"price":1096},{"date":"2016-09-05","available":true,"price":1096},{"date":"2016-09-06","available":true,"price":1096},{"date":"2016-09-07","available":true,"price":1096},{"date":"2016-09-08","available":true,"price":1096},{"date":"2016-09-09","available":true,"price":1096},{"date":"2016-09-10","available":true,"price":1096},{"date":"2016-09-11","available":true,"price":1096},{"date":"2016-09-12","available":true,"price":1096},{"date":"2016-09-13","available":true,"price":1096},{"date":"2016-09-14","available":true,"price":1096},{"date":"2016-09-15","available":true,"price":1096},{"date":"2016-09-16","available":true,"price":1096},{"date":"2016-09-17","available":true,"price":1096},{"date":"2016-09-18","available":true,"price":1096},{"date":"2016-09-19","available":true,"price":1096},{"date":"2016-09-20","available":true,"price":1096},{"date":"2016-09-21","available":true,"price":1096},{"date":"2016-09-22","available":true,"price":1096},{"date":"2016-09-23","available":true,"price":1096},{"date":"2016-09-24","available":true,"price":1096},{"date":"2016-09-25","available":true,"price":1096},{"date":"2016-09-26","available":true,"price":1096},{"date":"2016-09-27","available":true,"price":1096},{"date":"2016-09-28","available":true,"price":1096},{"date":"2016-09-29","available":true,"price":1096},{"date":"2016-09-30","available":true,"price":1096},{"date":"2016-10-01","available":true,"price":1096},{"date":"2016-10-02","available":true,"price":1096},{"date":"2016-10-03","available":true,"price":1096},{"date":"2016-10-04","available":true,"price":1096},{"date":"2016-10-05","available":true,"price":1096},{"date":"2016-10-06","available":true,"price":1096},{"date":"2016-10-07","available":true,"price":1096},{"date":"2016-10-08","available":true,"price":1096},{"date":"2016-10-09","available":true,"price":1096},{"date":"2016-10-10","available":true,"price":1096},{"date":"2016-10-11","available":true,"price":1096},{"date":"2016-10-12","available":true,"price":1096},{"date":"2016-10-13","available":true,"price":1096},{"date":"2016-10-14","available":true,"price":1096},{"date":"2016-10-15","available":true,"price":1096},{"date":"2016-10-16","available":true,"price":1096},{"date":"2016-10-17","available":true,"price":1096},{"date":"2016-10-18","available":true,"price":1096},{"date":"2016-10-19","available":true,"price":1096},{"date":"2016-10-20","available":true,"price":1096},{"date":"2016-10-21","available":true,"price":1096},{"date":"2016-10-22","available":true,"price":1096},{"date":"2016-10-23","available":true,"price":1096},{"date":"2016-10-24","available":true,"price":1096},{"date":"2016-10-25","available":true,"price":1096},{"date":"2016-10-26","available":true,"price":1096},{"date":"2016-10-27","available":true,"price":1096},{"date":"2016-10-28","available":true,"price":1096},{"date":"2016-10-29","available":true,"price":1096},{"date":"2016-10-30","available":true,"price":1096},{"date":"2016-10-31","available":true,"price":1096},{"date":"2016-11-01","available":true,"price":1096},{"date":"2016-11-02","available":true,"price":1096},{"date":"2016-11-03","available":true,"price":1096},{"date":"2016-11-04","available":true,"price":1096},{"date":"2016-11-05","available":true,"price":1096},{"date":"2016-11-06","available":true,"price":1096},{"date":"2016-11-07","available":true,"price":1096},{"date":"2016-11-08","available":true,"price":1096},{"date":"2016-11-09","available":true,"price":1096},{"date":"2016-11-10","available":true,"price":1096},{"date":"2016-11-11","available":true,"price":1096},{"date":"2016-11-12","available":true,"price":1096},{"date":"2016-11-13","available":true,"price":1096},{"date":"2016-11-14","available":true,"price":1096},{"date":"2016-11-15","available":true,"price":1096},{"date":"2016-11-16","available":true,"price":1096},{"date":"2016-11-17","available":true,"price":1096},{"date":"2016-11-18","available":true,"price":1096},{"date":"2016-11-19","available":true,"price":1096},{"date":"2016-11-20","available":true,"price":1096},{"date":"2016-11-21","available":true,"price":1096},{"date":"2016-11-22","available":true,"price":1096},{"date":"2016-11-23","available":true,"price":1096},{"date":"2016-11-24","available":true,"price":1096},{"date":"2016-11-25","available":true,"price":1096},{"date":"2016-11-26","available":true,"price":1096},{"date":"2016-11-27","available":true,"price":1096},{"date":"2016-11-28","available":true,"price":1096},{"date":"2016-11-29","available":true,"price":1096},{"date":"2016-11-30","available":true,"price":1096},{"date":"2016-12-01","available":true,"price":1096},{"date":"2016-12-02","available":true,"price":1096},{"date":"2016-12-03","available":true,"price":1096},{"date":"2016-12-04","available":true,"price":1096},{"date":"2016-12-05","available":true,"price":1096},{"date":"2016-12-06","available":true,"price":1096},{"date":"2016-12-07","available":true,"price":1096},{"date":"2016-12-08","available":true,"price":1096},{"date":"2016-12-09","available":true,"price":1096},{"date":"2016-12-10","available":true,"price":1096},{"date":"2016-12-11","available":true,"price":1096},{"date":"2016-12-12","available":true,"price":1096},{"date":"2016-12-13","available":true,"price":1096},{"date":"2016-12-14","available":true,"price":1096},{"date":"2016-12-15","available":true,"price":2426},{"date":"2016-12-16","available":true,"price":2426},{"date":"2016-12-17","available":true,"price":2426},{"date":"2016-12-18","available":true,"price":2426},{"date":"2016-12-19","available":true,"price":2426},{"date":"2016-12-20","available":true,"price":2426},{"date":"2016-12-21","available":true,"price":2426},{"date":"2016-12-22","available":true,"price":2426},{"date":"2016-12-23","available":true,"price":2426},{"date":"2016-12-24","available":true,"price":2426},{"date":"2016-12-25","available":true,"price":2426},{"date":"2016-12-26","available":false,"price":1126},{"date":"2016-12-27","available":false,"price":1126},{"date":"2016-12-28","available":false,"price":1126},{"date":"2016-12-29","available":false,"price":1126},{"date":"2016-12-30","available":false,"price":1126},{"date":"2016-12-31","available":false,"price":1126},{"date":"2017-01-01","available":false,"price":1126},{"date":"2017-01-02","available":false,"price":1126},{"date":"2017-01-03","available":false,"price":1126},{"date":"2017-01-04","available":false,"price":1126},{"date":"2017-01-05","available":true,"price":2426},{"date":"2017-01-06","available":true,"price":2426},{"date":"2017-01-07","available":true,"price":2426},{"date":"2017-01-08","available":true,"price":2426},{"date":"2017-01-09","available":true,"price":2426},{"date":"2017-01-10","available":true,"price":2426},{"date":"2017-01-11","available":true,"price":2426},{"date":"2017-01-12","available":true,"price":2426},{"date":"2017-01-13","available":true,"price":2426},{"date":"2017-01-14","available":true,"price":2426},{"date":"2017-01-15","available":true,"price":2426},{"date":"2017-01-16","available":true,"price":2426},{"date":"2017-01-17","available":true,"price":2426},{"date":"2017-01-18","available":true,"price":2426},{"date":"2017-01-19","available":true,"price":2426},{"date":"2017-01-20","available":true,"price":2426},{"date":"2017-01-21","available":true,"price":2426},{"date":"2017-01-22","available":true,"price":2426},{"date":"2017-01-23","available":true,"price":2426},{"date":"2017-01-24","available":true,"price":2426},{"date":"2017-01-25","available":true,"price":2426},{"date":"2017-01-26","available":true,"price":2426},{"date":"2017-01-27","available":true,"price":2426},{"date":"2017-01-28","available":true,"price":2426},{"date":"2017-01-29","available":true,"price":1615},{"date":"2017-01-30","available":true,"price":1615},{"date":"2017-01-31","available":true,"price":1615},{"date":"2017-02-01","available":true,"price":1615},{"date":"2017-02-02","available":true,"price":1615},{"date":"2017-02-03","available":true,"price":1615},{"date":"2017-02-04","available":true,"price":1615},{"date":"2017-02-05","available":true,"price":1615},{"date":"2017-02-06","available":true,"price":1615},{"date":"2017-02-07","available":true,"price":1615},{"date":"2017-02-08","available":true,"price":1615},{"date":"2017-02-09","available":true,"price":1615},{"date":"2017-02-10","available":true,"price":1615},{"date":"2017-02-11","available":true,"price":1615},{"date":"2017-02-12","available":true,"price":1615},{"date":"2017-02-13","available":true,"price":1615},{"date":"2017-02-14","available":true,"price":1615},{"date":"2017-02-15","available":true,"price":1615},{"date":"2017-02-16","available":true,"price":1615},{"date":"2017-02-17","available":true,"price":1615},{"date":"2017-02-18","available":true,"price":1615},{"date":"2017-02-19","available":true,"price":1615},{"date":"2017-02-20","available":true,"price":1615},{"date":"2017-02-21","available":true,"price":1615},{"date":"2017-02-22","available":true,"price":1615},{"date":"2017-02-23","available":true,"price":1615},{"date":"2017-02-24","available":true,"price":1615},{"date":"2017-02-25","available":true,"price":1615},{"date":"2017-02-26","available":true,"price":1615},{"date":"2017-02-27","available":true,"price":1615},{"date":"2017-02-28","available":true,"price":1615},{"date":"2017-03-01","available":true,"price":1615},{"date":"2017-03-02","available":true,"price":1615},{"date":"2017-03-03","available":true,"price":1615},{"date":"2017-03-04","available":true,"price":1615},{"date":"2017-03-05","available":true,"price":1615},{"date":"2017-03-06","available":true,"price":1615},{"date":"2017-03-07","available":true,"price":1615},{"date":"2017-03-08","available":true,"price":1615},{"date":"2017-03-09","available":true,"price":1615},{"date":"2017-03-10","available":true,"price":1615}]} 2 | -------------------------------------------------------------------------------- /ZBJCalendar/MutipleSelection/ZBJMutipleSelectionSectionHeaderView.h: -------------------------------------------------------------------------------- 1 | // 2 | // ZBJMutipleSelectionSectionHeaderView.h 3 | // ZBJCalendar 4 | // 5 | // Created by wanggang on 7/4/16. 6 | // Copyright © 2016 ZBJ. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | typedef void (^ZBJMonthTappedHandler)(NSDate *month); 12 | @interface ZBJMutipleSelectionSectionHeaderView : UICollectionReusableView 13 | 14 | @property (nonatomic, strong) NSDate *firstDateOfMonth; 15 | @property (nonatomic, copy) ZBJMonthTappedHandler tapHandler; 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /ZBJCalendar/MutipleSelection/ZBJMutipleSelectionSectionHeaderView.m: -------------------------------------------------------------------------------- 1 | // 2 | // ZBJMutipleSelectionSectionHeaderView.m 3 | // ZBJCalendar 4 | // 5 | // Created by wanggang on 7/4/16. 6 | // Copyright © 2016 ZBJ. All rights reserved. 7 | // 8 | 9 | #import "ZBJMutipleSelectionSectionHeaderView.h" 10 | #import "ZBJCalendar.h" 11 | 12 | @interface ZBJMutipleSelectionSectionHeaderView () 13 | 14 | @property (nonatomic, strong) UILabel *monthLabel; 15 | @property (nonatomic, assign) NSInteger weekday; 16 | 17 | @end 18 | 19 | @implementation ZBJMutipleSelectionSectionHeaderView 20 | 21 | - (id)initWithFrame:(CGRect)frame { 22 | self = [super initWithFrame:frame]; 23 | if (self) { 24 | [self addSubview:self.monthLabel]; 25 | 26 | UITapGestureRecognizer *gesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapMonthView:)]; 27 | self.userInteractionEnabled = YES; 28 | [self addGestureRecognizer:gesture]; 29 | } 30 | return self; 31 | } 32 | 33 | - (void)layoutSubviews { 34 | [super layoutSubviews]; 35 | self.monthLabel.frame = CGRectMake(10, (CGRectGetHeight(self.frame) - 20) / 2, CGRectGetWidth(self.monthLabel.frame), 20); 36 | } 37 | 38 | #pragma mark - events 39 | - (void)tapMonthView:(id)sender { 40 | if (self.tapHandler) { 41 | self.tapHandler(self.firstDateOfMonth); 42 | } 43 | } 44 | 45 | #pragma mark - setters 46 | - (void)setFirstDateOfMonth:(NSDate *)firstDateOfMonth { 47 | _firstDateOfMonth = firstDateOfMonth; 48 | NSCalendar *calendar = [NSDate gregorianCalendar]; 49 | NSDateComponents *components = [calendar components:NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitWeekday fromDate:firstDateOfMonth]; 50 | self.monthLabel.text = [NSString stringWithFormat:@"%ld月%ld", components.month, components.year]; 51 | [self.monthLabel sizeToFit]; 52 | [self layoutSubviews]; 53 | } 54 | 55 | 56 | #pragma mark - getters 57 | - (UILabel *)monthLabel { 58 | if (!_monthLabel) { 59 | _monthLabel = [[UILabel alloc] init]; 60 | _monthLabel.font = [UIFont systemFontOfSize:18]; 61 | _monthLabel.textColor = [UIColor grayColor]; 62 | } 63 | return _monthLabel; 64 | } 65 | 66 | @end 67 | -------------------------------------------------------------------------------- /ZBJCalendar/MutipleSelection/ZBJSimpleMutipleSelectionCell.h: -------------------------------------------------------------------------------- 1 | // 2 | // ZBJMutipleSelectionCell.h 3 | // ZBJCalendar 4 | // 5 | // Created by wanggang on 7/4/16. 6 | // Copyright © 2016 ZBJ. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | typedef CF_ENUM(NSInteger, ZBJCalendarCellState) { 12 | ZBJCalendarCellStateEmpty = 0, 13 | ZBJCalendarCellStateDisabled = 1, 14 | ZBJCalendarCellStateNormal = 2, 15 | ZBJCalendarCellStateSelected = 3, 16 | ZBJCalendarCellStateSelectedLeft = 4, 17 | ZBJCalendarCellStateSelectedMiddle = 5, 18 | ZBJCalendarCellStateSelectedRight = 6, 19 | }; 20 | 21 | @interface ZBJSimpleMutipleSelectionCell : UICollectionViewCell 22 | 23 | @property (nonatomic, strong) NSDate *date; 24 | @property (nonatomic, assign) ZBJCalendarCellState cellState; 25 | 26 | @end 27 | -------------------------------------------------------------------------------- /ZBJCalendar/MutipleSelection/ZBJSimpleMutipleSelectionCell.m: -------------------------------------------------------------------------------- 1 | // 2 | // ZBJMutipleSelectionCell.m 3 | // ZBJCalendar 4 | // 5 | // Created by wanggang on 7/4/16. 6 | // Copyright © 2016 ZBJ. All rights reserved. 7 | // 8 | 9 | #import "ZBJSimpleMutipleSelectionCell.h" 10 | #import "NSDate+ZBJAddition.h" 11 | 12 | @interface ZBJSimpleMutipleSelectionCell () 13 | 14 | @property (nonatomic, strong) UILabel *dateLabel; 15 | @property (nonatomic, strong) NSCalendar *calendar; 16 | 17 | @property (nonatomic, strong) UIImageView *backView; 18 | 19 | @end 20 | 21 | @implementation ZBJSimpleMutipleSelectionCell 22 | 23 | 24 | - (instancetype)initWithFrame:(CGRect)frame { 25 | if (self = [super initWithFrame:frame]) { 26 | [self.contentView addSubview:self.backView]; 27 | [self.contentView addSubview:self.dateLabel]; 28 | self.calendar = [NSDate gregorianCalendar]; 29 | } 30 | return self; 31 | } 32 | 33 | - (void)layoutSubviews { 34 | [super layoutSubviews]; 35 | CGFloat min = MIN(CGRectGetHeight(self.frame), CGRectGetWidth(self.frame)); 36 | self.dateLabel.frame = CGRectMake(0, 0, min, min); 37 | self.dateLabel.center = CGPointMake(CGRectGetWidth(self.frame) / 2, CGRectGetHeight(self.frame) / 2); 38 | self.backView.frame = self.bounds; 39 | } 40 | 41 | 42 | - (void)setDate:(NSDate *)date { 43 | _date = date; 44 | if (_date) { 45 | self.dateLabel.text = [NSString stringWithFormat:@"%ld", [self.calendar component:NSCalendarUnitDay fromDate:_date]]; 46 | } else { 47 | self.dateLabel.text = nil; 48 | } 49 | } 50 | 51 | - (void)setCellState:(ZBJCalendarCellState)cellState { 52 | 53 | _cellState = cellState; 54 | 55 | switch (_cellState) { 56 | case ZBJCalendarCellStateEmpty: { 57 | self.backView.image = nil; 58 | self.dateLabel.text = nil; 59 | self.dateLabel.textColor = [UIColor whiteColor]; 60 | break; 61 | } 62 | case ZBJCalendarCellStateDisabled: { 63 | self.backView.image = nil; 64 | self.dateLabel.textColor = [UIColor lightGrayColor]; 65 | break; 66 | } 67 | case ZBJCalendarCellStateNormal: { 68 | self.backView.image = nil; 69 | if ([self.date isToday]) { 70 | self.dateLabel.textColor = [UIColor colorWithRed:255.0/255.0 green:60.0/255.0 blue:57.0/255.0 alpha:1.0]; 71 | } else { 72 | self.dateLabel.textColor = [UIColor darkTextColor]; 73 | } 74 | break; 75 | } 76 | case ZBJCalendarCellStateSelected: { 77 | self.dateLabel.textColor = [UIColor whiteColor]; 78 | self.backView.image = [UIImage imageNamed:@"cal_selected"]; 79 | break; 80 | } 81 | case ZBJCalendarCellStateSelectedLeft: { 82 | 83 | self.dateLabel.textColor = [UIColor whiteColor]; 84 | self.backView.image = [UIImage imageNamed:@"cal_selected_left"]; 85 | 86 | break; 87 | } 88 | case ZBJCalendarCellStateSelectedRight: { 89 | self.dateLabel.textColor = [UIColor whiteColor]; 90 | self.backView.image = [UIImage imageNamed:@"cal_selected_right"]; 91 | break; 92 | } 93 | case ZBJCalendarCellStateSelectedMiddle: { 94 | self.dateLabel.textColor = [UIColor whiteColor]; 95 | self.backView.image = [UIImage imageNamed:@"cal_selected_middle"]; 96 | break; 97 | } 98 | default: 99 | break; 100 | } 101 | 102 | [self layoutSubviews]; 103 | } 104 | 105 | #pragma mark - getters 106 | - (UILabel *)dateLabel { 107 | if (!_dateLabel) { 108 | _dateLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 34, 34)]; 109 | _dateLabel.textAlignment = NSTextAlignmentCenter; 110 | _dateLabel.font = [UIFont systemFontOfSize:17]; 111 | _dateLabel.textColor = [UIColor darkTextColor]; 112 | _dateLabel.clipsToBounds = YES; 113 | } 114 | return _dateLabel; 115 | } 116 | 117 | - (UIImageView *)backView { 118 | if (!_backView) { 119 | _backView = [[UIImageView alloc] init]; 120 | _backView.contentMode = UIViewContentModeScaleAspectFill; 121 | } 122 | return _backView; 123 | } 124 | 125 | //- (CALayer *)topLine { 126 | // if (!_topLine) { 127 | // _topLine = [CALayer layer]; 128 | // _topLine.frame = CGRectMake(0, 0, CGRectGetWidth(self.frame), 0.5); 129 | // _topLine.backgroundColor = [UIColor colorWithRed:204.0/255.0 green:204.0/255.0 blue:204.0/255.0 alpha:1.0].CGColor; 130 | // } 131 | // return _topLine; 132 | //} 133 | 134 | @end 135 | -------------------------------------------------------------------------------- /ZBJCalendar/MutipleSelection/ZBJSimpleMutipleSelectionViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ZBJSimpleMutipleSelectionViewController.h 3 | // ZBJCalendar 4 | // 5 | // Created by wanggang on 7/4/16. 6 | // Copyright © 2016 ZBJ. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ZBJSimpleMutipleSelectionViewController : UIViewController 12 | 13 | @property (nonatomic, assign) NSInteger year; 14 | @property (nonatomic, assign) NSInteger month; 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /ZBJCalendar/MutipleSelection/ZBJSimpleMutipleSelectionViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ZBJSimpleMutipleSelectionViewController.m 3 | // ZBJCalendar 4 | // 5 | // Created by wanggang on 7/4/16. 6 | // Copyright © 2016 ZBJ. All rights reserved. 7 | // 8 | 9 | #import "ZBJSimpleMutipleSelectionViewController.h" 10 | #import "ZBJCalendar.h" 11 | #import "ZBJSimpleMutipleSelectionCell.h" 12 | #import "ZBJMutipleSelectionSectionHeaderView.h" 13 | #import "UINavigationBar+ZBJAddition.h" 14 | 15 | @interface ZBJSimpleMutipleSelectionViewController () 16 | 17 | @property (nonatomic, strong) ZBJCalendarView *calendarView; 18 | 19 | @property (nonatomic, strong) NSMutableSet *selectedMonths; 20 | @property (nonatomic, strong) NSMutableSet *selectedDates; 21 | @property (nonatomic, strong) NSMutableDictionary *selectedTypes; 22 | 23 | @end 24 | 25 | @implementation ZBJSimpleMutipleSelectionViewController 26 | 27 | - (void)viewWillAppear:(BOOL)animated { 28 | [super viewWillAppear:animated]; 29 | [self.navigationController.navigationBar hidenHairLine:YES]; 30 | } 31 | 32 | -(void)viewWillDisappear:(BOOL)animated { 33 | [super viewWillDisappear:animated]; 34 | [self.navigationController.navigationBar hidenHairLine:NO]; 35 | } 36 | 37 | - (void)viewDidLoad { 38 | [super viewDidLoad]; 39 | self.view.backgroundColor = [UIColor whiteColor]; 40 | 41 | // 获取当前日期 42 | NSDate *today = [NSDate today]; 43 | 44 | // NSDate *today = [NSDate date]; 45 | // 获取6个月后的最后一天 46 | NSCalendar *calendar = [NSDate gregorianCalendar]; 47 | NSDateComponents *components = [calendar components:NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitDay fromDate:today]; 48 | 49 | NSDate *firstDate = [calendar dateFromComponents:components]; 50 | 51 | components.month = components.month + 6 + 1; 52 | components.day = 0; 53 | NSDate *lastDate = [calendar dateFromComponents:components]; 54 | 55 | self.calendarView.firstDate = firstDate; 56 | self.calendarView.lastDate = lastDate; 57 | 58 | [self.view addSubview:self.calendarView]; 59 | 60 | self.selectedDates = [NSMutableSet new]; 61 | self.selectedMonths = [NSMutableSet new]; 62 | self.selectedTypes = [NSMutableDictionary new]; 63 | } 64 | 65 | - (void)didReceiveMemoryWarning { 66 | [super didReceiveMemoryWarning]; 67 | } 68 | 69 | 70 | - (void)addDateFromMonth:(NSDate *)month { 71 | 72 | NSCalendar *calendar = [NSDate gregorianCalendar]; 73 | NSDateComponents *components = [calendar components:NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitWeekday fromDate:month]; 74 | 75 | NSInteger count = [NSDate numberOfDaysInMonth:month]; 76 | 77 | if ([self.selectedMonths containsObject:month]) { 78 | [self.selectedMonths removeObject:month]; 79 | for (int i = 1; i <= count; i++) { 80 | components.day = i; 81 | NSDate *date = [calendar dateFromComponents:components]; 82 | [self.selectedDates removeObject:date]; 83 | } 84 | } else { 85 | [self.selectedMonths addObject:month]; 86 | NSDateComponents *todayComponents = [calendar components:NSCalendarUnitDay | NSCalendarUnitMonth | NSCalendarUnitYear fromDate:[NSDate date]]; 87 | NSDate *today = [calendar dateFromComponents:todayComponents]; 88 | for (int i = 1; i <= count; i++) { 89 | components.day = i; 90 | NSDate *date = [calendar dateFromComponents:components]; 91 | if ([date compare:today] != NSOrderedAscending) { 92 | [self.selectedDates addObject:date]; 93 | } 94 | } 95 | } 96 | } 97 | 98 | #pragma mark - ZBJCalendarDataSource 99 | - (void)calendarView:(ZBJCalendarView *)calendarView configureCell:(ZBJSimpleMutipleSelectionCell *)cell forDate:(NSDate *)date { 100 | 101 | cell.date = date; 102 | 103 | if (date) { 104 | if ([[date dateByAddingTimeInterval:86400.0 - 1] compare:calendarView.firstDate] == NSOrderedAscending || 105 | [date compare:calendarView.lastDate] == NSOrderedDescending) { //大于最后一天 106 | cell.cellState = ZBJCalendarCellStateDisabled; 107 | } else if ([self.selectedDates containsObject:date]) { 108 | NSNumber *type = self.selectedTypes[date]; 109 | if (type) { 110 | cell.cellState = type.integerValue; 111 | } else { 112 | cell.cellState = ZBJCalendarCellStateSelected; 113 | } 114 | } else { 115 | cell.cellState = ZBJCalendarCellStateNormal; 116 | } 117 | } else { 118 | cell.cellState = ZBJCalendarCellStateEmpty; 119 | } 120 | } 121 | 122 | - (void)calendarView:(ZBJCalendarView *)calendarView configureSectionHeaderView:(ZBJMutipleSelectionSectionHeaderView *)headerView firstDateOfMonth:(NSDate *)firstDateOfMonth { 123 | headerView.firstDateOfMonth = firstDateOfMonth; 124 | 125 | __weak ZBJSimpleMutipleSelectionViewController *me = self; 126 | headerView.tapHandler = ^(NSDate *month) { 127 | [me addDateFromMonth:month]; 128 | [me setSelectedTypes]; 129 | [me.calendarView reloadData]; 130 | NSLog(@"selected dates is : %@", self.selectedDates); 131 | }; 132 | } 133 | 134 | - (void)calendarView:(ZBJCalendarView *)calendarView configureWeekDayLabel:(UILabel *)dayLabel atWeekDay:(NSInteger)weekDay { 135 | dayLabel.font = [UIFont systemFontOfSize:13]; 136 | if (weekDay == 0 || weekDay == 6) { 137 | dayLabel.textColor = [UIColor lightGrayColor]; 138 | } 139 | } 140 | 141 | #pragma mark - ZBJCalendarDelegate 142 | - (BOOL)calendarView:(ZBJCalendarView *)calendarView shouldSelectDate:(NSDate *)date { 143 | if (date) { 144 | if ([[date dateByAddingTimeInterval:86400.0 - 1] compare:calendarView.firstDate] == NSOrderedAscending || 145 | [date compare:calendarView.lastDate] == NSOrderedDescending) { 146 | return NO; 147 | } 148 | } 149 | return YES; 150 | } 151 | 152 | - (void)calendarView:(ZBJCalendarView *)calendarView didSelectDate:(NSDate *)date ofCell:(ZBJSimpleMutipleSelectionCell *)cell { 153 | 154 | if ([self.selectedDates containsObject:date]) { 155 | [self.selectedDates removeObject:date]; 156 | } else { 157 | [self.selectedDates addObject:date]; 158 | } 159 | 160 | [self setSelectedTypes]; 161 | [calendarView reloadData]; 162 | NSLog(@"selected dates is : %@", self.selectedDates); 163 | } 164 | 165 | 166 | 167 | - (void)setSelectedTypes { 168 | 169 | for (NSDate *date in self.selectedDates) { 170 | NSCalendar *calendar = [NSDate gregorianCalendar]; 171 | NSDateComponents *components = [calendar components:NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitDay fromDate:date]; 172 | 173 | components.day = components.day + 1; 174 | NSDate *nextDate = [calendar dateFromComponents:components]; 175 | 176 | components.day = components.day - 2; 177 | NSDate *preDate = [calendar dateFromComponents:components]; 178 | 179 | BOOL containsNextDate = [self.selectedDates containsObject:nextDate]; 180 | BOOL containsPreDate = [self.selectedDates containsObject:preDate]; 181 | 182 | if (containsNextDate && containsPreDate) { 183 | self.selectedTypes[date] = @(ZBJCalendarCellStateSelectedMiddle); 184 | } else if (containsNextDate) { 185 | self.selectedTypes[date] = @(ZBJCalendarCellStateSelectedLeft); 186 | } else if (containsPreDate) { 187 | self.selectedTypes[date] = @(ZBJCalendarCellStateSelectedRight); 188 | } else { 189 | self.selectedTypes[date] = @(ZBJCalendarCellStateSelected); 190 | } 191 | } 192 | } 193 | 194 | 195 | #pragma mark - 196 | - (ZBJCalendarView *)calendarView { 197 | if (!_calendarView) { 198 | _calendarView = [[ZBJCalendarView alloc] initWithFrame:CGRectMake(0, 64, CGRectGetWidth(self.view.frame), CGRectGetHeight(self.view.frame) - 64)]; 199 | [_calendarView registerCellClass:[ZBJSimpleMutipleSelectionCell class] withReuseIdentifier:@"cell"]; 200 | [_calendarView registerSectionHeader:[ZBJMutipleSelectionSectionHeaderView class] withReuseIdentifier:@"sectionHeader"]; 201 | _calendarView.selectionMode = ZBJSelectionModeMutiple; 202 | _calendarView.dataSource = self; 203 | _calendarView.delegate = self; 204 | _calendarView.backgroundColor = [UIColor whiteColor]; 205 | _calendarView.contentInsets = UIEdgeInsetsMake(0, 12.5, 0, 12.5); 206 | _calendarView.cellScale = 1; 207 | _calendarView.sectionHeaderHeight = 50; 208 | _calendarView.weekViewHeight = 20; 209 | _calendarView.weekView.backgroundColor = [UIColor colorWithRed:249.0f/255.0f green:249.0f/255.0f blue:249.0f/255.0f alpha:1.0]; 210 | } 211 | return _calendarView; 212 | } 213 | 214 | @end 215 | -------------------------------------------------------------------------------- /ZBJCalendar/RangeSelection/complex/ZBJComplexRangeSelectionCell.h: -------------------------------------------------------------------------------- 1 | // 2 | // ZBJComplexRangeSelectionCell.h 3 | // ZBJCalendar 4 | // 5 | // Created by gumpwang on 2/24/16. 6 | // Copyright © 2016 ZBJ. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | typedef CF_ENUM(NSInteger, ZBJCalendarCellState) { 12 | ZBJCalendarCellStateEmpty, 13 | ZBJCalendarCellStateDisabled, 14 | ZBJCalendarCellStateUnavaible, 15 | ZBJCalendarCellStateAvaible, 16 | ZBJCalendarCellStateAvaibleDisabled, 17 | ZBJCalendarCellStateSelectedStart, 18 | ZBJCalendarCellStateSelectedMiddle, 19 | ZBJCalendarCellStateSelectedEnd, 20 | ZBJCalendarCellStateSelectedTempEnd, 21 | }; 22 | 23 | 24 | @interface ZBJComplexRangeSelectionCell : UICollectionViewCell 25 | 26 | @property (nonatomic, strong) NSDate *day; 27 | @property (nonatomic, strong) NSNumber *price; 28 | @property (nonatomic, assign) BOOL isToday; 29 | 30 | @property (nonatomic, assign) ZBJCalendarCellState cellState; 31 | 32 | @end 33 | -------------------------------------------------------------------------------- /ZBJCalendar/RangeSelection/complex/ZBJComplexRangeSelectionCell.m: -------------------------------------------------------------------------------- 1 | // 2 | // ZBJComplexRangeSelectionCell.m 3 | // ZBJCalendar 4 | // 5 | // Created by gumpwang on 2/24/16. 6 | // Copyright © 2016 ZBJ. All rights reserved. 7 | // 8 | 9 | #import "ZBJComplexRangeSelectionCell.h" 10 | #import "NSDate+ZBJAddition.h" 11 | 12 | @interface ZBJComplexRangeSelectionCell () 13 | 14 | @property (nonatomic, strong) UILabel *dateLabel; 15 | @property (nonatomic, strong) UILabel *priceLabel; 16 | 17 | @property (nonatomic, strong) NSCalendar *calendar; 18 | @property (nonatomic, strong) UIImageView *backgroundImageView; 19 | 20 | @end 21 | 22 | @implementation ZBJComplexRangeSelectionCell 23 | 24 | - (id)initWithFrame:(CGRect)frame { 25 | self = [super initWithFrame:frame]; 26 | if (self) { 27 | [self.contentView addSubview:self.dateLabel]; 28 | self.calendar = [NSDate gregorianCalendar]; 29 | } 30 | return self; 31 | } 32 | 33 | - (void)layoutSubviews { 34 | [super layoutSubviews]; 35 | 36 | if (![self.priceLabel superview] || !self.priceLabel.text) { 37 | self.dateLabel.frame = CGRectMake(0, (CGRectGetHeight(self.bounds) - 15) / 2, CGRectGetWidth(self.bounds), 15); 38 | } else { 39 | self.dateLabel.frame = CGRectMake(0, CGRectGetHeight(self.bounds) / 2 - CGRectGetHeight(self.dateLabel.frame) - 1, CGRectGetWidth(self.bounds), 15); 40 | self.priceLabel.frame = CGRectMake(0, CGRectGetHeight(self.frame) / 2 + 5, CGRectGetWidth(self.contentView.frame), 9); 41 | } 42 | 43 | self.backgroundImageView.frame = self.bounds; 44 | } 45 | 46 | #pragma mark - setters 47 | - (void)setDay:(NSDate *)day { 48 | _day = day; 49 | if (_day) { 50 | self.dateLabel.text = [NSString stringWithFormat:@"%ld", [self.calendar component:NSCalendarUnitDay fromDate:_day]]; 51 | } else { 52 | self.dateLabel.text = nil; 53 | } 54 | } 55 | 56 | - (void)setPrice:(NSNumber *)price { 57 | _price = price; 58 | if (_price) { 59 | self.priceLabel.text = [NSString stringWithFormat:@"¥%@", _price];; 60 | } else { 61 | self.priceLabel.text = nil; 62 | [self.priceLabel removeFromSuperview]; 63 | } 64 | } 65 | 66 | 67 | - (void)setIsToday:(BOOL)isToday { 68 | _isToday = isToday; 69 | } 70 | 71 | - (void)setCellState:(ZBJCalendarCellState)cellState { 72 | _cellState = cellState; 73 | switch (_cellState) { 74 | case ZBJCalendarCellStateDisabled: { 75 | // layout 76 | [self.priceLabel removeFromSuperview]; 77 | self.backgroundView = nil; 78 | 79 | // style 80 | self.contentView.backgroundColor = [UIColor whiteColor]; 81 | self.dateLabel.textColor = [UIColor colorWithRed:192.0/255.0 green:192.0/255.0 blue:200.0/255.0 alpha:1.0]; 82 | break; 83 | } 84 | case ZBJCalendarCellStateUnavaible: { 85 | // layout 86 | if (![self.priceLabel superview]) { 87 | [self.contentView addSubview:self.priceLabel]; 88 | } 89 | self.backgroundView = self.backgroundImageView; 90 | 91 | // style 92 | self.contentView.backgroundColor = [UIColor clearColor]; 93 | self.dateLabel.textColor = [UIColor colorWithRed:192.0/255.0 green:192.0/255.0 blue:200.0/255.0 alpha:1.0]; 94 | self.priceLabel.textColor = [UIColor colorWithRed:192.0/255.0 green:192.0/255.0 blue:200.0/255.0 alpha:1.0]; 95 | 96 | self.priceLabel.text = @"已租"; 97 | break; 98 | } 99 | case ZBJCalendarCellStateAvaible: { 100 | // layout 101 | if (![self.priceLabel superview]) { 102 | [self.contentView addSubview:self.priceLabel]; 103 | } 104 | self.backgroundView = nil; 105 | 106 | // style 107 | self.contentView.backgroundColor = [UIColor whiteColor]; 108 | self.dateLabel.textColor = [UIColor colorWithRed:9.0/255.0 green:9.0/255.0 blue:26.0/255.0 alpha:1.0]; 109 | self.priceLabel.textColor = [UIColor colorWithRed:157.0/255.0 green:157.0/255.0 blue:163.0/255.0 alpha:1.0]; 110 | break; 111 | } 112 | case ZBJCalendarCellStateAvaibleDisabled: { 113 | // layout 114 | if (![self.priceLabel superview]) { 115 | [self.contentView addSubview:self.priceLabel]; 116 | } 117 | self.backgroundView = nil; 118 | 119 | // style 120 | self.contentView.backgroundColor = [UIColor whiteColor]; 121 | self.dateLabel.textColor = [UIColor colorWithRed:192.0/255.0 green:192.0/255.0 blue:200.0/255.0 alpha:1.0]; 122 | self.priceLabel.textColor = [UIColor colorWithRed:192.0/255.0 green:192.0/255.0 blue:200.0/255.0 alpha:1.0]; 123 | break; 124 | } 125 | 126 | 127 | case ZBJCalendarCellStateSelectedStart: { 128 | // layout 129 | if (![self.priceLabel superview]) { 130 | [self.contentView addSubview:self.priceLabel]; 131 | } 132 | self.backgroundView = nil; 133 | 134 | // style 135 | self.contentView.backgroundColor = [UIColor colorWithRed:9.0/255.0 green:9.0/255.0 blue:26.0/255.0 alpha:1.0]; 136 | self.dateLabel.textColor = [UIColor whiteColor]; 137 | self.priceLabel.textColor = [UIColor whiteColor]; 138 | 139 | self.priceLabel.text = @"入住"; 140 | break; 141 | } 142 | case ZBJCalendarCellStateSelectedMiddle: { 143 | // layout 144 | if (![self.priceLabel superview]) { 145 | [self.contentView addSubview:self.priceLabel]; 146 | } 147 | self.backgroundView = nil; 148 | 149 | // style 150 | self.contentView.backgroundColor = [UIColor colorWithRed:58.0/255.0 green:58.0/255.0 blue:72.0/255.0 alpha:1.0]; 151 | self.dateLabel.textColor = [UIColor whiteColor]; 152 | self.priceLabel.textColor = [UIColor whiteColor]; 153 | break; 154 | } 155 | case ZBJCalendarCellStateSelectedEnd: { 156 | // layout 157 | if (![self.priceLabel superview]) { 158 | [self.contentView addSubview:self.priceLabel]; 159 | } 160 | self.backgroundView = nil; 161 | 162 | // style 163 | self.contentView.backgroundColor = [UIColor colorWithRed:9.0/255.0 green:9.0/255.0 blue:26.0/255.0 alpha:1.0]; 164 | self.dateLabel.textColor = [UIColor whiteColor]; 165 | self.priceLabel.textColor = [UIColor whiteColor]; 166 | 167 | self.priceLabel.text = @"退房"; 168 | break; 169 | } 170 | case ZBJCalendarCellStateSelectedTempEnd: { 171 | // layout 172 | if (![self.priceLabel superview]) { 173 | [self.contentView addSubview:self.priceLabel]; 174 | } 175 | self.backgroundView = nil; 176 | 177 | // style 178 | self.contentView.backgroundColor = [UIColor whiteColor]; 179 | self.dateLabel.textColor = [UIColor colorWithRed:9.0/255.0 green:9.0/255.0 blue:26.0/255.0 alpha:1.0]; 180 | self.priceLabel.textColor = [UIColor colorWithRed:157.0/255.0 green:157.0/255.0 blue:163.0/255.0 alpha:1.0]; 181 | 182 | self.priceLabel.text = @"仅退房"; 183 | break; 184 | } 185 | case ZBJCalendarCellStateEmpty: { 186 | 187 | self.dateLabel.text = nil; 188 | self.priceLabel.text = nil; 189 | [self.priceLabel removeFromSuperview]; 190 | self.backgroundView = nil; 191 | self.contentView.backgroundColor = [UIColor whiteColor]; 192 | 193 | break; 194 | } 195 | default: { 196 | 197 | break; 198 | } 199 | 200 | } 201 | [self layoutSubviews]; 202 | 203 | } 204 | 205 | #pragma mark - getters 206 | - (UILabel *)dateLabel { 207 | if (!_dateLabel) { 208 | _dateLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, CGRectGetWidth(self.bounds), 15)]; 209 | _dateLabel.textAlignment = NSTextAlignmentCenter; 210 | _dateLabel.font = [UIFont systemFontOfSize:15]; 211 | _dateLabel.textColor = [UIColor darkTextColor]; 212 | } 213 | return _dateLabel; 214 | } 215 | 216 | - (UILabel *)priceLabel { 217 | if (!_priceLabel) { 218 | _priceLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, CGRectGetWidth(self.frame), 9)]; 219 | _priceLabel.font = [UIFont systemFontOfSize:9]; 220 | _priceLabel.textAlignment = NSTextAlignmentCenter; 221 | _priceLabel.textColor = [UIColor colorWithRed:157.0/255.0 green:157.0/255.0 blue:163.0/255.0 alpha:1.0]; 222 | } 223 | return _priceLabel; 224 | } 225 | 226 | - (UIImageView *)backgroundImageView { 227 | if (!_backgroundImageView) { 228 | _backgroundImageView = [[UIImageView alloc] initWithFrame:self.contentView.bounds]; 229 | _backgroundImageView.image = [UIImage imageNamed:@"unavaible"]; 230 | _backgroundImageView.contentMode = UIViewContentModeScaleAspectFill; 231 | } 232 | return _backgroundImageView; 233 | } 234 | 235 | 236 | 237 | @end 238 | -------------------------------------------------------------------------------- /ZBJCalendar/RangeSelection/complex/ZBJComplexRangeSelectionController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ZBJComplexRangeSelectionController.h 3 | // ZBJCalendar 4 | // 5 | // Created by gumpwang on 3/10/16. 6 | // Copyright © 2016 ZBJ. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "ZBJCalendarDates.h" 11 | 12 | @protocol ZBJCalendarAdvanceSelectorDelegate; 13 | 14 | @interface ZBJComplexRangeSelectionController : UIViewController 15 | 16 | @property (nonatomic, weak) id delegate; 17 | 18 | @property (nonatomic, strong) NSDate *firstDate; 19 | @property (nonatomic, strong) NSDate *lastDate; 20 | 21 | @property (nonatomic, strong) NSSet *dates; 22 | 23 | @property (nonatomic, assign) NSInteger minNights; // 最小入住天数 24 | 25 | @property (nonatomic, strong) NSDate *startDate; 26 | @property (nonatomic, strong) NSDate *endDate; 27 | 28 | @end 29 | 30 | @protocol ZBJCalendarAdvanceSelectorDelegate 31 | 32 | - (void)popViewController:(UIViewController *)viewController startDate:(NSDate *)startDate endDate:(NSDate *)endDate; 33 | 34 | @end 35 | -------------------------------------------------------------------------------- /ZBJCalendar/RangeSelection/complex/ZBJComplexRangeSelectionController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ZBJComplexRangeSelectionController.m 3 | // ZBJCalendar 4 | // 5 | // Created by gumpwang on 3/10/16. 6 | // Copyright © 2016 ZBJ. All rights reserved. 7 | // 8 | 9 | #import "ZBJComplexRangeSelectionController.h" 10 | #import "ZBJCalendar.h" 11 | #import "ZBJComplexRangeSelectionCell.h" 12 | #import "ZBJCalendarSectionHeader.h" 13 | #import "ZBJCalendarSectionFooter.h" 14 | 15 | typedef CF_ENUM(NSInteger, ZBJCalendarSelectedState) { 16 | ZBJCalendarStateSelectedNone, 17 | ZBJCalendarStateSelectedStart, 18 | ZBJCalendarStateSelectedRange, 19 | }; 20 | 21 | @interface ZBJComplexRangeSelectionController () 22 | 23 | @property (nonatomic, strong) ZBJCalendarView *calendarView; 24 | 25 | @property (nonatomic, assign) ZBJCalendarSelectedState selectedState; 26 | 27 | @property (nonatomic, strong) NSMutableSet *tempUnavaibleDates; 28 | @property (nonatomic, strong) NSDate *nearestUnavaibleDate; 29 | 30 | @end 31 | 32 | @implementation ZBJComplexRangeSelectionController 33 | 34 | - (void)viewDidLoad { 35 | [super viewDidLoad]; 36 | 37 | self.view.backgroundColor = [UIColor whiteColor]; 38 | self.title = [NSString stringWithFormat:@"选择起始日期(至少%ld天)", self.minNights]; 39 | 40 | self.tempUnavaibleDates = [[NSMutableSet alloc] init]; 41 | self.calendarView.firstDate = self.firstDate; 42 | self.calendarView.lastDate = self.lastDate; 43 | 44 | [self.view addSubview:self.calendarView]; 45 | } 46 | 47 | - (void)viewWillAppear:(BOOL)animated { 48 | [super viewWillAppear:animated]; 49 | if (self.startDate && self.endDate) { 50 | self.selectedState = ZBJCalendarStateSelectedRange; 51 | } else { 52 | self.selectedState = ZBJCalendarStateSelectedNone; 53 | } 54 | } 55 | 56 | 57 | - (void)didReceiveMemoryWarning { 58 | [super didReceiveMemoryWarning]; 59 | } 60 | 61 | - (void)pop { 62 | if (self.delegate && [self.delegate respondsToSelector:@selector(popViewController:startDate:endDate:)]) { 63 | [self.delegate popViewController:self startDate:self.startDate endDate:self.endDate]; 64 | } 65 | } 66 | 67 | #pragma mark - private methods 68 | - (ZBJCalendarDate *)offerDateWithDate:(NSDate *)date { 69 | for (ZBJCalendarDate *day in self.dates) { 70 | if ([day.date isEqualToDate:date]) { 71 | return day; 72 | break; 73 | } 74 | } 75 | return nil; 76 | } 77 | 78 | - (void)reset { 79 | self.startDate = nil; 80 | self.endDate = nil; 81 | [self.calendarView reloadData]; 82 | self.calendarView.allowsSelection = YES; 83 | } 84 | 85 | // 计算开始日期之后最近的不可选日期 86 | - (NSDate *)findTheNearestUnavaibleDateByStartDate:(NSDate *)date { 87 | NSDate *nextDate = [date dateByAddingTimeInterval:86400.0]; 88 | ZBJCalendarDate *theDay = [self offerDateWithDate:nextDate]; 89 | if (theDay) { 90 | if (![theDay.available boolValue]) { 91 | return nextDate; 92 | } else { 93 | return [self findTheNearestUnavaibleDateByStartDate:nextDate]; 94 | } 95 | } 96 | return nil; 97 | } 98 | 99 | 100 | #pragma mark - ZBJCalendarDelegate 101 | - (BOOL)calendarView:(ZBJCalendarView *)calendarView shouldSelectDate:(NSDate *)date { 102 | 103 | // if (self.selectionMode == ZBJSelectionModeRange) { 104 | if (date) { 105 | // 日期在今天之前 106 | // 先取到当天的最后一秒: xxxx-xx-xx 23:59:59 107 | if ([[date dateByAddingTimeInterval:86400.0 - 1] compare:calendarView.firstDate] == NSOrderedAscending || 108 | [date compare:calendarView.lastDate] == NSOrderedDescending) { 109 | return NO; 110 | } 111 | switch (self.selectedState) { 112 | case ZBJCalendarStateSelectedStart: { 113 | // self.startDate && !self.endDate && 结束日期 < 起始日期 114 | if ([date compare:self.startDate] == NSOrderedAscending) { 115 | return NO; 116 | } 117 | 118 | // 如果最近的不可选日期等于此日期,那么可选 119 | if ([self.nearestUnavaibleDate isEqualToDate:date]) { 120 | return YES; 121 | } 122 | 123 | // 暂时不可选集合包涵次日期,那么不可选 124 | if ([self.tempUnavaibleDates containsObject:date]) { 125 | return NO; 126 | } 127 | 128 | if (self.nearestUnavaibleDate && 129 | [date compare:self.nearestUnavaibleDate] == NSOrderedDescending) { 130 | return NO; 131 | } 132 | 133 | // 如果不满足上面的条件,那么根据data中的avaible来判断是否可选 134 | ZBJCalendarDate *day = [self offerDateWithDate:date]; 135 | if (day) { 136 | return [day.available boolValue]; 137 | } 138 | break; 139 | } 140 | case ZBJCalendarStateSelectedRange: { 141 | // 如果不满足上面的条件,那么根据data中的avaible来判断是否可选 142 | ZBJCalendarDate *day = [self offerDateWithDate:date]; 143 | if (day) { 144 | return [day.available boolValue]; 145 | } 146 | break; 147 | } 148 | default: 149 | break; 150 | } 151 | 152 | } else { 153 | return NO; 154 | } 155 | // } 156 | 157 | return YES; 158 | } 159 | 160 | 161 | - (void)calendarView:(ZBJCalendarView *)calendarView configureCell:(ZBJComplexRangeSelectionCell *)cell forDate:(NSDate *)date { 162 | 163 | cell.day = date; 164 | 165 | NSNumber *price = nil; 166 | ZBJCalendarCellState cellState = -1; 167 | 168 | if (date) { 169 | // 如果小于起始日期或大于结束日期,那么disabled 170 | if ([[date dateByAddingTimeInterval:86400.0 - 1] compare:calendarView.firstDate] == NSOrderedAscending || 171 | [date compare:calendarView.lastDate] == NSOrderedDescending) { //大于最后一天 172 | 173 | cellState = ZBJCalendarCellStateDisabled; 174 | } else { 175 | 176 | ZBJCalendarDate *day = [self offerDateWithDate:date]; 177 | price = day.price; 178 | 179 | BOOL isAvaible = day.available.boolValue; 180 | 181 | switch (self.selectedState) { 182 | case ZBJCalendarStateSelectedStart: { 183 | 184 | if ([[date dateByAddingTimeInterval:86400.0 - 1] compare:self.startDate] == NSOrderedAscending && 185 | [date compare:calendarView.firstDate] == NSOrderedDescending) { 186 | 187 | if (isAvaible) { 188 | cellState = ZBJCalendarCellStateAvaibleDisabled; 189 | } else { 190 | cellState = ZBJCalendarCellStateUnavaible; 191 | } 192 | } else if ([self.startDate isEqualToDate:date]) { 193 | cellState = ZBJCalendarCellStateSelectedStart; 194 | } else if ([self.tempUnavaibleDates containsObject:date]) { 195 | cellState = ZBJCalendarCellStateAvaibleDisabled; 196 | } else if (self.nearestUnavaibleDate && [self.nearestUnavaibleDate isEqualToDate:date]) { 197 | cellState = ZBJCalendarCellStateSelectedTempEnd; 198 | } else if (self.nearestUnavaibleDate && 199 | [date compare:self.nearestUnavaibleDate] == NSOrderedDescending && 200 | [[date dateByAddingTimeInterval:86400.0 - 1] compare:calendarView.lastDate] == NSOrderedAscending 201 | ) { 202 | if (isAvaible) { 203 | cellState = ZBJCalendarCellStateAvaibleDisabled; 204 | } else { 205 | cellState = ZBJCalendarCellStateUnavaible; 206 | } 207 | } else { 208 | 209 | if (isAvaible) { 210 | cellState = ZBJCalendarCellStateAvaible; 211 | } else { 212 | cellState = ZBJCalendarCellStateUnavaible; 213 | } 214 | } 215 | 216 | break; 217 | } 218 | case ZBJCalendarStateSelectedRange: { 219 | 220 | if ([self.startDate isEqualToDate:date]) { 221 | cellState = ZBJCalendarCellStateSelectedStart; 222 | } else if ([self.endDate isEqualToDate:date]) { 223 | cellState = ZBJCalendarCellStateSelectedEnd; 224 | } else if (self.startDate && self.endDate && 225 | [[date dateByAddingTimeInterval:86400.0 - 1] compare:self.endDate] == NSOrderedAscending && 226 | [date compare:self.startDate] == NSOrderedDescending) { 227 | 228 | cellState = ZBJCalendarCellStateSelectedMiddle; 229 | } else { 230 | if (isAvaible) { 231 | cellState = ZBJCalendarCellStateAvaible; 232 | } else { 233 | cellState = ZBJCalendarCellStateUnavaible; 234 | } 235 | } 236 | break; 237 | } 238 | default: { 239 | 240 | if (isAvaible) { 241 | cellState = ZBJCalendarCellStateAvaible; 242 | } else { 243 | cellState = ZBJCalendarCellStateUnavaible; 244 | } 245 | break; 246 | } 247 | 248 | 249 | } 250 | } 251 | } else { 252 | cellState = ZBJCalendarCellStateEmpty; 253 | } 254 | 255 | cell.price = price; 256 | cell.cellState = cellState; 257 | 258 | } 259 | - (void)calendarView:(ZBJCalendarView *)calendarView didSelectDate:(NSDate *)date ofCell:(id)cell { 260 | if (calendarView.selectionMode == ZBJSelectionModeMutiple) { 261 | if (date) { 262 | if (!self.startDate) { 263 | self.startDate = date; 264 | self.selectedState = ZBJCalendarStateSelectedStart; 265 | } else if (!self.endDate) { 266 | 267 | if ([self.startDate isEqualToDate:date]) { 268 | self.selectedState = ZBJCalendarStateSelectedNone; 269 | return; 270 | } 271 | 272 | self.endDate = date; 273 | self.selectedState = ZBJCalendarStateSelectedRange; 274 | 275 | // pop self 276 | [self performSelector:@selector(pop) withObject:nil afterDelay:0.4]; 277 | } else { 278 | self.startDate = date; 279 | self.selectedState = ZBJCalendarStateSelectedStart; 280 | } 281 | } 282 | } 283 | } 284 | 285 | - (void)calendarView:(ZBJCalendarView *)calendarView configureSectionHeaderView:(ZBJCalendarSectionHeader *)headerView firstDateOfMonth:(NSDate *)firstDateOfMonth { 286 | NSCalendar *calendar = [NSDate gregorianCalendar]; 287 | NSDateComponents *components = [calendar components:NSCalendarUnitYear | NSCalendarUnitMonth fromDate:firstDateOfMonth]; 288 | headerView.year = components.year; 289 | headerView.month = components.month; 290 | } 291 | 292 | #pragma mark - getters 293 | - (ZBJCalendarView *)calendarView { 294 | if (!_calendarView) { 295 | _calendarView = [[ZBJCalendarView alloc] initWithFrame:CGRectMake(0, 64, CGRectGetWidth(self.view.frame), CGRectGetHeight(self.view.frame) - 64)]; 296 | _calendarView.delegate = self; 297 | _calendarView.dataSource = self; 298 | [_calendarView registerCellClass:[ZBJComplexRangeSelectionCell class] withReuseIdentifier:@"cell"]; 299 | [_calendarView registerSectionHeader:[ZBJCalendarSectionHeader class] withReuseIdentifier:@"sectionHeader"]; 300 | [_calendarView registerSectionFooter:[ZBJCalendarSectionFooter class] withReuseIdentifier:@"sectionFooter"]; 301 | _calendarView.contentInsets = UIEdgeInsetsMake(0, 14, 0, 14); 302 | _calendarView.sectionHeaderHeight = 52; 303 | _calendarView.sectionFooterHeight = 13; 304 | _calendarView.minimumLineSpacing = 0.5; 305 | _calendarView.cellScale = 100.0 / 104.0; 306 | } 307 | return _calendarView; 308 | } 309 | 310 | 311 | #pragma mark - setters 312 | - (void)setSelectedState:(ZBJCalendarSelectedState)selectedState { 313 | _selectedState = selectedState; 314 | switch (_selectedState) { 315 | case ZBJCalendarStateSelectedNone: { 316 | 317 | self.title = [NSString stringWithFormat:@"选择起始日期(至少%ld天)", self.minNights]; 318 | 319 | [self reset]; 320 | break; 321 | } 322 | case ZBJCalendarStateSelectedStart: { 323 | 324 | self.endDate = nil; 325 | [self.tempUnavaibleDates removeAllObjects]; 326 | self.nearestUnavaibleDate = nil; 327 | 328 | [self.calendarView reloadData]; 329 | 330 | // calculate avaible `endDate` based `startDate`. 331 | // - the days in `minNights` from `startDate` has days unavaible. 332 | for (int i = 1; i < self.minNights; i++) { 333 | NSDate *theDate = [self.startDate dateByAddingTimeInterval:(i * 24 * 60 * 60)]; 334 | // find the data about `theDate` and deal. 335 | ZBJCalendarDate *day = [self offerDateWithDate:theDate]; 336 | if (!day.available.boolValue) { 337 | self.calendarView.allowsSelection = NO; 338 | [self performSelector:@selector(reset) withObject:nil afterDelay:0.8]; 339 | return; 340 | } 341 | } 342 | 343 | self.title = @"选择退房日期"; 344 | 345 | // - set the day between and `startDate` and `minNights` unavaible. 346 | for (int i = 1; i < self.minNights; i++) { 347 | NSDate *theDate = [self.startDate dateByAddingTimeInterval:(i * 24 * 60 * 60)]; 348 | [self.tempUnavaibleDates addObject:theDate]; 349 | } 350 | // - 计算起始日期之后最近的不可选日期 351 | self.nearestUnavaibleDate = [self findTheNearestUnavaibleDateByStartDate:self.startDate]; 352 | [self.calendarView reloadData]; 353 | break; 354 | } 355 | case ZBJCalendarStateSelectedRange: { 356 | [self.calendarView reloadData]; 357 | self.title = [NSString stringWithFormat:@"选择起始日期(至少%ld天)", self.minNights]; 358 | break; 359 | } 360 | default: 361 | break; 362 | } 363 | } 364 | @end 365 | -------------------------------------------------------------------------------- /ZBJCalendar/RangeSelection/simple/ZBJSimpleRangeSelectionCell.h: -------------------------------------------------------------------------------- 1 | // 2 | // ZBJSimpleRangeSelectionCell.h 3 | // ZBJCalendar 4 | // 5 | // Created by gumpwang on 3/15/16. 6 | // Copyright © 2016 ZBJ. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | typedef CF_ENUM(NSInteger, ZBJCalendarCellState) { 12 | ZBJCalendarCellStateEmpty, 13 | ZBJCalendarCellStateDisabled, 14 | ZBJCalendarCellStateAvaible, 15 | ZBJCalendarCellStateAvaibleDisabled, 16 | ZBJCalendarCellStateSelectedStart, 17 | ZBJCalendarCellStateSelectedMiddle, 18 | ZBJCalendarCellStateSelectedEnd, 19 | }; 20 | 21 | typedef CF_ENUM(NSInteger, ZBJRangeCellStyle) { 22 | ZBJRangeCellStyle1 = 0, 23 | ZBJRangeCellStyle2, 24 | }; 25 | 26 | @interface ZBJSimpleRangeSelectionCell : UICollectionViewCell 27 | 28 | @property (nonatomic, strong) NSDate *date; 29 | @property (nonatomic, assign) ZBJCalendarCellState cellState; 30 | 31 | @property (nonatomic, assign) ZBJRangeCellStyle cellStyle; 32 | @end 33 | -------------------------------------------------------------------------------- /ZBJCalendar/RangeSelection/simple/ZBJSimpleRangeSelectionCell.m: -------------------------------------------------------------------------------- 1 | // 2 | // ZBJSimpleRangeSelectionCell.m 3 | // ZBJCalendar 4 | // 5 | // Created by gumpwang on 3/15/16. 6 | // Copyright © 2016 ZBJ. All rights reserved. 7 | // 8 | 9 | #import "ZBJSimpleRangeSelectionCell.h" 10 | #import "NSDate+ZBJAddition.h" 11 | 12 | @interface ZBJSimpleRangeSelectionCell() 13 | 14 | @property (nonatomic, strong) NSCalendar *calendar; 15 | 16 | @property (nonatomic, strong) UILabel *dateLabel; 17 | @property (nonatomic, strong) UILabel *stateLabel; 18 | @end 19 | 20 | @implementation ZBJSimpleRangeSelectionCell 21 | 22 | - (instancetype)initWithFrame:(CGRect)frame { 23 | if (self = [super initWithFrame:frame]) { 24 | [self.contentView addSubview:self.dateLabel]; 25 | self.calendar = [NSDate gregorianCalendar]; 26 | } 27 | return self; 28 | } 29 | 30 | - (void)layoutSubviews { 31 | [super layoutSubviews]; 32 | 33 | if (self.cellStyle == ZBJRangeCellStyle2) { 34 | 35 | if (![self.stateLabel superview] || !self.stateLabel.text) { 36 | self.dateLabel.frame = CGRectMake(0, (CGRectGetHeight(self.bounds) - 15) / 2, CGRectGetWidth(self.bounds), 15); 37 | } else { 38 | self.dateLabel.frame = CGRectMake(0, CGRectGetHeight(self.bounds) / 2 - CGRectGetHeight(self.dateLabel.frame) - 2, CGRectGetWidth(self.bounds), 15); 39 | self.stateLabel.frame = CGRectMake(0, CGRectGetHeight(self.frame) / 2 + 5, CGRectGetWidth(self.contentView.frame), 9); 40 | } 41 | 42 | } else { 43 | if (![self.stateLabel superview] || !self.stateLabel.text) { 44 | self.dateLabel.frame = CGRectMake(0, (CGRectGetHeight(self.bounds) - 17) / 2, CGRectGetWidth(self.bounds), 17); 45 | } else { 46 | self.dateLabel.frame = CGRectMake(0, CGRectGetHeight(self.bounds) / 2 - CGRectGetHeight(self.dateLabel.frame) + 1, CGRectGetWidth(self.bounds), 17); 47 | self.stateLabel.frame = CGRectMake(0, CGRectGetHeight(self.frame) / 2 + 4, CGRectGetWidth(self.contentView.frame), 10); 48 | } 49 | 50 | } 51 | 52 | 53 | } 54 | 55 | 56 | #pragma mark - setters 57 | 58 | - (void)setCellStyle:(ZBJRangeCellStyle)cellStyle { 59 | 60 | if (_cellStyle != cellStyle) { 61 | _cellStyle = cellStyle; 62 | 63 | switch (_cellStyle) { 64 | case ZBJRangeCellStyle2: { 65 | self.dateLabel.font = [UIFont systemFontOfSize:15]; 66 | self.stateLabel.font = [UIFont systemFontOfSize:9]; 67 | break; 68 | } 69 | default: { 70 | self.dateLabel.font = [UIFont systemFontOfSize:17]; 71 | self.stateLabel.font = [UIFont systemFontOfSize:10]; 72 | break; 73 | } 74 | } 75 | } 76 | } 77 | 78 | - (void)setDate:(NSDate *)date { 79 | _date = date; 80 | if (_date) { 81 | self.dateLabel.text = [NSString stringWithFormat:@"%ld", [self.calendar component:NSCalendarUnitDay fromDate:_date]]; 82 | } else { 83 | self.dateLabel.text = nil; 84 | } 85 | } 86 | 87 | - (void)setCellState:(ZBJCalendarCellState)cellState { 88 | _cellState = cellState; 89 | switch (_cellState) { 90 | case ZBJCalendarCellStateDisabled: { 91 | 92 | if (self.stateLabel.superview) { 93 | [self.stateLabel removeFromSuperview]; 94 | } 95 | self.contentView.backgroundColor = [UIColor clearColor]; 96 | self.dateLabel.textColor = [UIColor colorWithRed:192.0/255.0 green:192.0/255.0 blue:200.0/255.0 alpha:1.0]; 97 | break; 98 | } 99 | case ZBJCalendarCellStateAvaible: { 100 | 101 | if (self.stateLabel.superview) { 102 | [self.stateLabel removeFromSuperview]; 103 | } 104 | self.contentView.backgroundColor = [UIColor clearColor]; 105 | self.dateLabel.textColor = [UIColor colorWithRed:9.0/255.0 green:9.0/255.0 blue:26.0/255.0 alpha:1.0]; 106 | break; 107 | } 108 | case ZBJCalendarCellStateAvaibleDisabled: { 109 | if (self.stateLabel.superview) { 110 | [self.stateLabel removeFromSuperview]; 111 | } 112 | self.contentView.backgroundColor = [UIColor clearColor]; 113 | self.dateLabel.textColor = [UIColor colorWithRed:192.0/255.0 green:192.0/255.0 blue:200.0/255.0 alpha:1.0]; 114 | break; 115 | } 116 | case ZBJCalendarCellStateSelectedStart: { 117 | 118 | [self.contentView addSubview:self.stateLabel]; 119 | 120 | self.contentView.backgroundColor = [UIColor colorWithRed:9.0/255.0 green:9.0/255.0 blue:26.0/255.0 alpha:1.0]; 121 | self.dateLabel.textColor = [UIColor whiteColor]; 122 | self.stateLabel.textColor = [UIColor whiteColor]; 123 | self.stateLabel.text = @"入住"; 124 | break; 125 | } 126 | case ZBJCalendarCellStateSelectedMiddle: { 127 | if (self.stateLabel.superview) { 128 | [self.stateLabel removeFromSuperview]; 129 | } 130 | self.contentView.backgroundColor = [UIColor colorWithRed:58.0/255.0 green:58.0/255.0 blue:72.0/255.0 alpha:1.0]; 131 | self.dateLabel.textColor = [UIColor whiteColor]; 132 | self.stateLabel.textColor = [UIColor whiteColor]; 133 | break; 134 | } 135 | case ZBJCalendarCellStateSelectedEnd: { 136 | 137 | [self.contentView addSubview:self.stateLabel]; 138 | self.contentView.backgroundColor = [UIColor colorWithRed:9.0/255.0 green:9.0/255.0 blue:26.0/255.0 alpha:1.0]; 139 | self.dateLabel.textColor = [UIColor whiteColor]; 140 | self.stateLabel.textColor = [UIColor whiteColor]; 141 | 142 | self.stateLabel.text = @"退房"; 143 | break; 144 | } 145 | case ZBJCalendarCellStateEmpty: { 146 | 147 | self.dateLabel.text = nil; 148 | self.stateLabel.text = nil; 149 | [self.stateLabel removeFromSuperview]; 150 | 151 | self.contentView.backgroundColor = [UIColor clearColor]; 152 | break; 153 | } 154 | default: { 155 | break; 156 | } 157 | 158 | } 159 | [self layoutSubviews]; 160 | } 161 | 162 | #pragma mark - getters 163 | - (UILabel *)dateLabel { 164 | if (!_dateLabel) { 165 | _dateLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, CGRectGetWidth(self.frame), 17)]; 166 | _dateLabel.textAlignment = NSTextAlignmentCenter; 167 | _dateLabel.font = [UIFont systemFontOfSize:17]; 168 | _dateLabel.textColor = [UIColor darkTextColor]; 169 | } 170 | return _dateLabel; 171 | } 172 | 173 | - (UILabel *)stateLabel { 174 | if (!_stateLabel) { 175 | _stateLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, CGRectGetWidth(self.frame), 10)]; 176 | _stateLabel.font = [UIFont systemFontOfSize:10]; 177 | _stateLabel.textAlignment = NSTextAlignmentCenter; 178 | _stateLabel.textColor = [UIColor colorWithRed:157.0/255.0 green:157.0/255.0 blue:163.0/255.0 alpha:1.0]; 179 | } 180 | return _stateLabel; 181 | } 182 | 183 | @end 184 | -------------------------------------------------------------------------------- /ZBJCalendar/RangeSelection/simple/ZBJSimpleRangeSelectionController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ZBJSimpleRangeSelectionController.h 3 | // ZBJCalendar 4 | // 5 | // Created by gumpwang on 2/26/16. 6 | // Copyright © 2016 ZBJ. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @protocol ZBJCalendarRangeSelectorDelegate; 12 | 13 | @interface ZBJSimpleRangeSelectionController : UIViewController 14 | 15 | @property (nonatomic, weak) id delegate; 16 | 17 | @property (nonatomic, strong) NSDate *startDate; 18 | @property (nonatomic, strong) NSDate *endDate; 19 | 20 | @end 21 | 22 | @protocol ZBJCalendarRangeSelectorDelegate 23 | 24 | - (void)popViewController:(UIViewController *)viewController startDate:(NSDate *)startDate endDate:(NSDate *)endDate; 25 | 26 | @end 27 | -------------------------------------------------------------------------------- /ZBJCalendar/RangeSelection/simple/ZBJSimpleRangeSelectionController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ZBJSimpleRangeSelectionController.m 3 | // ZBJCalendar 4 | // 5 | // Created by gumpwang on 2/26/16. 6 | // Copyright © 2016 ZBJ. All rights reserved. 7 | // 8 | 9 | #import "ZBJSimpleRangeSelectionController.h" 10 | #import "ZBJCalendar.h" 11 | #import "ZBJSimpleRangeSelectionCell.h" 12 | #import "ZBJCalendarSectionHeader.h" 13 | #import "ZBJCalendarSectionFooter.h" 14 | 15 | typedef CF_ENUM(NSInteger, ZBJCalendarSelectedState) { 16 | ZBJCalendarStateSelectedNone, 17 | ZBJCalendarStateSelectedStart, 18 | ZBJCalendarStateSelectedRange, 19 | }; 20 | 21 | @interface ZBJSimpleRangeSelectionController () 22 | 23 | @property (nonatomic, strong) ZBJCalendarView *calendarView; 24 | @property (nonatomic, assign) ZBJCalendarSelectedState selectedState; 25 | @property (nonatomic, assign) ZBJRangeCellStyle cellStyle; 26 | 27 | @end 28 | 29 | @implementation ZBJSimpleRangeSelectionController 30 | 31 | - (void)viewDidLoad { 32 | [super viewDidLoad]; 33 | 34 | self.view.backgroundColor = [UIColor whiteColor]; 35 | 36 | NSDate *firstDate = [NSDate date]; 37 | NSCalendar *calendar = [NSCalendar currentCalendar]; 38 | [calendar setTimeZone:[NSTimeZone timeZoneWithAbbreviation:@"GMT"]]; 39 | NSDateComponents *components = [calendar components:NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitDay fromDate:firstDate]; 40 | components.month = components.month + 6; // 41 | NSDate *lastDate = [calendar dateFromComponents:components]; 42 | 43 | self.calendarView.delegate = self; 44 | self.calendarView.dataSource = self; 45 | self.calendarView.firstDate = firstDate; 46 | self.calendarView.lastDate = lastDate; 47 | 48 | [self.view addSubview:self.calendarView]; 49 | } 50 | 51 | - (void)didReceiveMemoryWarning { 52 | [super didReceiveMemoryWarning]; 53 | } 54 | 55 | - (void)pop { 56 | if (self.delegate && [self.delegate respondsToSelector:@selector(popViewController:startDate:endDate:)]) { 57 | [self.delegate popViewController:self startDate:self.startDate endDate:self.endDate]; 58 | } 59 | } 60 | 61 | #pragma mark - ZBJCalendarDelegate && ZBJCalendarDataSource 62 | - (void)calendarView:(ZBJCalendarView *)calendarView configureCell:(ZBJSimpleRangeSelectionCell *)cell forDate:(NSDate *)date { 63 | 64 | cell.cellStyle = self.cellStyle; 65 | 66 | cell.date = date; 67 | 68 | ZBJCalendarCellState cellState = -1; 69 | 70 | if (date) { 71 | // 如果小于起始日期或大于结束日期,那么disabled 72 | if ([[date dateByAddingTimeInterval:86400.0 - 1] compare:calendarView.firstDate] == NSOrderedAscending || 73 | [date compare:calendarView.lastDate] != NSOrderedAscending) { //不大于最后一天 74 | cellState = ZBJCalendarCellStateDisabled; 75 | } else { 76 | 77 | switch (self.selectedState) { 78 | case ZBJCalendarStateSelectedStart: { 79 | 80 | if ([[date dateByAddingTimeInterval:86400.0 - 1] compare:self.startDate] == NSOrderedAscending) { 81 | 82 | cellState = ZBJCalendarCellStateAvaibleDisabled; 83 | } else if ([self.startDate isEqualToDate:date]) { 84 | cellState = ZBJCalendarCellStateSelectedStart; 85 | } else { 86 | cellState = ZBJCalendarCellStateAvaible; 87 | } 88 | 89 | break; 90 | } 91 | case ZBJCalendarStateSelectedRange: { 92 | 93 | if ([self.startDate isEqualToDate:date]) { 94 | cellState = ZBJCalendarCellStateSelectedStart; 95 | } else if ([self.endDate isEqualToDate:date]) { 96 | cellState = ZBJCalendarCellStateSelectedEnd; 97 | } else if (self.startDate && self.endDate && 98 | [[date dateByAddingTimeInterval:86400.0 - 1] compare:self.endDate] == NSOrderedAscending && 99 | [date compare:self.startDate] == NSOrderedDescending) { 100 | cellState = ZBJCalendarCellStateSelectedMiddle; 101 | } else { 102 | cellState = ZBJCalendarCellStateAvaible; 103 | } 104 | 105 | break; 106 | } 107 | default: { 108 | cellState = ZBJCalendarCellStateAvaible; 109 | break; 110 | } 111 | } 112 | } 113 | } else { 114 | cellState = ZBJCalendarCellStateEmpty; 115 | } 116 | 117 | cell.cellState = cellState; 118 | } 119 | - (void)calendarView:(ZBJCalendarView *)calendarView didSelectDate:(NSDate *)date ofCell:(id)cell { 120 | 121 | if (calendarView.selectionMode == ZBJSelectionModeMutiple) { 122 | if (date) { 123 | if (!self.startDate) { 124 | self.startDate = date; 125 | [self setSelectedState:ZBJCalendarStateSelectedStart calendarView:calendarView]; 126 | } else if (!self.endDate) { 127 | if ([self.startDate isEqualToDate:date]) { 128 | [self setSelectedState:ZBJCalendarStateSelectedNone calendarView:calendarView]; 129 | return; 130 | } 131 | self.endDate = date; 132 | [self setSelectedState:ZBJCalendarStateSelectedRange calendarView:calendarView]; 133 | } else { 134 | self.endDate = nil; 135 | self.startDate = date; 136 | [self setSelectedState:ZBJCalendarStateSelectedStart calendarView:calendarView]; 137 | } 138 | } 139 | } 140 | } 141 | 142 | - (void)setSelectedState:(ZBJCalendarSelectedState)selectedState calendarView:(ZBJCalendarView *)calendarView { 143 | 144 | _selectedState = selectedState; 145 | 146 | switch (_selectedState) { 147 | case ZBJCalendarStateSelectedNone: { 148 | self.startDate = nil; 149 | self.endDate = nil; 150 | [calendarView reloadData]; 151 | calendarView.allowsSelection = YES; 152 | break; 153 | } 154 | case ZBJCalendarStateSelectedStart: { 155 | 156 | [calendarView reloadData]; 157 | break; 158 | } 159 | case ZBJCalendarStateSelectedRange: { 160 | [calendarView reloadData]; 161 | // pop self 162 | [self performSelector:@selector(pop) withObject:nil afterDelay:0.4]; 163 | break; 164 | } 165 | default: 166 | break; 167 | } 168 | } 169 | 170 | 171 | - (void)calendarView:(ZBJCalendarView *)calendarView configureSectionHeaderView:(ZBJCalendarSectionHeader *)headerView firstDateOfMonth:(NSDate *)firstDateOfMonth { 172 | NSCalendar *calendar = [NSDate gregorianCalendar]; 173 | NSDateComponents *components = [calendar components:NSCalendarUnitYear | NSCalendarUnitMonth fromDate:firstDateOfMonth]; 174 | headerView.year = components.year; 175 | headerView.month = components.month; 176 | } 177 | 178 | 179 | 180 | 181 | #pragma mark - 182 | - (ZBJCalendarView *)calendarView { 183 | if (!_calendarView) { 184 | _calendarView = [[ZBJCalendarView alloc] initWithFrame:CGRectMake(0, 64, CGRectGetWidth(self.view.frame), CGRectGetHeight(self.view.frame) - 64)]; 185 | [_calendarView registerCellClass:[ZBJSimpleRangeSelectionCell class] withReuseIdentifier:@"cell"]; 186 | [_calendarView registerSectionHeader:[ZBJCalendarSectionHeader class] withReuseIdentifier:@"sectionHeader"]; 187 | [_calendarView registerSectionFooter:[ZBJCalendarSectionFooter class] withReuseIdentifier:@"sectionFooter"]; 188 | _calendarView.contentInsets = UIEdgeInsetsMake(0, 14, 0, 14); 189 | _calendarView.sectionHeaderHeight = 52; 190 | _calendarView.sectionFooterHeight = 13; 191 | _calendarView.cellScale = 90.0 / 102.0; 192 | 193 | } 194 | return _calendarView; 195 | } 196 | 197 | @end 198 | -------------------------------------------------------------------------------- /ZBJCalendar/Show/ZBJCalendarShowCell.h: -------------------------------------------------------------------------------- 1 | // 2 | // ZBJCalendarShowCell.h 3 | // ZBJCalendar 4 | // 5 | // Created by gumpwang on 3/15/16. 6 | // Copyright © 2016 ZBJ. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | typedef CF_ENUM(NSInteger, ZBJCalendarCellState) { 12 | ZBJCalendarCellStateEmpty, 13 | ZBJCalendarCellStateDisabled, 14 | ZBJCalendarCellStateAvaible, 15 | ZBJCalendarCellStateUnavaible, 16 | }; 17 | 18 | @interface ZBJCalendarShowCell : UICollectionViewCell 19 | 20 | @property (nonatomic, strong) NSDate *date; 21 | @property (nonatomic, strong) NSNumber *price; 22 | 23 | @property (nonatomic, assign) ZBJCalendarCellState cellState; 24 | 25 | @end 26 | -------------------------------------------------------------------------------- /ZBJCalendar/Show/ZBJCalendarShowCell.m: -------------------------------------------------------------------------------- 1 | // 2 | // ZBJCalendarShowCell.m 3 | // ZBJCalendar 4 | // 5 | // Created by gumpwang on 3/15/16. 6 | // Copyright © 2016 ZBJ. All rights reserved. 7 | // 8 | 9 | #import "ZBJCalendarShowCell.h" 10 | #import "NSDate+ZBJAddition.h" 11 | 12 | @interface ZBJCalendarShowCell() 13 | 14 | @property (nonatomic, strong) UILabel *dateLabel; 15 | @property (nonatomic, strong) UILabel *priceLabel; 16 | @property (nonatomic, strong) UIImageView *backgroundImageView; 17 | 18 | @property (nonatomic, strong) NSCalendar *calendar; 19 | 20 | @end 21 | 22 | @implementation ZBJCalendarShowCell 23 | 24 | - (instancetype)initWithFrame:(CGRect)frame { 25 | if (self = [super initWithFrame:frame]) { 26 | 27 | [self.contentView addSubview:self.dateLabel]; 28 | [self.contentView addSubview:self.priceLabel]; 29 | self.calendar = [NSDate gregorianCalendar]; 30 | } 31 | return self; 32 | } 33 | 34 | - (void)layoutSubviews { 35 | [super layoutSubviews]; 36 | self.dateLabel.frame = CGRectMake(0, self.contentView.center.y - CGRectGetHeight(self.dateLabel.frame) - 1, CGRectGetWidth(self.bounds), 15); 37 | self.priceLabel.frame = CGRectMake(0, self.contentView.center.y + 5, CGRectGetWidth(self.frame), 9); 38 | self.backgroundImageView.frame = self.contentView.bounds; 39 | } 40 | 41 | 42 | #pragma mark - setters 43 | - (void)setDate:(NSDate *)date { 44 | _date = date; 45 | if (_date) { 46 | self.dateLabel.text = [NSString stringWithFormat:@"%ld", [self.calendar component:NSCalendarUnitDay fromDate:_date]]; 47 | } else { 48 | self.dateLabel.text = nil; 49 | } 50 | } 51 | 52 | - (void)setPrice:(NSNumber *)price { 53 | _price = price; 54 | if (_price) { 55 | self.priceLabel.text = [NSString stringWithFormat:@"¥%@", _price]; 56 | } else { 57 | self.priceLabel.text = nil; 58 | } 59 | } 60 | 61 | - (void)setCellState:(ZBJCalendarCellState)cellState { 62 | _cellState = cellState; 63 | switch (_cellState) { 64 | case ZBJCalendarCellStateDisabled: { 65 | self.backgroundView = nil; 66 | self.dateLabel.textColor = [UIColor colorWithRed:192.0/255.0 green:192.0/255.0 blue:200.0/255.0 alpha:1.0]; 67 | break; 68 | } 69 | case ZBJCalendarCellStateAvaible: { 70 | self.backgroundView = nil; 71 | self.dateLabel.textColor = [UIColor colorWithRed:9.0/255.0 green:9.0/255.0 blue:26.0/255.0 alpha:1.0]; 72 | break; 73 | } 74 | case ZBJCalendarCellStateUnavaible: { 75 | self.backgroundView = self.backgroundImageView; 76 | 77 | self.dateLabel.textColor = [UIColor colorWithRed:192.0/255.0 green:192.0/255.0 blue:200.0/255.0 alpha:1.0]; 78 | self.priceLabel.textColor = [UIColor colorWithRed:192.0/255.0 green:192.0/255.0 blue:200.0/255.0 alpha:1.0]; 79 | 80 | self.priceLabel.text = @"已租"; 81 | break; 82 | } 83 | 84 | case ZBJCalendarCellStateEmpty: { 85 | self.dateLabel.text = nil; 86 | self.priceLabel.text = nil; 87 | self.backgroundView = nil; 88 | break; 89 | } 90 | default: { 91 | break; 92 | } 93 | 94 | } 95 | [self layoutSubviews]; 96 | } 97 | 98 | #pragma mark - getters 99 | - (UILabel *)dateLabel { 100 | if (!_dateLabel) { 101 | _dateLabel = [[UILabel alloc] initWithFrame:self.bounds]; 102 | _dateLabel.textAlignment = NSTextAlignmentCenter; 103 | _dateLabel.font = [UIFont systemFontOfSize:15]; 104 | _dateLabel.textColor = [UIColor darkTextColor]; 105 | } 106 | return _dateLabel; 107 | } 108 | 109 | - (UILabel *)priceLabel { 110 | if (!_priceLabel) { 111 | _priceLabel = [[UILabel alloc] init]; 112 | _priceLabel.font = [UIFont systemFontOfSize:9]; 113 | _priceLabel.textAlignment = NSTextAlignmentCenter; 114 | _priceLabel.textColor = [UIColor colorWithRed:157.0/255.0 green:157.0/255.0 blue:163.0/255.0 alpha:1.0]; 115 | } 116 | return _priceLabel; 117 | } 118 | 119 | - (UIImageView *)backgroundImageView { 120 | if (!_backgroundImageView) { 121 | _backgroundImageView = [[UIImageView alloc] initWithFrame:self.contentView.bounds]; 122 | _backgroundImageView.image = [UIImage imageNamed:@"unav"]; 123 | _backgroundImageView.contentMode = UIViewContentModeScaleAspectFill; 124 | } 125 | return _backgroundImageView; 126 | } 127 | 128 | 129 | @end 130 | -------------------------------------------------------------------------------- /ZBJCalendar/Show/ZBJCalendarShowView.h: -------------------------------------------------------------------------------- 1 | // 2 | // ZBJCalendarShowView.h 3 | // ZBJCalendar 4 | // 5 | // Created by gumpwang on 3/15/16. 6 | // Copyright © 2016 ZBJ. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "ZBJCalendarDates.h" 11 | 12 | @interface ZBJCalendarShowView : UIView 13 | 14 | - (instancetype)initWithFrame:(CGRect)frame calendarDates:(ZBJCalendarDates *)calendarDates; 15 | 16 | @property (nonatomic, strong) ZBJCalendarDates *calendarDates; 17 | 18 | 19 | @end 20 | -------------------------------------------------------------------------------- /ZBJCalendar/Show/ZBJCalendarShowView.m: -------------------------------------------------------------------------------- 1 | // 2 | // ZBJCalendarShowView.m 3 | // ZBJCalendar 4 | // 5 | // Created by gumpwang on 3/15/16. 6 | // Copyright © 2016 ZBJ. All rights reserved. 7 | // 8 | 9 | #import "ZBJCalendarShowView.h" 10 | #import "ZBJCalendar.h" 11 | #import "ZBJCalendarShowCell.h" 12 | #import "ZBJCalendarSectionHeader.h" 13 | #import "ZBJCalendarSectionFooter.h" 14 | 15 | @interface ZBJCalendarShowView () 16 | 17 | @property (nonatomic, strong) UIView *backView; 18 | @property (nonatomic, strong) UILabel *titleLable; 19 | @property (nonatomic, strong) ZBJCalendarView *calendarView; 20 | @property (nonatomic, strong) UIButton *cancelButton; 21 | 22 | @end 23 | 24 | @implementation ZBJCalendarShowView 25 | 26 | - (instancetype)initWithFrame:(CGRect)frame calendarDates:(ZBJCalendarDates *)calendarDates { 27 | if (self = [super initWithFrame:frame]) { 28 | self.backgroundColor = [UIColor colorWithWhite:0.0 alpha:0.8]; 29 | [self addSubview:self.backView]; 30 | self.calendarDates = calendarDates; 31 | } 32 | return self; 33 | } 34 | 35 | - (void)cancel:(id)sender { 36 | [UIView animateWithDuration:0.2 animations:^{ 37 | self.alpha = 0.0; 38 | } completion:^(BOOL finished) { 39 | [self removeFromSuperview]; 40 | }]; 41 | } 42 | 43 | - (void)layoutSubviews { 44 | [super layoutSubviews]; 45 | self.cancelButton.center = CGPointMake(self.backView.frame.size.width/2, CGRectGetHeight(self.backView.frame) - 15 - 13); 46 | } 47 | 48 | 49 | 50 | #pragma mark - private methods 51 | - (ZBJCalendarDate *)offerDateWithDate:(NSDate *)date { 52 | for (ZBJCalendarDate *day in self.calendarDates.dates) { 53 | if ([day.date isEqualToDate:date]) { 54 | return day; 55 | break; 56 | } 57 | } 58 | return nil; 59 | } 60 | 61 | #pragma mark - ZBJCalendarDataSource 62 | - (void)calendarView:(ZBJCalendarView *)calendarView configureCell:(ZBJCalendarShowCell *)cell forDate:(NSDate *)date { 63 | 64 | cell.date = date; 65 | 66 | ZBJCalendarCellState cellState = -1; 67 | NSNumber *price = nil; 68 | 69 | if (date) { 70 | // 如果小于起始日期或大于结束日期,那么disabled 71 | if ([[date dateByAddingTimeInterval:86400.0 - 1] compare:calendarView.firstDate] == NSOrderedAscending || 72 | [date compare:calendarView.lastDate] == NSOrderedDescending) { //不小于最后一天 73 | cellState = ZBJCalendarCellStateDisabled; 74 | } else { 75 | 76 | ZBJCalendarDate *day = [self offerDateWithDate:date]; 77 | price = day.price; 78 | 79 | if (day.available.boolValue) { 80 | cellState = ZBJCalendarCellStateAvaible; 81 | } else { 82 | cellState = ZBJCalendarCellStateUnavaible; 83 | } 84 | } 85 | } else { 86 | cellState = ZBJCalendarCellStateEmpty; 87 | } 88 | 89 | cell.price = price; 90 | cell.cellState = cellState; 91 | } 92 | 93 | - (void)calendarView:(ZBJCalendarView *)calendarView configureSectionHeaderView:(ZBJCalendarSectionHeader *)headerView firstDateOfMonth:(NSDate *)firstDateOfMonth { 94 | NSCalendar *calendar = [NSDate gregorianCalendar]; 95 | NSDateComponents *components = [calendar components:NSCalendarUnitYear | NSCalendarUnitMonth fromDate:firstDateOfMonth]; 96 | headerView.year = components.year; 97 | headerView.month = components.month; 98 | } 99 | 100 | 101 | #pragma mark - getters 102 | - (ZBJCalendarView *)calendarView { 103 | if (!_calendarView) { 104 | _calendarView = [[ZBJCalendarView alloc] initWithFrame:CGRectMake(0, CGRectGetMaxY(self.titleLable.frame) + 17, CGRectGetWidth(self.backView.frame), CGRectGetHeight(self.backView.frame) - CGRectGetMaxY(self.titleLable.frame) - 17 - 55)]; 105 | [_calendarView registerCellClass:[ZBJCalendarShowCell class] withReuseIdentifier:@"cell"]; 106 | [_calendarView registerSectionHeader:[ZBJCalendarSectionHeader class] withReuseIdentifier:@"sectionHader"]; 107 | [_calendarView registerSectionFooter:[ZBJCalendarSectionFooter class] withReuseIdentifier:@"sectionFooter"]; 108 | _calendarView.selectionMode = ZBJSelectionModeDisable; 109 | _calendarView.contentInsets = UIEdgeInsetsMake(0, 16, 0, 16); 110 | _calendarView.sectionHeaderHeight = 52; 111 | _calendarView.sectionFooterHeight = 13; 112 | } 113 | return _calendarView; 114 | } 115 | 116 | - (UIView *)backView { 117 | if (!_backView) { 118 | _backView = [[UIView alloc] initWithFrame:CGRectMake(15, 40, CGRectGetWidth(self.frame) - 30, CGRectGetHeight(self.frame) - 70)]; 119 | _backView.backgroundColor = [UIColor whiteColor]; 120 | _backView.layer.cornerRadius = 3; 121 | _backView.clipsToBounds = YES; 122 | 123 | [_backView addSubview:self.titleLable]; 124 | [_backView addSubview:self.calendarView]; 125 | [_backView addSubview:self.cancelButton]; 126 | } 127 | return _backView; 128 | } 129 | 130 | - (UILabel *)titleLable { 131 | if (!_titleLable) { 132 | _titleLable = [[UILabel alloc] initWithFrame:CGRectMake(25, 25, 200, 20)]; 133 | _titleLable.font = [UIFont systemFontOfSize:20]; 134 | _titleLable.text = @"可租价格日历"; 135 | } 136 | return _titleLable; 137 | } 138 | 139 | - (UIButton *)cancelButton { 140 | if (!_cancelButton) { 141 | _cancelButton = [UIButton buttonWithType:UIButtonTypeCustom]; 142 | _cancelButton.frame = CGRectMake(self.backView.frame.size.width/2 - 12, CGRectGetHeight(self.backView.frame) - 15 - 25, 25, 25); 143 | [_cancelButton setImage:[UIImage imageNamed:@"cancel"] forState:UIControlStateNormal]; 144 | [_cancelButton addTarget:self action:@selector(cancel:) forControlEvents:UIControlEventTouchUpInside]; 145 | } 146 | return _cancelButton; 147 | } 148 | 149 | #pragma mark - setters 150 | - (void)setCalendarDates:(ZBJCalendarDates *)calendarDates { 151 | _calendarDates = calendarDates; 152 | self.calendarView.firstDate = self.calendarDates.startDate; 153 | self.calendarView.lastDate = self.calendarDates.endDate; 154 | self.calendarView.dataSource = self; 155 | } 156 | 157 | @end 158 | -------------------------------------------------------------------------------- /ZBJCalendar/SingleSelection/UINavigationBar+ZBJAddition.h: -------------------------------------------------------------------------------- 1 | // 2 | // UINavigationBar+ZBJAddition.h 3 | // ZBJCalendar 4 | // 5 | // Created by gumpwang on 5/26/16. 6 | // Copyright © 2016 ZBJ. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface UINavigationBar (ZBJAddition) 12 | 13 | - (void)hidenHairLine:(BOOL)hiden; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /ZBJCalendar/SingleSelection/UINavigationBar+ZBJAddition.m: -------------------------------------------------------------------------------- 1 | // 2 | // UINavigationBar+ZBJAddition.m 3 | // ZBJCalendar 4 | // 5 | // Created by gumpwang on 5/26/16. 6 | // Copyright © 2016 ZBJ. All rights reserved. 7 | // 8 | 9 | #import "UINavigationBar+ZBJAddition.h" 10 | 11 | @implementation UINavigationBar (ZBJAddition) 12 | 13 | - (void)hidenHairLine:(BOOL)hiden { 14 | UIImageView *lineView = [self findHairlineFromView:self]; 15 | lineView.hidden = hiden; 16 | } 17 | 18 | - (UIImageView *)findHairlineFromView:(UIView *)view 19 | { 20 | if ([view isKindOfClass:[UIImageView class]] && view.frame.size.height <= 1.0) { 21 | return (UIImageView *)view; 22 | } 23 | 24 | for (UIView *subView in view.subviews) { 25 | UIImageView *imageView = [self findHairlineFromView:subView]; 26 | if (imageView) { 27 | return imageView; 28 | } 29 | } 30 | 31 | return nil; 32 | } 33 | 34 | @end 35 | -------------------------------------------------------------------------------- /ZBJCalendar/SingleSelection/ZBJSingleSelectionCell.h: -------------------------------------------------------------------------------- 1 | // 2 | // ZBJSingleSelectionCell.h 3 | // ZBJCalendar 4 | // 5 | // Created by gumpwang on 5/26/16. 6 | // Copyright © 2016 ZBJ. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | typedef CF_ENUM(NSInteger, ZBJCalendarCellState) { 12 | ZBJCalendarCellStateEmpty, 13 | ZBJCalendarCellStateNormal, 14 | ZBJCalendarCellStateSelected, 15 | }; 16 | 17 | 18 | 19 | @interface ZBJSingleSelectionCell : UICollectionViewCell 20 | 21 | @property (nonatomic, strong) NSDate *date; 22 | @property (nonatomic, assign) ZBJCalendarCellState cellState; 23 | 24 | @end 25 | -------------------------------------------------------------------------------- /ZBJCalendar/SingleSelection/ZBJSingleSelectionCell.m: -------------------------------------------------------------------------------- 1 | // 2 | // ZBJSingleSelectionCell.m 3 | // ZBJCalendar 4 | // 5 | // Created by gumpwang on 5/26/16. 6 | // Copyright © 2016 ZBJ. All rights reserved. 7 | // 8 | 9 | #import "ZBJSingleSelectionCell.h" 10 | #import "NSDate+ZBJAddition.h" 11 | 12 | @interface ZBJSingleSelectionCell() 13 | 14 | @property (nonatomic, strong) UILabel *dateLabel; 15 | @property (nonatomic, strong) NSCalendar *calendar; 16 | 17 | @property (nonatomic, strong) CALayer *topLine; 18 | @end 19 | @implementation ZBJSingleSelectionCell 20 | 21 | - (instancetype)initWithFrame:(CGRect)frame { 22 | if (self = [super initWithFrame:frame]) { 23 | [self.contentView addSubview:self.dateLabel]; 24 | [self.contentView.layer addSublayer:self.topLine]; 25 | self.calendar = [NSDate gregorianCalendar]; 26 | } 27 | return self; 28 | } 29 | 30 | - (void)layoutSubviews { 31 | [super layoutSubviews]; 32 | self.topLine.frame = CGRectMake(0, 0, CGRectGetWidth(self.frame), 0.5); 33 | self.dateLabel.center = CGPointMake(CGRectGetWidth(self.frame) / 2, CGRectGetHeight(self.frame) / 2 - 13); 34 | } 35 | 36 | 37 | - (void)setDate:(NSDate *)date { 38 | _date = date; 39 | if (_date) { 40 | self.dateLabel.text = [NSString stringWithFormat:@"%ld", [self.calendar component:NSCalendarUnitDay fromDate:_date]]; 41 | } else { 42 | self.dateLabel.text = nil; 43 | } 44 | } 45 | 46 | - (void)setCellState:(ZBJCalendarCellState)cellState { 47 | _cellState = cellState; 48 | switch (_cellState) { 49 | case ZBJCalendarCellStateEmpty: { 50 | self.dateLabel.text = nil; 51 | self.dateLabel.textColor = [UIColor whiteColor]; 52 | self.dateLabel.backgroundColor = [UIColor whiteColor]; 53 | self.dateLabel.layer.cornerRadius = 0; 54 | 55 | self.topLine.hidden = YES; 56 | break; 57 | } 58 | case ZBJCalendarCellStateNormal: { 59 | self.dateLabel.backgroundColor = [UIColor whiteColor]; 60 | self.dateLabel.layer.cornerRadius = 0; 61 | 62 | if ([self.date isWeekend]) { 63 | self.dateLabel.textColor = [UIColor lightGrayColor]; 64 | } else if ([self.date isToday]) { 65 | self.dateLabel.textColor = [UIColor colorWithRed:255.0/255.0 green:60.0/255.0 blue:57.0/255.0 alpha:1.0]; 66 | } else { 67 | self.dateLabel.textColor = [UIColor darkTextColor]; 68 | } 69 | 70 | self.topLine.hidden = NO; 71 | break; 72 | } 73 | case ZBJCalendarCellStateSelected: { 74 | self.dateLabel.textColor = [UIColor whiteColor]; 75 | self.dateLabel.layer.cornerRadius = 17; 76 | 77 | if ([self.date isToday]) { 78 | self.dateLabel.backgroundColor = [UIColor colorWithRed:255.0/255.0 green:60.0/255.0 blue:57.0/255.0 alpha:1.0]; 79 | } else { 80 | self.dateLabel.backgroundColor = [UIColor darkTextColor]; 81 | } 82 | 83 | self.topLine.hidden = NO; 84 | break; 85 | } 86 | default: 87 | break; 88 | } 89 | 90 | [self layoutSubviews]; 91 | } 92 | 93 | #pragma mark - getters 94 | - (UILabel *)dateLabel { 95 | if (!_dateLabel) { 96 | _dateLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 34, 34)]; 97 | _dateLabel.textAlignment = NSTextAlignmentCenter; 98 | _dateLabel.font = [UIFont systemFontOfSize:17]; 99 | _dateLabel.textColor = [UIColor darkTextColor]; 100 | _dateLabel.clipsToBounds = YES; 101 | } 102 | return _dateLabel; 103 | } 104 | 105 | - (CALayer *)topLine { 106 | if (!_topLine) { 107 | _topLine = [CALayer layer]; 108 | _topLine.frame = CGRectMake(0, 0, CGRectGetWidth(self.frame), 0.5); 109 | _topLine.backgroundColor = [UIColor colorWithRed:204.0/255.0 green:204.0/255.0 blue:204.0/255.0 alpha:1.0].CGColor; 110 | } 111 | return _topLine; 112 | } 113 | @end 114 | -------------------------------------------------------------------------------- /ZBJCalendar/SingleSelection/ZBJSingleSelectionController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ZBJSingleSelectionController.h 3 | // ZBJCalendar 4 | // 5 | // Created by gumpwang on 5/26/16. 6 | // Copyright © 2016 ZBJ. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ZBJSingleSelectionController : UIViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /ZBJCalendar/SingleSelection/ZBJSingleSelectionController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ZBJSingleSelectionController.m 3 | // ZBJCalendar 4 | // 5 | // Created by gumpwang on 5/26/16. 6 | // Copyright © 2016 ZBJ. All rights reserved. 7 | // 8 | 9 | #import "ZBJSingleSelectionController.h" 10 | #import "ZBJCalendar.h" 11 | #import "ZBJSingleSelectionCell.h" 12 | #import "ZBJSingleSelectionHeaderView.h" 13 | #import "UINavigationBar+ZBJAddition.h" 14 | 15 | @interface ZBJSingleSelectionController () 16 | 17 | @property (nonatomic, strong) ZBJCalendarView *calendarView; 18 | @property (nonatomic, strong) NSDate *selectedDate; 19 | 20 | @end 21 | @implementation ZBJSingleSelectionController 22 | 23 | - (void)viewWillAppear:(BOOL)animated { 24 | [super viewWillAppear:animated]; 25 | [self.navigationController.navigationBar hidenHairLine:YES]; 26 | } 27 | 28 | -(void)viewWillDisappear:(BOOL)animated { 29 | [super viewWillDisappear:animated]; 30 | [self.navigationController.navigationBar hidenHairLine:NO]; 31 | } 32 | 33 | - (void)viewDidLoad { 34 | [super viewDidLoad]; 35 | self.view.backgroundColor = [UIColor whiteColor]; 36 | 37 | // 获取当前日期 38 | NSDate *today = [NSDate today]; 39 | 40 | // NSDate *today = [NSDate date]; 41 | // 获取6个月后的最后一天 42 | NSCalendar *calendar = [NSDate gregorianCalendar]; 43 | NSDateComponents *components = [calendar components:NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitDay fromDate:today]; 44 | 45 | NSDate *firstDate = [calendar dateFromComponents:components]; 46 | 47 | components.month = components.month + 6 + 1; 48 | components.day = 0; 49 | NSDate *lastDate = [calendar dateFromComponents:components]; 50 | 51 | self.calendarView.firstDate = firstDate; 52 | self.calendarView.lastDate = lastDate; 53 | 54 | [self.view addSubview:self.calendarView]; 55 | 56 | self.selectedDate = firstDate; 57 | } 58 | 59 | - (void)didReceiveMemoryWarning { 60 | [super didReceiveMemoryWarning]; 61 | } 62 | 63 | 64 | #pragma mark - ZBJCalendarDataSource 65 | - (void)calendarView:(ZBJCalendarView *)calendarView configureCell:(ZBJSingleSelectionCell *)cell forDate:(NSDate *)date { 66 | 67 | cell.date = date; 68 | if (date) { 69 | if ([date isEqualToDate:self.selectedDate]) { 70 | cell.cellState = ZBJCalendarCellStateSelected; 71 | } else { 72 | cell.cellState = ZBJCalendarCellStateNormal; 73 | } 74 | } else { 75 | cell.cellState = ZBJCalendarCellStateEmpty; 76 | } 77 | } 78 | 79 | - (void)calendarView:(ZBJCalendarView *)calendarView configureSectionHeaderView:(ZBJSingleSelectionHeaderView *)headerView firstDateOfMonth:(NSDate *)firstDateOfMonth { 80 | headerView.firstDateOfMonth = firstDateOfMonth; 81 | } 82 | 83 | - (void)calendarView:(ZBJCalendarView *)calendarView configureWeekDayLabel:(UILabel *)dayLabel atWeekDay:(NSInteger)weekDay { 84 | dayLabel.font = [UIFont systemFontOfSize:13]; 85 | if (weekDay == 0 || weekDay == 6) { 86 | dayLabel.textColor = [UIColor lightGrayColor]; 87 | } 88 | } 89 | 90 | #pragma mark - ZBJCalendarDelegate 91 | - (void)calendarView:(ZBJCalendarView *)calendarView didSelectDate:(NSDate *)date ofCell:(ZBJSingleSelectionCell *)cell { 92 | 93 | NSDate *oldDate = [self.selectedDate copy]; 94 | self.selectedDate = date; 95 | 96 | [calendarView reloadItemsAtDates:[NSMutableSet setWithObjects:oldDate, self.selectedDate, nil]]; 97 | // if (date) { 98 | NSLog(@"selected date is : %@", self.selectedDate); 99 | // cell.cellState = ZBJCalendarCellStateSelected; 100 | // } 101 | 102 | } 103 | 104 | 105 | #pragma mark - 106 | - (ZBJCalendarView *)calendarView { 107 | if (!_calendarView) { 108 | _calendarView = [[ZBJCalendarView alloc] initWithFrame:CGRectMake(0, 64, CGRectGetWidth(self.view.frame), CGRectGetHeight(self.view.frame) - 64)]; 109 | [_calendarView registerCellClass:[ZBJSingleSelectionCell class] withReuseIdentifier:@"singleCell"]; 110 | [_calendarView registerSectionHeader:[ZBJSingleSelectionHeaderView class] withReuseIdentifier:@"singleSectionHeader"]; 111 | _calendarView.selectionMode = ZBJSelectionModeSingle; 112 | _calendarView.dataSource = self; 113 | _calendarView.delegate = self; 114 | _calendarView.backgroundColor = [UIColor whiteColor]; 115 | _calendarView.contentInsets = UIEdgeInsetsMake(0, 14, 0, 14); 116 | _calendarView.cellScale = 140.0 / 100.0; 117 | _calendarView.sectionHeaderHeight = 27; 118 | _calendarView.weekViewHeight = 20; 119 | _calendarView.weekView.backgroundColor = [UIColor colorWithRed:249.0f/255.0f green:249.0f/255.0f blue:249.0f/255.0f alpha:1.0]; 120 | } 121 | return _calendarView; 122 | } 123 | @end 124 | -------------------------------------------------------------------------------- /ZBJCalendar/SingleSelection/ZBJSingleSelectionHeaderView.h: -------------------------------------------------------------------------------- 1 | // 2 | // ZBJSingleSelectionHeaderView.h 3 | // ZBJCalendar 4 | // 5 | // Created by gumpwang on 5/26/16. 6 | // Copyright © 2016 ZBJ. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ZBJSingleSelectionHeaderView : UICollectionReusableView 12 | 13 | @property (nonatomic, strong) NSDate *firstDateOfMonth; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /ZBJCalendar/SingleSelection/ZBJSingleSelectionHeaderView.m: -------------------------------------------------------------------------------- 1 | // 2 | // ZBJSingleSelectionHeaderView.m 3 | // ZBJCalendar 4 | // 5 | // Created by wanggang on 5/26/16. 6 | // Copyright © 2016 ZBJ. All rights reserved. 7 | // 8 | 9 | #import "ZBJSingleSelectionHeaderView.h" 10 | #import "ZBJCalendar.h" 11 | 12 | @interface ZBJSingleSelectionHeaderView () 13 | 14 | @property (nonatomic, strong) UILabel *monthLabel; 15 | @property (nonatomic, assign) NSInteger weekday; 16 | @end 17 | @implementation ZBJSingleSelectionHeaderView 18 | 19 | 20 | - (id)initWithFrame:(CGRect)frame { 21 | self = [super initWithFrame:frame]; 22 | if (self) { 23 | [self addSubview:self.monthLabel]; 24 | } 25 | return self; 26 | } 27 | 28 | - (void)layoutSubviews { 29 | [super layoutSubviews]; 30 | self.monthLabel.frame = CGRectMake(0, 0, CGRectGetWidth(self.monthLabel.frame), 20); 31 | self.monthLabel.center = CGPointMake(25 * ((self.weekday - 1) * 2 + 1), CGRectGetHeight(self.frame) / 2); 32 | } 33 | 34 | #pragma mark - setters 35 | - (void)setFirstDateOfMonth:(NSDate *)firstDateOfMonth { 36 | _firstDateOfMonth = firstDateOfMonth; 37 | NSCalendar *calendar = [NSDate gregorianCalendar]; 38 | NSDateComponents *components = [calendar components:NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitWeekday fromDate:firstDateOfMonth]; 39 | self.weekday = components.weekday; 40 | self.monthLabel.text = [NSString stringWithFormat:@"%ld月", components.month]; 41 | [self.monthLabel sizeToFit]; 42 | [self layoutSubviews]; 43 | } 44 | 45 | 46 | #pragma mark - getters 47 | - (UILabel *)monthLabel { 48 | if (!_monthLabel) { 49 | _monthLabel = [[UILabel alloc] init]; 50 | _monthLabel.font = [UIFont systemFontOfSize:17]; 51 | _monthLabel.textColor = [UIColor darkTextColor]; 52 | } 53 | return _monthLabel; 54 | } 55 | @end 56 | -------------------------------------------------------------------------------- /ZBJCalendar/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // ZBJCalendar 4 | // 5 | // Created by gumpwang on 15/12/8. 6 | // Copyright © 2015年 ZBJ. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ViewController : UITableViewController 12 | 13 | 14 | @end 15 | 16 | -------------------------------------------------------------------------------- /ZBJCalendar/ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.m 3 | // ZBJCalendar 4 | // 5 | // Created by gumpwang on 15/12/8. 6 | // Copyright © 2015年 ZBJ. All rights reserved. 7 | // 8 | 9 | #import "ViewController.h" 10 | #import "ZBJCalendarShowView.h" 11 | #import "ZBJSimpleRangeSelectionController.h" 12 | #import "ZBJComplexRangeSelectionController.h" 13 | #import "ZBJCalendarDates.h" 14 | #import "ZBJSingleSelectionController.h" 15 | #import "ZBJSimpleMutipleSelectionViewController.h" 16 | 17 | static NSString * const ZBJCellIdentifier = @"cell"; 18 | 19 | @interface ViewController () 20 | 21 | @property (nonatomic, strong) NSArray *tableData; 22 | 23 | @property (nonatomic, strong) NSDate *startDate; 24 | @property (nonatomic, strong) NSDate *endDate; 25 | 26 | @property (nonatomic, strong) ZBJSimpleRangeSelectionController *rangeController; 27 | @property (nonatomic, strong) ZBJComplexRangeSelectionController *advanceCalController; 28 | 29 | @property (nonatomic, strong) ZBJSimpleMutipleSelectionViewController *mutipleSelectionController; 30 | @end 31 | 32 | @implementation ViewController 33 | 34 | - (void)viewWillAppear:(BOOL)animated { 35 | [super viewWillAppear:animated]; 36 | 37 | } 38 | 39 | - (void)viewDidLoad { 40 | [super viewDidLoad]; 41 | self.tableView.tableFooterView = [UIView new]; 42 | } 43 | 44 | - (void)didReceiveMemoryWarning { 45 | [super didReceiveMemoryWarning]; 46 | } 47 | 48 | 49 | #pragma mark - UITableViewDataSource 50 | - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { 51 | return self.tableData.count; 52 | } 53 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { 54 | return [self.tableData[section][@"cells"] count]; 55 | } 56 | 57 | - (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section { 58 | return self.tableData[section][@"sectionTitle"]; 59 | } 60 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { 61 | UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:ZBJCellIdentifier forIndexPath:indexPath]; 62 | 63 | NSDictionary *cellData = self.tableData[indexPath.section][@"cells"][indexPath.row]; 64 | cell.textLabel.text = cellData[@"cellTitle"]; 65 | return cell; 66 | } 67 | 68 | 69 | #pragma mark - UITableViewDelegate 70 | - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { 71 | [tableView deselectRowAtIndexPath:indexPath animated:YES]; 72 | 73 | switch (indexPath.section) { 74 | case 0: { 75 | switch (indexPath.row) { 76 | case 0: { 77 | NSString *path = [[NSBundle mainBundle] pathForResource:@"calendar_dates" ofType:@"json"]; 78 | NSData *data = [NSData dataWithContentsOfFile:path]; 79 | 80 | NSError *error; 81 | ZBJCalendarDates *calendarDates; 82 | NSDictionary *dic = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingAllowFragments error:&error]; 83 | if (!error) { 84 | calendarDates = [[ZBJCalendarDates alloc] initWithDictionary:dic]; 85 | } 86 | 87 | ZBJCalendarShowView *controller = [[ZBJCalendarShowView alloc] initWithFrame:[UIApplication sharedApplication].keyWindow.frame calendarDates:calendarDates]; 88 | controller.alpha = 0.0; 89 | 90 | [[UIApplication sharedApplication].keyWindow addSubview:controller]; 91 | [UIView animateWithDuration:0.2f animations:^{ 92 | controller.alpha = 1.0; 93 | } completion:^(BOOL finished) { 94 | 95 | }]; 96 | break; 97 | } 98 | default: 99 | break; 100 | } 101 | break; 102 | } 103 | case 1: { 104 | switch (indexPath.row) { 105 | case 0: { 106 | ZBJSingleSelectionController *controller = [[ZBJSingleSelectionController alloc] init]; 107 | [self.navigationController pushViewController:controller animated:YES]; 108 | break; 109 | } 110 | default: 111 | break; 112 | } 113 | break; 114 | } 115 | case 2: { 116 | 117 | switch (indexPath.row) { 118 | case 0: { 119 | self.rangeController.startDate = self.startDate; 120 | self.rangeController.endDate = self.endDate; 121 | [self.navigationController pushViewController:self.rangeController animated:YES]; 122 | break; 123 | } 124 | case 1: { 125 | self.advanceCalController.startDate = self.startDate; 126 | self.advanceCalController.endDate = self.endDate; 127 | [self.navigationController pushViewController:self.advanceCalController animated:YES]; 128 | break; 129 | } 130 | default: 131 | break; 132 | } 133 | break; 134 | } 135 | case 3: { 136 | 137 | switch (indexPath.row) { 138 | case 0: 139 | [self.navigationController pushViewController:self.mutipleSelectionController animated:YES]; 140 | break; 141 | 142 | default: 143 | break; 144 | } 145 | } 146 | default: 147 | break; 148 | } 149 | } 150 | 151 | #pragma mark - range && advance delegate 152 | - (void)popViewController:(UIViewController *)viewController startDate:(NSDate *)startDate endDate:(NSDate *)endDate { 153 | [viewController.navigationController popViewControllerAnimated:YES]; 154 | 155 | NSLog(@"----> startDate : %@, endDate: %@", startDate, endDate); 156 | 157 | self.startDate = startDate; 158 | self.endDate = endDate; 159 | } 160 | 161 | #pragma mark - getter 162 | - (NSArray *)tableData { 163 | if (!_tableData) { 164 | _tableData = @[@{@"sectionTitle": @"Show", 165 | @"cells": @[@{@"cellTitle": @"view"}]}, 166 | @{@"sectionTitle": @"Single Selection", 167 | @"cells": @[@{@"cellTitle": @"general"}]}, 168 | @{@"sectionTitle": @"Range Selection", 169 | @"cells": @[@{@"cellTitle": @"simple"}, @{@"cellTitle": @"complex"}]}, 170 | @{@"sectionTitle": @"Mutiple Selection", 171 | @"cells": @[@{@"cellTitle": @"simple"}]} 172 | ]; 173 | } 174 | return _tableData; 175 | } 176 | 177 | - (ZBJSimpleRangeSelectionController *)rangeController { 178 | if (!_rangeController) { 179 | _rangeController = [ZBJSimpleRangeSelectionController new]; 180 | _rangeController.delegate = self; 181 | } 182 | return _rangeController; 183 | } 184 | 185 | - (ZBJComplexRangeSelectionController *)advanceCalController { 186 | if (!_advanceCalController) { 187 | _advanceCalController = [ZBJComplexRangeSelectionController new]; 188 | _advanceCalController.delegate = self; 189 | 190 | NSString *path = [[NSBundle mainBundle] pathForResource:@"calendar_dates" ofType:@"json"]; 191 | NSData *data = [NSData dataWithContentsOfFile:path]; 192 | 193 | NSError *error; 194 | ZBJCalendarDates *calendarDates; 195 | NSDictionary *dic = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingAllowFragments error:&error]; 196 | if (!error) { 197 | calendarDates = [[ZBJCalendarDates alloc] initWithDictionary:dic]; 198 | } 199 | 200 | _advanceCalController.firstDate = calendarDates.startDate; 201 | _advanceCalController.lastDate = calendarDates.endDate; 202 | _advanceCalController.dates = calendarDates.dates; 203 | _advanceCalController.minNights = 2; 204 | } 205 | return _advanceCalController; 206 | } 207 | 208 | - (ZBJSimpleMutipleSelectionViewController *)mutipleSelectionController { 209 | if (!_mutipleSelectionController) { 210 | _mutipleSelectionController = [[ZBJSimpleMutipleSelectionViewController alloc] init]; 211 | } 212 | return _mutipleSelectionController; 213 | } 214 | 215 | @end 216 | -------------------------------------------------------------------------------- /ZBJCalendar/ZBJCalendar/NSDate+IndexPath.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSDate+IndexPath.h 3 | // ZBJCalendar 4 | // 5 | // Created by gumpwang on 2/29/16. 6 | // Copyright © 2016 ZBJ. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | /** 12 | * This category provide methods used to corresponding relations between indexpath and date. 13 | */ 14 | @interface NSDate (IndexPath) 15 | 16 | + (NSDate *)dateForFirstDayInSection:(NSInteger)section firstDate:(NSDate *)firstDate; 17 | + (NSDate *)dateAtIndexPath:(NSIndexPath *)indexPath firstDate:(NSDate *)firstDate; 18 | + (NSIndexPath *)indexPathAtDate:(NSDate *)date firstDate:(NSDate *)firstDate; 19 | 20 | @end 21 | -------------------------------------------------------------------------------- /ZBJCalendar/ZBJCalendar/NSDate+IndexPath.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSDate+IndexPath.m 3 | // ZBJCalendar 4 | // 5 | // Created by gumpwang on 2/29/16. 6 | // Copyright © 2016 ZBJ. All rights reserved. 7 | // 8 | 9 | #import "NSDate+IndexPath.h" 10 | #import "NSDate+ZBJAddition.h" 11 | #import 12 | 13 | @implementation NSDate (IndexPath) 14 | 15 | + (NSDate *)dateForFirstDayInSection:(NSInteger)section firstDate:(NSDate *)firstDate { 16 | NSCalendar *calendar = [NSDate gregorianCalendar]; 17 | 18 | NSDateComponents *dateComponents = [NSDateComponents new]; 19 | dateComponents.month = section; 20 | return [calendar dateByAddingComponents:dateComponents toDate:[firstDate firstDateOfMonth] options:0]; 21 | } 22 | 23 | + (NSDate *)dateAtIndexPath:(NSIndexPath *)indexPath firstDate:(NSDate *)firstDate { 24 | NSDate *firstDay = [NSDate dateForFirstDayInSection:indexPath.section firstDate:firstDate]; 25 | NSInteger weekDay = [firstDay weekday]; 26 | NSDate *dateToReturn = nil; 27 | 28 | if (indexPath.row < (weekDay - 1) || indexPath.row > weekDay - 1 + [NSDate numberOfDaysInMonth:firstDay] - 1) { 29 | dateToReturn = nil; 30 | } else { 31 | NSCalendar *calendar = [NSDate gregorianCalendar]; 32 | 33 | NSDateComponents *components = [calendar components:NSCalendarUnitMonth | NSCalendarUnitDay fromDate:firstDay]; 34 | [components setDay:indexPath.row - (weekDay - 1)]; 35 | [components setMonth:indexPath.section]; 36 | dateToReturn = [calendar dateByAddingComponents:components toDate:[firstDate firstDateOfMonth] options:0]; 37 | } 38 | return dateToReturn; 39 | } 40 | 41 | + (NSIndexPath *)indexPathAtDate:(NSDate *)date firstDate:(NSDate *)firstDate { 42 | NSCalendar *calendar = [NSDate gregorianCalendar]; 43 | NSDateComponents *components = [calendar components:NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitDay fromDate:date]; 44 | 45 | NSDateComponents *firstDateComponents = [calendar components:NSCalendarUnitYear | NSCalendarUnitMonth fromDate:firstDate]; 46 | 47 | NSDate *firstDateOfMonth = [date firstDateOfMonth]; 48 | NSDateComponents *firstDateOfMonthComponents = [calendar components:NSCalendarUnitWeekday fromDate:firstDateOfMonth]; 49 | 50 | NSInteger section = (components.year - firstDateComponents.year) * 12 + components.month - firstDateComponents.month; 51 | NSInteger index = firstDateOfMonthComponents.weekday + components.day - 2; 52 | 53 | return [NSIndexPath indexPathForItem:index inSection:section]; 54 | } 55 | 56 | @end 57 | -------------------------------------------------------------------------------- /ZBJCalendar/ZBJCalendar/NSDate+ZBJAddition.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSDate+ZBJAddition.h 3 | // ZBJCalendar 4 | // 5 | // Created by gumpwang on 2/26/16. 6 | // Copyright © 2016 ZBJ. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | /** 12 | * This category provide utility methods for `NSDate`. 13 | */ 14 | @interface NSDate (ZBJAddition) 15 | 16 | + (NSCalendar *)gregorianCalendar; 17 | + (NSLocale *)locale; 18 | 19 | + (NSDate *)today; 20 | + (NSInteger)numberOfMonthsFromDate:(NSDate *)fromDate toDate:(NSDate *)toDate; 21 | + (NSInteger)numberOfDaysFromMonth:(NSDate *)fromMonth toMonth:(NSDate *)toMonth; 22 | + (NSInteger)numberOfDaysInMonth:(NSDate *)date; 23 | + (NSInteger)numberOfNightsFromDate:(NSDate *)fromDate toDate:(NSDate *)toDate; 24 | 25 | - (NSDate *)firstDateOfMonth; 26 | - (NSDate *)firstDateOfWeek; 27 | - (NSDate *)lastDateOfMonth; 28 | 29 | - (NSInteger)weekday; 30 | 31 | - (BOOL)isToday; 32 | - (BOOL)isWeekend; 33 | 34 | //- (BOOL)isEqualToDate:(NSDate *)date toUnitGranularity:(NSCalendarUnit)unit; 35 | - (BOOL)isSameMonthWithDate:(NSDate *)date; 36 | @end 37 | -------------------------------------------------------------------------------- /ZBJCalendar/ZBJCalendar/NSDate+ZBJAddition.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSDate+ZBJAddition.m 3 | // ZBJCalendar 4 | // 5 | // Created by gumpwang on 2/26/16. 6 | // Copyright © 2016 ZBJ. All rights reserved. 7 | // 8 | 9 | #import "NSDate+ZBJAddition.h" 10 | #import 11 | 12 | const char * const JmoCalendarStoreKey = "jmo.calendar"; 13 | const char * const JmoLocaleStoreKey = "jmo.locale"; 14 | 15 | @implementation NSDate (ZBJAddition) 16 | 17 | #pragma mark - 18 | + (void)setGregorianCalendar:(NSCalendar *)gregorianCalendar 19 | { 20 | objc_setAssociatedObject(self, JmoCalendarStoreKey, gregorianCalendar, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 21 | } 22 | 23 | + (NSCalendar *)gregorianCalendar 24 | { 25 | NSCalendar *cal = objc_getAssociatedObject(self, JmoCalendarStoreKey); 26 | if (nil == cal) { 27 | cal = [[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierGregorian]; 28 | [cal setTimeZone:[NSTimeZone timeZoneWithAbbreviation:@"GMT"]]; 29 | [cal setLocale:[self locale]]; 30 | [self setGregorianCalendar:cal]; 31 | } 32 | return cal; 33 | } 34 | 35 | + (void)setLocal:(NSLocale *)locale 36 | { 37 | objc_setAssociatedObject(self, JmoLocaleStoreKey, locale, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 38 | } 39 | 40 | + (NSLocale *)locale 41 | { 42 | NSLocale *locale = objc_getAssociatedObject(self, JmoLocaleStoreKey); 43 | if (nil == locale) { 44 | locale = [[NSLocale alloc] initWithLocaleIdentifier:@"fr_FR"]; 45 | [self setLocal:locale]; 46 | } 47 | return locale; 48 | } 49 | 50 | 51 | #pragma mark - 52 | + (NSDate *)today { 53 | NSDate *sourceDate = [NSDate date]; 54 | 55 | NSTimeZone *sourceTimeZone = [NSTimeZone timeZoneWithAbbreviation:@"GMT"]; 56 | NSTimeZone *destinationTimeZone = [NSTimeZone systemTimeZone]; 57 | 58 | NSInteger sourceGMTOffset = [sourceTimeZone secondsFromGMTForDate:sourceDate]; 59 | NSInteger destinationGMTOffset = [destinationTimeZone secondsFromGMTForDate:sourceDate]; 60 | NSTimeInterval interval = destinationGMTOffset - sourceGMTOffset; 61 | 62 | return [[NSDate alloc] initWithTimeInterval:interval sinceDate:sourceDate]; 63 | } 64 | + (NSInteger)numberOfMonthsFromDate:(NSDate *)fromDate toDate:(NSDate *)toDate { 65 | NSCalendar *calendar = [self gregorianCalendar]; 66 | NSDateComponents *components = [calendar components:NSCalendarUnitMonth fromDate:[fromDate firstDateOfMonth] toDate:[toDate lastDateOfMonth] options:NSCalendarMatchStrictly]; 67 | // 2016.9.16 - 2016.3.16 = 5 if use [fromDate firstDateOfMonth] and [toDate lastDateOfMonth], the result is 6. ok 68 | // 2017.3.16 - 2016.3.16 = 12 69 | return components.month + 1; 70 | } 71 | 72 | + (NSInteger)numberOfDaysFromMonth:(NSDate *)fromMonth toMonth:(NSDate *)toMonth { 73 | NSCalendar *calendar = [self gregorianCalendar]; 74 | NSDate *firstDate = [fromMonth firstDateOfMonth]; 75 | NSDate *lastDate = [toMonth lastDateOfMonth]; 76 | NSDateComponents *components = [calendar components:NSCalendarUnitDay fromDate:firstDate toDate:lastDate options:NSCalendarMatchStrictly]; 77 | return components.day + 1; 78 | } 79 | 80 | + (NSInteger)numberOfDaysInMonth:(NSDate *)date { 81 | NSCalendar *calendar = [self gregorianCalendar]; 82 | NSRange range = [calendar rangeOfUnit:NSCalendarUnitDay inUnit:NSCalendarUnitMonth forDate:date]; 83 | return range.length; 84 | } 85 | 86 | + (NSInteger)numberOfNightsFromDate:(NSDate *)fromDate toDate:(NSDate *)toDate { 87 | NSCalendar *calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierGregorian]; 88 | unsigned int unitFlag = NSCalendarUnitDay; 89 | NSDateComponents *components = [calendar components:unitFlag fromDate:fromDate toDate:toDate options:0]; 90 | NSInteger days = [components day]; 91 | return days; 92 | } 93 | 94 | #pragma mark - 95 | - (NSDate *)firstDateOfMonth { 96 | NSCalendar *calendar = [self.class gregorianCalendar]; 97 | NSDateComponents *components = [calendar components:NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitDay fromDate:self]; 98 | components.day = 1; 99 | return [calendar dateFromComponents:components]; 100 | } 101 | 102 | - (NSDate *)firstDateOfWeek { 103 | 104 | NSInteger weekDay = self.weekday; 105 | 106 | NSCalendar *calendar = [self.class gregorianCalendar]; 107 | NSDateComponents *components = [calendar components:NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitDay | NSCalendarUnitWeekday fromDate:self]; 108 | components.day = components.day - weekDay + 1; 109 | return [calendar dateFromComponents:components]; 110 | } 111 | 112 | - (NSDate *)lastDateOfMonth { 113 | NSCalendar *calendar = [self.class gregorianCalendar]; 114 | NSDateComponents *components = [calendar components:NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitDay fromDate:self]; 115 | components.month = components.month + 1; 116 | components.day = 0; 117 | return [calendar dateFromComponents:components]; 118 | } 119 | 120 | - (NSInteger)weekday { 121 | NSCalendar *calendar = [self.class gregorianCalendar]; 122 | NSDateComponents *compoents = [calendar components:NSCalendarUnitWeekday fromDate:self]; 123 | return compoents.weekday; 124 | } 125 | 126 | - (BOOL)isToday { 127 | NSCalendar *calendar = [self.class gregorianCalendar]; 128 | NSDateComponents *otherDay = [calendar components:NSCalendarUnitEra | NSCalendarUnitYear |NSCalendarUnitMonth | NSCalendarUnitDay fromDate:self]; 129 | NSDateComponents *today = [[NSCalendar currentCalendar] components:NSCalendarUnitEra | NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitDay fromDate:[NSDate date]]; 130 | if([today day] == [otherDay day] && 131 | [today month] == [otherDay month] && 132 | [today year] == [otherDay year] && 133 | [today era] == [otherDay era]) { 134 | return YES; 135 | } 136 | return NO; 137 | } 138 | 139 | - (BOOL)isWeekend { 140 | NSCalendar *calendar = [self.class gregorianCalendar]; 141 | NSRange weekdayRange = [calendar maximumRangeOfUnit:NSCalendarUnitWeekday]; 142 | NSDateComponents *components = [calendar components:NSCalendarUnitWeekday fromDate:self]; 143 | NSUInteger weekdayOfDate = [components weekday]; 144 | 145 | if (weekdayOfDate == weekdayRange.location || weekdayOfDate == weekdayRange.length) { 146 | //the date falls somewhere on the first or last days of the week 147 | return YES; 148 | } 149 | return NO; 150 | } 151 | 152 | - (BOOL)isSameMonthWithDate:(NSDate *)date { 153 | 154 | NSCalendar *calendar = [self.class gregorianCalendar]; 155 | NSDateComponents *components = [calendar components:NSCalendarUnitMonth fromDate:self]; 156 | NSDateComponents *toComponents = [calendar components:NSCalendarUnitMonth fromDate:date]; 157 | 158 | return components.month == toComponents.month; 159 | } 160 | 161 | 162 | 163 | 164 | @end 165 | -------------------------------------------------------------------------------- /ZBJCalendar/ZBJCalendar/ZBJCalendar.h: -------------------------------------------------------------------------------- 1 | // 2 | // ZBJCalendar.h 3 | // ZBJCalendar 4 | // 5 | // Created by gumpwang on 3/2/16. 6 | // Copyright © 2016 ZBJ. All rights reserved. 7 | // 8 | 9 | #ifndef ZBJCalendar_h 10 | #define ZBJCalendar_h 11 | 12 | #import "ZBJCalendarView.h" 13 | #import "ZBJCalendarWeekView.h" 14 | #import "NSDate+ZBJAddition.h" 15 | #import "NSDate+IndexPath.h" 16 | 17 | #endif /* ZBJCalendar_h */ 18 | -------------------------------------------------------------------------------- /ZBJCalendar/ZBJCalendar/ZBJCalendarView.h: -------------------------------------------------------------------------------- 1 | // 2 | // ZBJCalendarView.h 3 | // ZBJCalendar 4 | // 5 | // Created by gumpwang on 2/24/16. 6 | // Copyright © 2016 ZBJ. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "ZBJCalendarWeekView.h" 11 | 12 | typedef CF_ENUM(NSInteger, ZBJSelectionMode) { 13 | ZBJSelectionModeDisable = 0, // Can not select 14 | ZBJSelectionModeSingle, // Single selection mode 15 | ZBJSelectionModeMutiple, // Mutile selection mode 16 | }; 17 | 18 | typedef CF_ENUM(NSInteger, ZBJCalendarViewHeadStyle) { 19 | ZBJCalendarViewHeadStyleCurrentMonth = 0, // Show current month of the first day. 20 | ZBJCalendarViewHeadStyleCurrentWeek, // Show current week of the first day, the days before current will hiden. 21 | }; 22 | 23 | @protocol ZBJCalendarDataSource, ZBJCalendarDelegate; 24 | 25 | /** 26 | * `ZBJCalendarView` provide a convenience way to write a calendar view. The concept of `ZBJCalendarView` is `UICollectionView` or `UITableView`, some properties contains `dataSource` which to used to custom cells or headers and `delegate` which used to handle the display and behaviour of the day cells. 27 | * 28 | * The structure of `ZBJCalendarView` is simple, it contains a `weekView` as public and a `collectionView` as private. 29 | - The `weekView` is an instance of `ZBJCalendarWeekView` that you can implements the methods in `dataSource` about week view. 30 | - The `collectionView` is an instance of `UICollectionView`, the UICollectionView cell correspond the day of the calendar. 31 | * 32 | * The usage of `ZBJCalendarView` is simple, is familiar like `UITableView` or `UICollectionView` 33 | - first, you should point the `firstDate` and `lastDate`, 34 | - second, like `UITableView` or `UICollectionView`, it must registe a cell through `- (void)registerCellClass:(id)clazz withReuseIdentifier:(NSString *)identifier;`, 35 | - finally, conform the `ZBJCalendarDataSource` protocol and must implement `- (void)calendarView:(ZBJCalendarView *)calendarView configureCell:(id)cell forDate:(NSDate *)date;` as `@required`. 36 | * 37 | */ 38 | @interface ZBJCalendarView : UIView 39 | 40 | /** 41 | * A reference of `ZBJCalendarDataSource` used to custom calendar day cells. 42 | */ 43 | @property (nonatomic, weak) id dataSource; 44 | 45 | /** 46 | * A reference of `ZBJCalendarDelegate` used to handle the calendar day cells's display and behaviours. 47 | */ 48 | @property (nonatomic, weak) id delegate; 49 | 50 | /** 51 | * The tow properties `firstDate` and `lastDate` are necessary for calendar. 52 | - `firstDate` is the start date of the calendar 53 | - `lastDate` is the end date of the calendar 54 | */ 55 | @property (nonatomic, strong) NSDate *firstDate; 56 | @property (nonatomic, strong) NSDate *lastDate; 57 | 58 | /** 59 | * `weekView` is used to display the week days. 60 | * It's a public property that means you can define the style as you like. 61 | */ 62 | @property (nonatomic, strong) ZBJCalendarWeekView *weekView; 63 | 64 | /** 65 | * `ZBJCalendarView` has three selection mode, it's defined as a enum `ZBJSelectionMode`. 66 | * The concept of selection mode is to control the `collectionView`'s `allowsSelection` and `allowsMultipleSelection`. 67 | * Default value is `ZBJSelectionModeRange` 68 | */ 69 | @property (nonatomic, assign) ZBJSelectionMode selectionMode; 70 | 71 | /** 72 | * This property used to control whether the calendar view is selectable or not directly. 73 | * As `selectionMode`, it controler the collectionView's `allowsSelection` property. 74 | */ 75 | @property (nonatomic, assign) BOOL allowsSelection; 76 | 77 | /** 78 | * This `headStyle` is the style of the current month display style. 79 | */ 80 | @property (nonatomic, assign) ZBJCalendarViewHeadStyle headStyle; 81 | 82 | ///------------------------------- 83 | /// @name layout properties 84 | ///------------------------------- 85 | 86 | /** 87 | * The calendarView inner padding. 88 | */ 89 | @property (nonatomic, assign) UIEdgeInsets contentInsets; 90 | @property (nonatomic, assign) CGFloat minimumLineSpacing; 91 | @property (nonatomic, assign) CGFloat weekViewHeight; 92 | @property (nonatomic, assign) CGFloat sectionHeaderHeight; 93 | @property (nonatomic, assign) CGFloat sectionFooterHeight; 94 | 95 | /** 96 | * Calendar offset which is not contains contentInset 97 | * `contentOffset.x` = `collectionView.contentOffset.x` + `collectionView.contentInset.left` 98 | * `contentOffset.y` = `collectionView.contentOffset.y` + `collectionView.contentInset.top` 99 | */ 100 | @property (nonatomic, assign) CGPoint contentOffset; 101 | 102 | /** 103 | * Calendar content size which is not contains weekHeight in vertical. 104 | */ 105 | @property (nonatomic, assign, readonly) CGSize contentSize; 106 | 107 | /** 108 | * In calendar view, the cell width is calculated as by this expression, `cellWidth = (calendar.width - contentInsets.left - contentInsets.right) / 7` 109 | cellWidth is variable with the screen changing, 110 | So, we define a scale to calculate cellHeight is convenient, `cellScale = cellHeight / cellWidth`. 111 | */ 112 | @property (nonatomic, assign) CGFloat cellScale; 113 | 114 | /** 115 | * Registe a class for calendar day cell. 116 | * 117 | * @param clazz Calendar day cell, it should be a subclass of `UICollectionViewCell` 118 | * @param identifier The cell's reuse identifier. 119 | */ 120 | - (void)registerCellClass:(id)clazz withReuseIdentifier:(NSString *)identifier; 121 | 122 | /** 123 | * This two methods used to registe a class for calendar section header or footer view. 124 | * 125 | * @param clazz Calendar section header or footer view, it should be a subclass of `UICollectionReusableView` 126 | * @param identifier The view's reuse identifier. 127 | */ 128 | - (void)registerSectionHeader:(id)clazz withReuseIdentifier:(NSString *)identifier; 129 | - (void)registerSectionFooter:(id)clazz withReuseIdentifier:(NSString *)identifier; 130 | 131 | /** 132 | * Reloads everything from scratch, redisplays visible cells. 133 | */ 134 | - (void)reloadData; 135 | 136 | /** 137 | * Get corresponding cell with `date` 138 | * 139 | * @param date 140 | * 141 | * @return The corresponding cell. 142 | */ 143 | - (id)cellAtDate:(NSDate *)date; 144 | 145 | /** 146 | * Reload cell with `NSDate` set. 147 | * 148 | * @param dates 149 | */ 150 | - (void)reloadItemsAtDates:(NSSet *)dates; 151 | 152 | /** 153 | * Reload month section with `NSDate` set 154 | * 155 | * @param months month element in this set should contains year and month 156 | */ 157 | - (void)reloadItemsAtMonths:(NSSet *)months; 158 | 159 | /** 160 | * Set content offset of calendar view 161 | * 162 | * @param contentOffset 163 | * @param animated 164 | */ 165 | - (void)setContentOffset:(CGPoint)contentOffset animated:(BOOL)animated; 166 | 167 | @end 168 | /** 169 | * This protocol represents the cell data model object. 170 | * As such, it supplies the cell's information about appearance and data. 171 | */ 172 | @protocol ZBJCalendarDataSource 173 | 174 | @required 175 | /** 176 | * This method is used for configure the day cell with current `date`. 177 | * Implementers can setup `date` to cell and custom appearance for cell by this method. 178 | * 179 | * @param calendarView self 180 | * @param cell current reuse cell 181 | * @param date current date 182 | */ 183 | - (void)calendarView:(ZBJCalendarView *)calendarView configureCell:(id)cell forDate:(NSDate *)date; 184 | 185 | @optional 186 | /** 187 | * If you registe a section header or footer, you should configure them by them two methods, calendar will provide current `year` and `month` for you. 188 | * 189 | * @param calendarView self 190 | * @param headerView current reuse section header or footer view 191 | * @param firstDateOfMonth & lastDateOfMonth 192 | */ 193 | - (void)calendarView:(ZBJCalendarView *)calendarView configureSectionHeaderView:(id)headerView firstDateOfMonth:(NSDate *)firstDateOfMonth; 194 | - (void)calendarView:(ZBJCalendarView *)calendarView configureSectionFooterView:(id)headerView lastDateOfMonth:(NSDate *)lastDateOfMonth; 195 | 196 | /** 197 | * This method is used for configure the cell of the week view with the `weekDay` 198 | * 199 | * @param calendarView self 200 | * @param dayLabel week day label 201 | * @param weekDay current week day 202 | */ 203 | - (void)calendarView:(ZBJCalendarView *)calendarView configureWeekDayLabel:(UILabel *)dayLabel atWeekDay:(NSInteger)weekDay; 204 | @end 205 | 206 | @protocol ZBJCalendarDelegate 207 | 208 | @optional 209 | /** 210 | * Return whether the date is selectable 211 | * 212 | * @param calendarView self 213 | * @param date current date 214 | * 215 | * @return selectable of the calendarView cell 216 | */ 217 | - (BOOL)calendarView:(ZBJCalendarView *)calendarView shouldSelectDate:(NSDate *)date; 218 | 219 | /** 220 | * Correspond the selection event of the calendarView with current `date`. 221 | * 222 | * @param calendarView self 223 | * @param date current date 224 | */ 225 | - (void)calendarView:(ZBJCalendarView *)calendarView didSelectDate:(NSDate *)date ofCell:(id)cell; 226 | 227 | /** 228 | * ScrollView delegate methods 229 | * 230 | * @param scrollView UICollectionView 231 | */ 232 | - (void)scrollViewDidScroll:(UIScrollView *)scrollView; 233 | - (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView; 234 | - (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate; 235 | - (void)scrollViewWillBeginDecelerating:(UIScrollView *)scrollView; 236 | - (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView; 237 | 238 | @end 239 | -------------------------------------------------------------------------------- /ZBJCalendar/ZBJCalendar/ZBJCalendarWeekView.h: -------------------------------------------------------------------------------- 1 | // 2 | // ZBJCalendarWeekView.h 3 | // ZBJCalendar 4 | // 5 | // Created by gumpwang on 16/2/25. 6 | // Copyright © 2016年 ZBJ. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @protocol ZBJCalendarWeekViewDelegate; 12 | 13 | /** 14 | * `ZBJCalendarWeekView` is the default view for display week days. 15 | * By default, is contains 7 labels present monday to sunday. 16 | * You can custom weekView style by the public property `weekView` of `ZBJCalendarView`. 17 | * If you want to change each week day's style, you can conform the `ZBJCalendarWeekViewDelegate` and implement `- (void)calendarWeekView:(ZBJCalendarWeekView *)weekView configureWeekDayLabel:(UILabel *)dayLabel atWeekDay:(NSInteger)weekDay;` method. 18 | */ 19 | @interface ZBJCalendarWeekView : UIView 20 | 21 | /** 22 | * A reference of `ZBJCalendarWeekViewDelegate` which can custom week day label. 23 | */ 24 | @property (nonatomic, weak) id delegate; 25 | 26 | /** 27 | * The inner padding of week view. 28 | */ 29 | @property (nonatomic, assign) UIEdgeInsets contentInsets; // the inner padding 30 | 31 | /** 32 | * Default line at the bottom of week view. 33 | */ 34 | @property (nonatomic, strong) CALayer *bottomLine; 35 | 36 | /** 37 | * reload week View manual 38 | */ 39 | - (void)reloadWeekView; 40 | 41 | @end 42 | 43 | @protocol ZBJCalendarWeekViewDelegate 44 | 45 | @optional 46 | /** 47 | * Used to configure the weekday label with the `weekDay` 48 | * 49 | * @param weekView self 50 | * @param dayLabel weekday label 51 | * @param weekDay integer of the weekday 52 | */ 53 | - (void)calendarWeekView:(ZBJCalendarWeekView *)weekView configureWeekDayLabel:(UILabel *)dayLabel atWeekDay:(NSInteger)weekDay; 54 | 55 | @end 56 | -------------------------------------------------------------------------------- /ZBJCalendar/ZBJCalendar/ZBJCalendarWeekView.m: -------------------------------------------------------------------------------- 1 | // 2 | // ZBJCalendarWeekView.m 3 | // ZBJCalendar 4 | // 5 | // Created by gumpwang on 16/2/25. 6 | // Copyright © 2016年 ZBJ. All rights reserved. 7 | // 8 | 9 | #import "ZBJCalendarWeekView.h" 10 | #import "NSDate+ZBJAddition.h" 11 | 12 | @interface ZBJCalendarWeekView () 13 | @property (nonatomic, strong) NSMutableArray *adjustedSymbols; 14 | @end 15 | 16 | @implementation ZBJCalendarWeekView 17 | 18 | - (instancetype)initWithFrame:(CGRect)frame 19 | { 20 | self = [super initWithFrame:frame]; 21 | if (self) { 22 | 23 | self.backgroundColor = [UIColor whiteColor]; 24 | NSCalendar *calendar = [NSDate gregorianCalendar]; 25 | NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init]; 26 | dateFormatter.calendar = calendar; 27 | NSArray *weekdaySymbols = nil; 28 | 29 | weekdaySymbols = [dateFormatter veryShortWeekdaySymbols]; 30 | 31 | NSMutableArray *adjustedSymbols = [NSMutableArray arrayWithArray:weekdaySymbols]; 32 | for (NSInteger index = 0; index < (1 - calendar.firstWeekday + weekdaySymbols.count + 1); index++) { 33 | NSString *lastObject = [adjustedSymbols lastObject]; 34 | [adjustedSymbols removeLastObject]; 35 | [adjustedSymbols insertObject:lastObject atIndex:0]; 36 | } 37 | 38 | self.adjustedSymbols = adjustedSymbols; 39 | 40 | for (int i = 0 ; i < self.adjustedSymbols.count; i++) { 41 | CGFloat w = CGRectGetWidth(self.frame)/7; 42 | UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(w * i, 0, w, CGRectGetHeight(self.frame))]; 43 | label.tag = 10901 + i; 44 | label.textColor = [UIColor darkTextColor]; 45 | label.font = [UIFont systemFontOfSize:15]; 46 | label.text = [self.adjustedSymbols[i] uppercaseString]; 47 | label.textAlignment = NSTextAlignmentCenter; 48 | [self addSubview:label]; 49 | 50 | } 51 | 52 | // bottom line is default style, you 53 | if (!self.bottomLine) { 54 | self.bottomLine = [CALayer layer]; 55 | self.bottomLine.frame = CGRectMake(0, CGRectGetHeight(self.frame) - 0.5, CGRectGetWidth(self.frame), 0.5); 56 | self.bottomLine.backgroundColor = [UIColor colorWithRed:230.0/255.0 green:230.0/255.0 blue:233.0/255.0 alpha:1.0].CGColor; 57 | [self.layer addSublayer:self.bottomLine]; 58 | } 59 | } 60 | return self; 61 | } 62 | 63 | - (void)layoutSubviews { 64 | [super layoutSubviews]; 65 | for (int i = 0; i < 7; i++) { 66 | UILabel *label = [self viewWithTag:10901 + i]; 67 | CGFloat width = (self.frame.size.width - _contentInsets.left - _contentInsets.right) / 7; 68 | label.frame = CGRectMake(_contentInsets.left + i * width, 0, width, CGRectGetHeight(self.frame)); 69 | } 70 | self.bottomLine.frame = CGRectMake(0, CGRectGetHeight(self.frame) - 0.5, CGRectGetWidth(self.frame), 0.5); 71 | } 72 | 73 | - (void)reloadWeekView { 74 | for (int i = 0; i < self.adjustedSymbols.count; i++) { 75 | UILabel *label = [self viewWithTag:10901 + i]; 76 | if (label && _delegate && [_delegate respondsToSelector:@selector(calendarWeekView:configureWeekDayLabel:atWeekDay:)]) { 77 | [_delegate calendarWeekView:self configureWeekDayLabel:label atWeekDay:i]; 78 | } 79 | } 80 | } 81 | 82 | - (void)setDelegate:(id)delegate { 83 | _delegate = delegate; 84 | [self reloadWeekView]; 85 | } 86 | 87 | - (void)setContentInsets:(UIEdgeInsets)contentInsets { 88 | _contentInsets = contentInsets; 89 | [self layoutSubviews]; 90 | } 91 | 92 | @end 93 | -------------------------------------------------------------------------------- /ZBJCalendar/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // ZBJCalendar 4 | // 5 | // Created by gumpwang on 15/12/8. 6 | // Copyright © 2015年 ZBJ. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "AppDelegate.h" 11 | 12 | int main(int argc, char * argv[]) { 13 | @autoreleasepool { 14 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /ZBJCalendarTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /ZBJCalendarTests/ZBJCalendarTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // ZBJCalendarTests.m 3 | // ZBJCalendarTests 4 | // 5 | // Created by 王刚 on 15/12/8. 6 | // Copyright © 2015年 ZBJ. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ZBJCalendarTests : XCTestCase 12 | 13 | @end 14 | 15 | @implementation ZBJCalendarTests 16 | 17 | - (void)setUp { 18 | [super setUp]; 19 | // Put setup code here. This method is called before the invocation of each test method in the class. 20 | } 21 | 22 | - (void)tearDown { 23 | // Put teardown code here. This method is called after the invocation of each test method in the class. 24 | [super tearDown]; 25 | } 26 | 27 | - (void)testExample { 28 | // This is an example of a functional test case. 29 | // Use XCTAssert and related functions to verify your tests produce the correct results. 30 | } 31 | 32 | - (void)testPerformanceExample { 33 | // This is an example of a performance test case. 34 | [self measureBlock:^{ 35 | // Put the code you want to measure the time of here. 36 | }]; 37 | } 38 | 39 | @end 40 | -------------------------------------------------------------------------------- /ZBJCalendarUITests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /ZBJCalendarUITests/ZBJCalendarUITests.m: -------------------------------------------------------------------------------- 1 | // 2 | // ZBJCalendarUITests.m 3 | // ZBJCalendarUITests 4 | // 5 | // Created by 王刚 on 15/12/8. 6 | // Copyright © 2015年 ZBJ. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ZBJCalendarUITests : XCTestCase 12 | 13 | @end 14 | 15 | @implementation ZBJCalendarUITests 16 | 17 | - (void)setUp { 18 | [super setUp]; 19 | 20 | // Put setup code here. This method is called before the invocation of each test method in the class. 21 | 22 | // In UI tests it is usually best to stop immediately when a failure occurs. 23 | self.continueAfterFailure = NO; 24 | // UI tests must launch the application that they test. Doing this in setup will make sure it happens for each test method. 25 | [[[XCUIApplication alloc] init] launch]; 26 | 27 | // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this. 28 | } 29 | 30 | - (void)tearDown { 31 | // Put teardown code here. This method is called after the invocation of each test method in the class. 32 | [super tearDown]; 33 | } 34 | 35 | - (void)testExample { 36 | // Use recording to get started writing UI tests. 37 | // Use XCTAssert and related functions to verify your tests produce the correct results. 38 | } 39 | 40 | @end 41 | -------------------------------------------------------------------------------- /calendar_dates.json: -------------------------------------------------------------------------------- 1 | {"offer_id":17313,"start_date":"2016-03-10","end_date":"2017-03-10","dates":[{"date":"2016-03-10","available":false,"price":1000},{"date":"2016-03-11","available":false,"price":1099},{"date":"2016-03-12","available":false,"price":1092},{"date":"2016-03-13","available":false,"price":1092},{"date":"2016-03-14","available":false,"price":992},{"date":"2016-03-15","available":false,"price":992},{"date":"2016-03-16","available":true,"price":1096},{"date":"2016-03-17","available":false,"price":1130},{"date":"2016-03-18","available":false,"price":1130},{"date":"2016-03-19","available":false,"price":1130},{"date":"2016-03-20","available":false,"price":809},{"date":"2016-03-21","available":false,"price":809},{"date":"2016-03-22","available":false,"price":809},{"date":"2016-03-23","available":false,"price":910},{"date":"2016-03-24","available":false,"price":1021},{"date":"2016-03-25","available":false,"price":1021},{"date":"2016-03-26","available":false,"price":1021},{"date":"2016-03-27","available":false,"price":1021},{"date":"2016-03-28","available":false,"price":1074},{"date":"2016-03-29","available":false,"price":810},{"date":"2016-03-30","available":false,"price":810},{"date":"2016-03-31","available":false,"price":810},{"date":"2016-04-01","available":true,"price":1096},{"date":"2016-04-02","available":false,"price":809},{"date":"2016-04-03","available":false,"price":809},{"date":"2016-04-04","available":false,"price":809},{"date":"2016-04-05","available":false,"price":809},{"date":"2016-04-06","available":false,"price":809},{"date":"2016-04-07","available":false,"price":809},{"date":"2016-04-08","available":false,"price":809},{"date":"2016-04-09","available":false,"price":809},{"date":"2016-04-10","available":false,"price":809},{"date":"2016-04-11","available":false,"price":809},{"date":"2016-04-12","available":true,"price":1298},{"date":"2016-04-13","available":false,"price":1305},{"date":"2016-04-14","available":false,"price":1305},{"date":"2016-04-15","available":false,"price":1638},{"date":"2016-04-16","available":false,"price":1638},{"date":"2016-04-17","available":false,"price":1229},{"date":"2016-04-18","available":false,"price":1210},{"date":"2016-04-19","available":false,"price":1210},{"date":"2016-04-20","available":false,"price":807},{"date":"2016-04-21","available":false,"price":807},{"date":"2016-04-22","available":true,"price":1096},{"date":"2016-04-23","available":false,"price":1132},{"date":"2016-04-24","available":false,"price":1132},{"date":"2016-04-25","available":false,"price":1132},{"date":"2016-04-26","available":false,"price":1132},{"date":"2016-04-27","available":false,"price":1132},{"date":"2016-04-28","available":true,"price":1096},{"date":"2016-04-29","available":true,"price":1096},{"date":"2016-04-30","available":false,"price":816},{"date":"2016-05-01","available":false,"price":1440},{"date":"2016-05-02","available":false,"price":1440},{"date":"2016-05-03","available":true,"price":1096},{"date":"2016-05-04","available":true,"price":1096},{"date":"2016-05-05","available":true,"price":1096},{"date":"2016-05-06","available":true,"price":1096},{"date":"2016-05-07","available":true,"price":1096},{"date":"2016-05-08","available":true,"price":1096},{"date":"2016-05-09","available":true,"price":1096},{"date":"2016-05-10","available":true,"price":1096},{"date":"2016-05-11","available":true,"price":1096},{"date":"2016-05-12","available":true,"price":1096},{"date":"2016-05-13","available":true,"price":1096},{"date":"2016-05-14","available":true,"price":1096},{"date":"2016-05-15","available":true,"price":1096},{"date":"2016-05-16","available":true,"price":1096},{"date":"2016-05-17","available":true,"price":1096},{"date":"2016-05-18","available":true,"price":1096},{"date":"2016-05-19","available":true,"price":1096},{"date":"2016-05-20","available":true,"price":1096},{"date":"2016-05-21","available":true,"price":1096},{"date":"2016-05-22","available":true,"price":1096},{"date":"2016-05-23","available":true,"price":1096},{"date":"2016-05-24","available":true,"price":1096},{"date":"2016-05-25","available":true,"price":1096},{"date":"2016-05-26","available":true,"price":1096},{"date":"2016-05-27","available":true,"price":1096},{"date":"2016-05-28","available":true,"price":1096},{"date":"2016-05-29","available":true,"price":1096},{"date":"2016-05-30","available":true,"price":1096},{"date":"2016-05-31","available":true,"price":1096},{"date":"2016-06-01","available":true,"price":1096},{"date":"2016-06-02","available":true,"price":1096},{"date":"2016-06-03","available":true,"price":1096},{"date":"2016-06-04","available":true,"price":1096},{"date":"2016-06-05","available":true,"price":1096},{"date":"2016-06-06","available":true,"price":1096},{"date":"2016-06-07","available":true,"price":1096},{"date":"2016-06-08","available":true,"price":1096},{"date":"2016-06-09","available":true,"price":1096},{"date":"2016-06-10","available":true,"price":1096},{"date":"2016-06-11","available":true,"price":1096},{"date":"2016-06-12","available":true,"price":1096},{"date":"2016-06-13","available":true,"price":1096},{"date":"2016-06-14","available":true,"price":1096},{"date":"2016-06-15","available":true,"price":1096},{"date":"2016-06-16","available":true,"price":1096},{"date":"2016-06-17","available":false,"price":1095},{"date":"2016-06-18","available":false,"price":1095},{"date":"2016-06-19","available":false,"price":1095},{"date":"2016-06-20","available":true,"price":1096},{"date":"2016-06-21","available":true,"price":1096},{"date":"2016-06-22","available":false,"price":1464},{"date":"2016-06-23","available":false,"price":1464},{"date":"2016-06-24","available":false,"price":1464},{"date":"2016-06-25","available":true,"price":1096},{"date":"2016-06-26","available":true,"price":1096},{"date":"2016-06-27","available":true,"price":1096},{"date":"2016-06-28","available":true,"price":1096},{"date":"2016-06-29","available":true,"price":1096},{"date":"2016-06-30","available":true,"price":1096},{"date":"2016-07-01","available":true,"price":2061},{"date":"2016-07-02","available":true,"price":2061},{"date":"2016-07-03","available":true,"price":2061},{"date":"2016-07-04","available":true,"price":2061},{"date":"2016-07-05","available":true,"price":2061},{"date":"2016-07-06","available":true,"price":2061},{"date":"2016-07-07","available":true,"price":2061},{"date":"2016-07-08","available":true,"price":2061},{"date":"2016-07-09","available":true,"price":2061},{"date":"2016-07-10","available":true,"price":2061},{"date":"2016-07-11","available":true,"price":2061},{"date":"2016-07-12","available":true,"price":2061},{"date":"2016-07-13","available":true,"price":2061},{"date":"2016-07-14","available":true,"price":2061},{"date":"2016-07-15","available":true,"price":2061},{"date":"2016-07-16","available":true,"price":2061},{"date":"2016-07-17","available":true,"price":2061},{"date":"2016-07-18","available":true,"price":2061},{"date":"2016-07-19","available":true,"price":2061},{"date":"2016-07-20","available":true,"price":2061},{"date":"2016-07-21","available":true,"price":2061},{"date":"2016-07-22","available":true,"price":2061},{"date":"2016-07-23","available":true,"price":2061},{"date":"2016-07-24","available":true,"price":2061},{"date":"2016-07-25","available":true,"price":2061},{"date":"2016-07-26","available":true,"price":2061},{"date":"2016-07-27","available":true,"price":2061},{"date":"2016-07-28","available":true,"price":2061},{"date":"2016-07-29","available":true,"price":2061},{"date":"2016-07-30","available":true,"price":2061},{"date":"2016-07-31","available":true,"price":2061},{"date":"2016-08-01","available":false,"price":2093},{"date":"2016-08-02","available":false,"price":2093},{"date":"2016-08-03","available":false,"price":2093},{"date":"2016-08-04","available":false,"price":2093},{"date":"2016-08-05","available":false,"price":2093},{"date":"2016-08-06","available":false,"price":2093},{"date":"2016-08-07","available":false,"price":2093},{"date":"2016-08-08","available":false,"price":2093},{"date":"2016-08-09","available":false,"price":2093},{"date":"2016-08-10","available":false,"price":2093},{"date":"2016-08-11","available":false,"price":2093},{"date":"2016-08-12","available":false,"price":2093},{"date":"2016-08-13","available":false,"price":2093},{"date":"2016-08-14","available":false,"price":2093},{"date":"2016-08-15","available":false,"price":2093},{"date":"2016-08-16","available":false,"price":2093},{"date":"2016-08-17","available":false,"price":2093},{"date":"2016-08-18","available":false,"price":2093},{"date":"2016-08-19","available":false,"price":2093},{"date":"2016-08-20","available":false,"price":2094},{"date":"2016-08-21","available":true,"price":2061},{"date":"2016-08-22","available":true,"price":2061},{"date":"2016-08-23","available":true,"price":2061},{"date":"2016-08-24","available":true,"price":2061},{"date":"2016-08-25","available":true,"price":2061},{"date":"2016-08-26","available":true,"price":2061},{"date":"2016-08-27","available":true,"price":2061},{"date":"2016-08-28","available":true,"price":2061},{"date":"2016-08-29","available":true,"price":2061},{"date":"2016-08-30","available":true,"price":2061},{"date":"2016-08-31","available":true,"price":2061},{"date":"2016-09-01","available":true,"price":1096},{"date":"2016-09-02","available":true,"price":1096},{"date":"2016-09-03","available":true,"price":1096},{"date":"2016-09-04","available":true,"price":1096},{"date":"2016-09-05","available":true,"price":1096},{"date":"2016-09-06","available":true,"price":1096},{"date":"2016-09-07","available":true,"price":1096},{"date":"2016-09-08","available":true,"price":1096},{"date":"2016-09-09","available":true,"price":1096},{"date":"2016-09-10","available":true,"price":1096},{"date":"2016-09-11","available":true,"price":1096},{"date":"2016-09-12","available":true,"price":1096},{"date":"2016-09-13","available":true,"price":1096},{"date":"2016-09-14","available":true,"price":1096},{"date":"2016-09-15","available":true,"price":1096},{"date":"2016-09-16","available":true,"price":1096},{"date":"2016-09-17","available":true,"price":1096},{"date":"2016-09-18","available":true,"price":1096},{"date":"2016-09-19","available":true,"price":1096},{"date":"2016-09-20","available":true,"price":1096},{"date":"2016-09-21","available":true,"price":1096},{"date":"2016-09-22","available":true,"price":1096},{"date":"2016-09-23","available":true,"price":1096},{"date":"2016-09-24","available":true,"price":1096},{"date":"2016-09-25","available":true,"price":1096},{"date":"2016-09-26","available":true,"price":1096},{"date":"2016-09-27","available":true,"price":1096},{"date":"2016-09-28","available":true,"price":1096},{"date":"2016-09-29","available":true,"price":1096},{"date":"2016-09-30","available":true,"price":1096},{"date":"2016-10-01","available":true,"price":1096},{"date":"2016-10-02","available":true,"price":1096},{"date":"2016-10-03","available":true,"price":1096},{"date":"2016-10-04","available":true,"price":1096},{"date":"2016-10-05","available":true,"price":1096},{"date":"2016-10-06","available":true,"price":1096},{"date":"2016-10-07","available":true,"price":1096},{"date":"2016-10-08","available":true,"price":1096},{"date":"2016-10-09","available":true,"price":1096},{"date":"2016-10-10","available":true,"price":1096},{"date":"2016-10-11","available":true,"price":1096},{"date":"2016-10-12","available":true,"price":1096},{"date":"2016-10-13","available":true,"price":1096},{"date":"2016-10-14","available":true,"price":1096},{"date":"2016-10-15","available":true,"price":1096},{"date":"2016-10-16","available":true,"price":1096},{"date":"2016-10-17","available":true,"price":1096},{"date":"2016-10-18","available":true,"price":1096},{"date":"2016-10-19","available":true,"price":1096},{"date":"2016-10-20","available":true,"price":1096},{"date":"2016-10-21","available":true,"price":1096},{"date":"2016-10-22","available":true,"price":1096},{"date":"2016-10-23","available":true,"price":1096},{"date":"2016-10-24","available":true,"price":1096},{"date":"2016-10-25","available":true,"price":1096},{"date":"2016-10-26","available":true,"price":1096},{"date":"2016-10-27","available":true,"price":1096},{"date":"2016-10-28","available":true,"price":1096},{"date":"2016-10-29","available":true,"price":1096},{"date":"2016-10-30","available":true,"price":1096},{"date":"2016-10-31","available":true,"price":1096},{"date":"2016-11-01","available":true,"price":1096},{"date":"2016-11-02","available":true,"price":1096},{"date":"2016-11-03","available":true,"price":1096},{"date":"2016-11-04","available":true,"price":1096},{"date":"2016-11-05","available":true,"price":1096},{"date":"2016-11-06","available":true,"price":1096},{"date":"2016-11-07","available":true,"price":1096},{"date":"2016-11-08","available":true,"price":1096},{"date":"2016-11-09","available":true,"price":1096},{"date":"2016-11-10","available":true,"price":1096},{"date":"2016-11-11","available":true,"price":1096},{"date":"2016-11-12","available":true,"price":1096},{"date":"2016-11-13","available":true,"price":1096},{"date":"2016-11-14","available":true,"price":1096},{"date":"2016-11-15","available":true,"price":1096},{"date":"2016-11-16","available":true,"price":1096},{"date":"2016-11-17","available":true,"price":1096},{"date":"2016-11-18","available":true,"price":1096},{"date":"2016-11-19","available":true,"price":1096},{"date":"2016-11-20","available":true,"price":1096},{"date":"2016-11-21","available":true,"price":1096},{"date":"2016-11-22","available":true,"price":1096},{"date":"2016-11-23","available":true,"price":1096},{"date":"2016-11-24","available":true,"price":1096},{"date":"2016-11-25","available":true,"price":1096},{"date":"2016-11-26","available":true,"price":1096},{"date":"2016-11-27","available":true,"price":1096},{"date":"2016-11-28","available":true,"price":1096},{"date":"2016-11-29","available":true,"price":1096},{"date":"2016-11-30","available":true,"price":1096},{"date":"2016-12-01","available":true,"price":1096},{"date":"2016-12-02","available":true,"price":1096},{"date":"2016-12-03","available":true,"price":1096},{"date":"2016-12-04","available":true,"price":1096},{"date":"2016-12-05","available":true,"price":1096},{"date":"2016-12-06","available":true,"price":1096},{"date":"2016-12-07","available":true,"price":1096},{"date":"2016-12-08","available":true,"price":1096},{"date":"2016-12-09","available":true,"price":1096},{"date":"2016-12-10","available":true,"price":1096},{"date":"2016-12-11","available":true,"price":1096},{"date":"2016-12-12","available":true,"price":1096},{"date":"2016-12-13","available":true,"price":1096},{"date":"2016-12-14","available":true,"price":1096},{"date":"2016-12-15","available":true,"price":2426},{"date":"2016-12-16","available":true,"price":2426},{"date":"2016-12-17","available":true,"price":2426},{"date":"2016-12-18","available":true,"price":2426},{"date":"2016-12-19","available":true,"price":2426},{"date":"2016-12-20","available":true,"price":2426},{"date":"2016-12-21","available":true,"price":2426},{"date":"2016-12-22","available":true,"price":2426},{"date":"2016-12-23","available":true,"price":2426},{"date":"2016-12-24","available":true,"price":2426},{"date":"2016-12-25","available":true,"price":2426},{"date":"2016-12-26","available":false,"price":1126},{"date":"2016-12-27","available":false,"price":1126},{"date":"2016-12-28","available":false,"price":1126},{"date":"2016-12-29","available":false,"price":1126},{"date":"2016-12-30","available":false,"price":1126},{"date":"2016-12-31","available":false,"price":1126},{"date":"2017-01-01","available":false,"price":1126},{"date":"2017-01-02","available":false,"price":1126},{"date":"2017-01-03","available":false,"price":1126},{"date":"2017-01-04","available":false,"price":1126},{"date":"2017-01-05","available":true,"price":2426},{"date":"2017-01-06","available":true,"price":2426},{"date":"2017-01-07","available":true,"price":2426},{"date":"2017-01-08","available":true,"price":2426},{"date":"2017-01-09","available":true,"price":2426},{"date":"2017-01-10","available":true,"price":2426},{"date":"2017-01-11","available":true,"price":2426},{"date":"2017-01-12","available":true,"price":2426},{"date":"2017-01-13","available":true,"price":2426},{"date":"2017-01-14","available":true,"price":2426},{"date":"2017-01-15","available":true,"price":2426},{"date":"2017-01-16","available":true,"price":2426},{"date":"2017-01-17","available":true,"price":2426},{"date":"2017-01-18","available":true,"price":2426},{"date":"2017-01-19","available":true,"price":2426},{"date":"2017-01-20","available":true,"price":2426},{"date":"2017-01-21","available":true,"price":2426},{"date":"2017-01-22","available":true,"price":2426},{"date":"2017-01-23","available":true,"price":2426},{"date":"2017-01-24","available":true,"price":2426},{"date":"2017-01-25","available":true,"price":2426},{"date":"2017-01-26","available":true,"price":2426},{"date":"2017-01-27","available":true,"price":2426},{"date":"2017-01-28","available":true,"price":2426},{"date":"2017-01-29","available":true,"price":1615},{"date":"2017-01-30","available":true,"price":1615},{"date":"2017-01-31","available":true,"price":1615},{"date":"2017-02-01","available":true,"price":1615},{"date":"2017-02-02","available":true,"price":1615},{"date":"2017-02-03","available":true,"price":1615},{"date":"2017-02-04","available":true,"price":1615},{"date":"2017-02-05","available":true,"price":1615},{"date":"2017-02-06","available":true,"price":1615},{"date":"2017-02-07","available":true,"price":1615},{"date":"2017-02-08","available":true,"price":1615},{"date":"2017-02-09","available":true,"price":1615},{"date":"2017-02-10","available":true,"price":1615},{"date":"2017-02-11","available":true,"price":1615},{"date":"2017-02-12","available":true,"price":1615},{"date":"2017-02-13","available":true,"price":1615},{"date":"2017-02-14","available":true,"price":1615},{"date":"2017-02-15","available":true,"price":1615},{"date":"2017-02-16","available":true,"price":1615},{"date":"2017-02-17","available":true,"price":1615},{"date":"2017-02-18","available":true,"price":1615},{"date":"2017-02-19","available":true,"price":1615},{"date":"2017-02-20","available":true,"price":1615},{"date":"2017-02-21","available":true,"price":1615},{"date":"2017-02-22","available":true,"price":1615},{"date":"2017-02-23","available":true,"price":1615},{"date":"2017-02-24","available":true,"price":1615},{"date":"2017-02-25","available":true,"price":1615},{"date":"2017-02-26","available":true,"price":1615},{"date":"2017-02-27","available":true,"price":1615},{"date":"2017-02-28","available":true,"price":1615},{"date":"2017-03-01","available":true,"price":1615},{"date":"2017-03-02","available":true,"price":1615},{"date":"2017-03-03","available":true,"price":1615},{"date":"2017-03-04","available":true,"price":1615},{"date":"2017-03-05","available":true,"price":1615},{"date":"2017-03-06","available":true,"price":1615},{"date":"2017-03-07","available":true,"price":1615},{"date":"2017-03-08","available":true,"price":1615},{"date":"2017-03-09","available":true,"price":1615},{"date":"2017-03-10","available":true,"price":1615}]} 2 | -------------------------------------------------------------------------------- /screenshots/00.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wanggang316/ZBJCalendar/bbe2ebcbdfc35eb01271242c1546d9e9aa00ad8c/screenshots/00.png --------------------------------------------------------------------------------