ActionSheetDatePicker 2.m 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272
  1. //
  2. //Copyright (c) 2011, Tim Cinel
  3. //All rights reserved.
  4. //
  5. //Redistribution and use in source and binary forms, with or without
  6. //modification, are permitted provided that the following conditions are met:
  7. //* Redistributions of source code must retain the above copyright
  8. //notice, this list of conditions and the following disclaimer.
  9. //* Redistributions in binary form must reproduce the above copyright
  10. //notice, this list of conditions and the following disclaimer in the
  11. //documentation and/or other materials provided with the distribution.
  12. //* Neither the name of the <organization> nor the
  13. //names of its contributors may be used to endorse or promote products
  14. //derived from this software without specific prior written permission.
  15. //
  16. //THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
  17. //ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  18. //WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  19. //DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
  20. //DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  21. //(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  22. //LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  23. //ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  24. //(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  25. //SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  26. //
  27. #import "ActionSheetDatePicker.h"
  28. #import <objc/message.h>
  29. @interface ActionSheetDatePicker()
  30. @property (nonatomic, assign) UIDatePickerMode datePickerMode;
  31. @property (nonatomic, strong) NSDate *selectedDate;
  32. @end
  33. @implementation ActionSheetDatePicker
  34. + (instancetype)showPickerWithTitle:(NSString *)title
  35. datePickerMode:(UIDatePickerMode)datePickerMode selectedDate:(NSDate *)selectedDate
  36. target:(id)target action:(SEL)action origin:(id)origin {
  37. ActionSheetDatePicker *picker = [[ActionSheetDatePicker alloc] initWithTitle:title datePickerMode:datePickerMode selectedDate:selectedDate target:target action:action origin:origin];
  38. [picker showActionSheetPicker];
  39. return picker;
  40. }
  41. + (instancetype)showPickerWithTitle:(NSString *)title
  42. datePickerMode:(UIDatePickerMode)datePickerMode selectedDate:(NSDate *)selectedDate
  43. target:(id)target action:(SEL)action origin:(id)origin cancelAction:(SEL)cancelAction {
  44. ActionSheetDatePicker *picker = [[ActionSheetDatePicker alloc] initWithTitle:title datePickerMode:datePickerMode selectedDate:selectedDate target:target action:action origin:origin cancelAction:cancelAction];
  45. [picker showActionSheetPicker];
  46. return picker;
  47. }
  48. + (instancetype)showPickerWithTitle:(NSString *)title
  49. datePickerMode:(UIDatePickerMode)datePickerMode selectedDate:(NSDate *)selectedDate
  50. minimumDate:(NSDate *)minimumDate maximumDate:(NSDate *)maximumDate
  51. target:(id)target action:(SEL)action origin:(id)origin {
  52. ActionSheetDatePicker *picker = [[ActionSheetDatePicker alloc] initWithTitle:title datePickerMode:datePickerMode selectedDate:selectedDate target:target action:action origin:origin];
  53. [picker setMinimumDate:minimumDate];
  54. [picker setMaximumDate:maximumDate];
  55. [picker showActionSheetPicker];
  56. return picker;
  57. }
  58. + (instancetype)showPickerWithTitle:(NSString *)title
  59. datePickerMode:(UIDatePickerMode)datePickerMode
  60. selectedDate:(NSDate *)selectedDate
  61. doneBlock:(ActionDateDoneBlock)doneBlock
  62. cancelBlock:(ActionDateCancelBlock)cancelBlock
  63. origin:(UIView*)view
  64. {
  65. ActionSheetDatePicker* picker = [[ActionSheetDatePicker alloc] initWithTitle:title
  66. datePickerMode:datePickerMode
  67. selectedDate:selectedDate
  68. doneBlock:doneBlock
  69. cancelBlock:cancelBlock
  70. origin:view];
  71. [picker showActionSheetPicker];
  72. return picker;
  73. }
  74. + (instancetype)showPickerWithTitle:(NSString *)title
  75. datePickerMode:(UIDatePickerMode)datePickerMode
  76. selectedDate:(NSDate *)selectedDate
  77. minimumDate:(NSDate *)minimumDate
  78. maximumDate:(NSDate *)maximumDate
  79. doneBlock:(ActionDateDoneBlock)doneBlock
  80. cancelBlock:(ActionDateCancelBlock)cancelBlock
  81. origin:(UIView*)view
  82. {
  83. ActionSheetDatePicker* picker = [[ActionSheetDatePicker alloc] initWithTitle:title
  84. datePickerMode:datePickerMode
  85. selectedDate:selectedDate
  86. doneBlock:doneBlock
  87. cancelBlock:cancelBlock
  88. origin:view];
  89. [picker setMinimumDate:minimumDate];
  90. [picker setMaximumDate:maximumDate];
  91. [picker showActionSheetPicker];
  92. return picker;
  93. }
  94. - (id)initWithTitle:(NSString *)title datePickerMode:(UIDatePickerMode)datePickerMode selectedDate:(NSDate *)selectedDate target:(id)target action:(SEL)action origin:(id)origin
  95. {
  96. self = [self initWithTitle:title datePickerMode:datePickerMode selectedDate:selectedDate target:target action:action origin:origin cancelAction:nil];
  97. return self;
  98. }
  99. - (instancetype)initWithTitle:(NSString *)title datePickerMode:(UIDatePickerMode)datePickerMode selectedDate:(NSDate *)selectedDate minimumDate:(NSDate *)minimumDate maximumDate:(NSDate *)maximumDate target:(id)target action:(SEL)action origin:(id)origin
  100. {
  101. self = [self initWithTitle:title datePickerMode:datePickerMode selectedDate:selectedDate target:target action:action origin:origin cancelAction:nil];
  102. self.minimumDate = minimumDate;
  103. self.maximumDate = maximumDate;
  104. return self;
  105. }
  106. - (instancetype)initWithTitle:(NSString *)title datePickerMode:(UIDatePickerMode)datePickerMode selectedDate:(NSDate *)selectedDate target:(id)target action:(SEL)action origin:(id)origin cancelAction:(SEL)cancelAction
  107. {
  108. self = [super initWithTarget:target successAction:action cancelAction:cancelAction origin:origin];
  109. if (self) {
  110. self.title = title;
  111. self.datePickerMode = datePickerMode;
  112. self.selectedDate = selectedDate;
  113. }
  114. return self;
  115. }
  116. - (instancetype)initWithTitle:(NSString *)title datePickerMode:(UIDatePickerMode)datePickerMode selectedDate:(NSDate *)selectedDate minimumDate:(NSDate *)minimumDate maximumDate:(NSDate *)maximumDate target:(id)target action:(SEL)action cancelAction:(SEL)cancelAction origin:(id)origin
  117. {
  118. self = [super initWithTarget:target successAction:action cancelAction:cancelAction origin:origin];
  119. if (self) {
  120. self.title = title;
  121. self.datePickerMode = datePickerMode;
  122. self.selectedDate = selectedDate;
  123. self.minimumDate = minimumDate;
  124. self.maximumDate = maximumDate;
  125. }
  126. return self;
  127. }
  128. - (instancetype)initWithTitle:(NSString *)title
  129. datePickerMode:(UIDatePickerMode)datePickerMode
  130. selectedDate:(NSDate *)selectedDate
  131. doneBlock:(ActionDateDoneBlock)doneBlock
  132. cancelBlock:(ActionDateCancelBlock)cancelBlock
  133. origin:(UIView*)origin
  134. {
  135. self = [self initWithTitle:title datePickerMode:datePickerMode selectedDate:selectedDate target:nil action:nil origin:origin];
  136. if (self) {
  137. self.onActionSheetDone = doneBlock;
  138. self.onActionSheetCancel = cancelBlock;
  139. }
  140. return self;
  141. }
  142. - (UIView *)configuredPickerView {
  143. CGRect datePickerFrame = CGRectMake(0, 40, self.viewSize.width, 216);
  144. UIDatePicker *datePicker = [[UIDatePicker alloc] initWithFrame:datePickerFrame];
  145. datePicker.datePickerMode = self.datePickerMode;
  146. datePicker.maximumDate = self.maximumDate;
  147. datePicker.minimumDate = self.minimumDate;
  148. datePicker.minuteInterval = self.minuteInterval;
  149. datePicker.calendar = self.calendar;
  150. datePicker.timeZone = self.timeZone;
  151. datePicker.locale = self.locale;
  152. // if datepicker is set with a date in countDownMode then
  153. // 1h is added to the initial countdown
  154. if (self.datePickerMode == UIDatePickerModeCountDownTimer) {
  155. datePicker.countDownDuration = self.countDownDuration;
  156. // Due to a bug in UIDatePicker, countDownDuration needs to be set asynchronously
  157. // more info: http://stackoverflow.com/a/20204317/1161723
  158. dispatch_async(dispatch_get_main_queue(), ^{
  159. datePicker.countDownDuration = self.countDownDuration;
  160. });
  161. } else {
  162. [datePicker setDate:self.selectedDate animated:NO];
  163. }
  164. [datePicker addTarget:self action:@selector(eventForDatePicker:) forControlEvents:UIControlEventValueChanged];
  165. //need to keep a reference to the picker so we can clear the DataSource / Delegate when dismissing (not used in this picker, but just in case somebody uses this as a template for another picker)
  166. self.pickerView = datePicker;
  167. return datePicker;
  168. }
  169. - (void)notifyTarget:(id)target didSucceedWithAction:(SEL)action origin:(id)origin
  170. {
  171. if (self.onActionSheetDone)
  172. {
  173. if (self.datePickerMode == UIDatePickerModeCountDownTimer)
  174. self.onActionSheetDone(self, @(((UIDatePicker *)self.pickerView).countDownDuration), origin);
  175. else
  176. self.onActionSheetDone(self, self.selectedDate, origin);
  177. return;
  178. }
  179. else if ([target respondsToSelector:action])
  180. #pragma clang diagnostic push
  181. #pragma clang diagnostic ignored "-Warc-performSelector-leaks"
  182. if (self.datePickerMode == UIDatePickerModeCountDownTimer) {
  183. [target performSelector:action withObject:@(((UIDatePicker *)self.pickerView).countDownDuration) withObject:origin];
  184. } else {
  185. [target performSelector:action withObject:self.selectedDate withObject:origin];
  186. }
  187. #pragma clang diagnostic pop
  188. else
  189. NSAssert(NO, @"Invalid target/action ( %s / %s ) combination used for ActionSheetPicker", object_getClassName(target), sel_getName(action));
  190. }
  191. - (void)notifyTarget:(id)target didCancelWithAction:(SEL)cancelAction origin:(id)origin
  192. {
  193. if (self.onActionSheetCancel)
  194. {
  195. self.onActionSheetCancel(self);
  196. return;
  197. }
  198. else
  199. if ( target && cancelAction && [target respondsToSelector:cancelAction] )
  200. {
  201. #pragma clang diagnostic push
  202. #pragma clang diagnostic ignored "-Warc-performSelector-leaks"
  203. [target performSelector:cancelAction withObject:origin];
  204. #pragma clang diagnostic pop
  205. }
  206. }
  207. - (void)eventForDatePicker:(id)sender
  208. {
  209. if (!sender || ![sender isKindOfClass:[UIDatePicker class]])
  210. return;
  211. UIDatePicker *datePicker = (UIDatePicker *)sender;
  212. self.selectedDate = datePicker.date;
  213. self.countDownDuration = datePicker.countDownDuration;
  214. }
  215. - (void)customButtonPressed:(id)sender {
  216. UIBarButtonItem *button = (UIBarButtonItem*)sender;
  217. NSInteger index = button.tag;
  218. NSAssert((index >= 0 && index < self.customButtons.count), @"Bad custom button tag: %zd, custom button count: %zd", index, self.customButtons.count);
  219. NSDictionary *buttonDetails = (self.customButtons)[(NSUInteger) index];
  220. NSAssert(buttonDetails != NULL, @"Custom button dictionary is invalid");
  221. ActionType actionType = (ActionType) [buttonDetails[kActionType] integerValue];
  222. switch (actionType) {
  223. case ActionTypeValue: {
  224. NSAssert([self.pickerView respondsToSelector:@selector(setDate:animated:)], @"Bad pickerView for ActionSheetDatePicker, doesn't respond to setDate:animated:");
  225. NSDate *itemValue = buttonDetails[kButtonValue];
  226. UIDatePicker *picker = (UIDatePicker *)self.pickerView;
  227. if (self.datePickerMode != UIDatePickerModeCountDownTimer)
  228. {
  229. [picker setDate:itemValue animated:YES];
  230. [self eventForDatePicker:picker];
  231. }
  232. break;
  233. }
  234. case ActionTypeBlock:
  235. case ActionTypeSelector:
  236. [super customButtonPressed:sender];
  237. break;
  238. default:
  239. NSAssert(false, @"Unknown action type");
  240. break;
  241. }
  242. }
  243. @end