XLFormViewController.m 45 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046
  1. //
  2. // XLFormViewController.m
  3. // XLForm ( https://github.com/xmartlabs/XLForm )
  4. //
  5. // Copyright (c) 2015 Xmartlabs ( http://xmartlabs.com )
  6. //
  7. //
  8. // Permission is hereby granted, free of charge, to any person obtaining a copy
  9. // of this software and associated documentation files (the "Software"), to deal
  10. // in the Software without restriction, including without limitation the rights
  11. // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  12. // copies of the Software, and to permit persons to whom the Software is
  13. // furnished to do so, subject to the following conditions:
  14. //
  15. // The above copyright notice and this permission notice shall be included in
  16. // all copies or substantial portions of the Software.
  17. //
  18. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  19. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  20. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  21. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  22. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  23. // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  24. // THE SOFTWARE.
  25. #import "UIView+XLFormAdditions.h"
  26. #import "NSObject+XLFormAdditions.h"
  27. #import "XLFormViewController.h"
  28. #import "UIView+XLFormAdditions.h"
  29. #import "XLForm.h"
  30. #import "NSString+XLFormAdditions.h"
  31. @interface XLFormRowDescriptor(_XLFormViewController)
  32. @property (readonly) NSArray * observers;
  33. -(BOOL)evaluateIsDisabled;
  34. -(BOOL)evaluateIsHidden;
  35. @end
  36. @interface XLFormSectionDescriptor(_XLFormViewController)
  37. -(BOOL)evaluateIsHidden;
  38. @end
  39. @interface XLFormDescriptor (_XLFormViewController)
  40. @property NSMutableDictionary* rowObservers;
  41. @end
  42. @interface XLFormViewController()
  43. {
  44. NSNumber *_oldBottomTableContentInset;
  45. CGRect _keyboardFrame;
  46. }
  47. @property UITableViewStyle tableViewStyle;
  48. @property (nonatomic) XLFormRowNavigationAccessoryView * navigationAccessoryView;
  49. @end
  50. @implementation XLFormViewController
  51. @synthesize form = _form;
  52. #pragma mark - Initialization
  53. -(instancetype)initWithForm:(XLFormDescriptor *)form
  54. {
  55. return [self initWithForm:form style:UITableViewStyleGrouped];
  56. }
  57. -(instancetype)initWithForm:(XLFormDescriptor *)form style:(UITableViewStyle)style
  58. {
  59. self = [self initWithNibName:nil bundle:nil];
  60. if (self){
  61. _tableViewStyle = style;
  62. _form = form;
  63. }
  64. return self;
  65. }
  66. -(instancetype)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
  67. {
  68. self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
  69. if (self){
  70. _form = nil;
  71. _tableViewStyle = UITableViewStyleGrouped;
  72. }
  73. return self;
  74. }
  75. -(instancetype)initWithCoder:(NSCoder *)aDecoder
  76. {
  77. self = [super initWithCoder:aDecoder];
  78. if (self) {
  79. _form = nil;
  80. _tableViewStyle = UITableViewStyleGrouped;
  81. }
  82. return self;
  83. }
  84. - (void)dealloc
  85. {
  86. self.tableView.delegate = nil;
  87. self.tableView.dataSource = nil;
  88. }
  89. - (void)viewDidLoad
  90. {
  91. [super viewDidLoad];
  92. if (!self.tableView){
  93. self.tableView = [[UITableView alloc] initWithFrame:self.view.bounds
  94. style:self.tableViewStyle];
  95. self.tableView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
  96. if([self.tableView respondsToSelector:@selector(cellLayoutMarginsFollowReadableWidth)]){
  97. self.tableView.cellLayoutMarginsFollowReadableWidth = NO;
  98. }
  99. }
  100. if (!self.tableView.superview){
  101. [self.view addSubview:self.tableView];
  102. }
  103. if (!self.tableView.delegate){
  104. self.tableView.delegate = self;
  105. }
  106. if (!self.tableView.dataSource){
  107. self.tableView.dataSource = self;
  108. }
  109. if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(@"8.0")){
  110. self.tableView.rowHeight = UITableViewAutomaticDimension;
  111. self.tableView.estimatedRowHeight = 44.0;
  112. }
  113. if (self.form.title){
  114. self.title = self.form.title;
  115. }
  116. [self.tableView setEditing:YES animated:NO];
  117. self.tableView.allowsSelectionDuringEditing = YES;
  118. self.form.delegate = self;
  119. _oldBottomTableContentInset = nil;
  120. }
  121. -(void)viewWillAppear:(BOOL)animated
  122. {
  123. [super viewWillAppear:animated];
  124. NSIndexPath *selected = [self.tableView indexPathForSelectedRow];
  125. if (selected){
  126. // Trigger a cell refresh
  127. XLFormRowDescriptor * rowDescriptor = [self.form formRowAtIndex:selected];
  128. [self updateFormRow:rowDescriptor];
  129. [self.tableView selectRowAtIndexPath:selected animated:NO scrollPosition:UITableViewScrollPositionNone];
  130. [self.tableView deselectRowAtIndexPath:selected animated:YES];
  131. }
  132. [[NSNotificationCenter defaultCenter] addObserver:self
  133. selector:@selector(contentSizeCategoryChanged:)
  134. name:UIContentSizeCategoryDidChangeNotification
  135. object:nil];
  136. [[NSNotificationCenter defaultCenter] addObserver:self
  137. selector:@selector(keyboardWillShow:)
  138. name:UIKeyboardWillShowNotification
  139. object:nil];
  140. [[NSNotificationCenter defaultCenter] addObserver:self
  141. selector:@selector(keyboardWillHide:)
  142. name:UIKeyboardWillHideNotification
  143. object:nil];
  144. }
  145. -(void)viewDidDisappear:(BOOL)animated
  146. {
  147. [super viewDidDisappear:animated];
  148. [[NSNotificationCenter defaultCenter] removeObserver:self
  149. name:UIContentSizeCategoryDidChangeNotification
  150. object:nil];
  151. [[NSNotificationCenter defaultCenter] removeObserver:self
  152. name:UIKeyboardWillShowNotification
  153. object:nil];
  154. [[NSNotificationCenter defaultCenter] removeObserver:self
  155. name:UIKeyboardWillHideNotification
  156. object:nil];
  157. }
  158. - (void)viewDidAppear:(BOOL)animated
  159. {
  160. [super viewDidAppear:animated];
  161. if (self.form.assignFirstResponderOnShow) {
  162. self.form.assignFirstResponderOnShow = NO;
  163. [self.form setFirstResponder:self];
  164. }
  165. }
  166. - (void)didReceiveMemoryWarning
  167. {
  168. [super didReceiveMemoryWarning];
  169. }
  170. #pragma mark - CellClasses
  171. +(NSMutableDictionary *)cellClassesForRowDescriptorTypes
  172. {
  173. static NSMutableDictionary * _cellClassesForRowDescriptorTypes;
  174. static dispatch_once_t onceToken;
  175. dispatch_once(&onceToken, ^{
  176. _cellClassesForRowDescriptorTypes = [@{XLFormRowDescriptorTypeText:[XLFormTextFieldCell class],
  177. XLFormRowDescriptorTypeName: [XLFormTextFieldCell class],
  178. XLFormRowDescriptorTypePhone:[XLFormTextFieldCell class],
  179. XLFormRowDescriptorTypeURL:[XLFormTextFieldCell class],
  180. XLFormRowDescriptorTypeEmail: [XLFormTextFieldCell class],
  181. XLFormRowDescriptorTypeTwitter: [XLFormTextFieldCell class],
  182. XLFormRowDescriptorTypeAccount: [XLFormTextFieldCell class],
  183. XLFormRowDescriptorTypePassword: [XLFormTextFieldCell class],
  184. XLFormRowDescriptorTypeNumber: [XLFormTextFieldCell class],
  185. XLFormRowDescriptorTypeInteger: [XLFormTextFieldCell class],
  186. XLFormRowDescriptorTypeDecimal: [XLFormTextFieldCell class],
  187. XLFormRowDescriptorTypeZipCode: [XLFormTextFieldCell class],
  188. XLFormRowDescriptorTypeSelectorPush: [XLFormSelectorCell class],
  189. XLFormRowDescriptorTypeSelectorPopover: [XLFormSelectorCell class],
  190. XLFormRowDescriptorTypeSelectorActionSheet: [XLFormSelectorCell class],
  191. XLFormRowDescriptorTypeSelectorAlertView: [XLFormSelectorCell class],
  192. XLFormRowDescriptorTypeSelectorPickerView: [XLFormSelectorCell class],
  193. XLFormRowDescriptorTypeSelectorPickerViewInline: [XLFormInlineSelectorCell class],
  194. XLFormRowDescriptorTypeSelectorSegmentedControl: [XLFormSegmentedCell class],
  195. XLFormRowDescriptorTypeMultipleSelector: [XLFormSelectorCell class],
  196. XLFormRowDescriptorTypeMultipleSelectorPopover: [XLFormSelectorCell class],
  197. XLFormRowDescriptorTypeImage: [XLFormImageCell class],
  198. XLFormRowDescriptorTypeTextView: [XLFormTextViewCell class],
  199. XLFormRowDescriptorTypeButton: [XLFormButtonCell class],
  200. XLFormRowDescriptorTypeInfo: [XLFormSelectorCell class],
  201. XLFormRowDescriptorTypeBooleanSwitch : [XLFormSwitchCell class],
  202. XLFormRowDescriptorTypeBooleanCheck : [XLFormCheckCell class],
  203. XLFormRowDescriptorTypeDate: [XLFormDateCell class],
  204. XLFormRowDescriptorTypeTime: [XLFormDateCell class],
  205. XLFormRowDescriptorTypeDateTime : [XLFormDateCell class],
  206. XLFormRowDescriptorTypeCountDownTimer : [XLFormDateCell class],
  207. XLFormRowDescriptorTypeDateInline: [XLFormDateCell class],
  208. XLFormRowDescriptorTypeTimeInline: [XLFormDateCell class],
  209. XLFormRowDescriptorTypeDateTimeInline: [XLFormDateCell class],
  210. XLFormRowDescriptorTypeCountDownTimerInline : [XLFormDateCell class],
  211. XLFormRowDescriptorTypeDatePicker : [XLFormDatePickerCell class],
  212. XLFormRowDescriptorTypePicker : [XLFormPickerCell class],
  213. XLFormRowDescriptorTypeSlider : [XLFormSliderCell class],
  214. XLFormRowDescriptorTypeSelectorLeftRight : [XLFormLeftRightSelectorCell class],
  215. XLFormRowDescriptorTypeStepCounter: [XLFormStepCounterCell class]
  216. } mutableCopy];
  217. });
  218. return _cellClassesForRowDescriptorTypes;
  219. }
  220. #pragma mark - inlineRowDescriptorTypes
  221. +(NSMutableDictionary *)inlineRowDescriptorTypesForRowDescriptorTypes
  222. {
  223. static NSMutableDictionary * _inlineRowDescriptorTypesForRowDescriptorTypes;
  224. static dispatch_once_t onceToken;
  225. dispatch_once(&onceToken, ^{
  226. _inlineRowDescriptorTypesForRowDescriptorTypes = [
  227. @{XLFormRowDescriptorTypeSelectorPickerViewInline: XLFormRowDescriptorTypePicker,
  228. XLFormRowDescriptorTypeDateInline: XLFormRowDescriptorTypeDatePicker,
  229. XLFormRowDescriptorTypeDateTimeInline: XLFormRowDescriptorTypeDatePicker,
  230. XLFormRowDescriptorTypeTimeInline: XLFormRowDescriptorTypeDatePicker,
  231. XLFormRowDescriptorTypeCountDownTimerInline: XLFormRowDescriptorTypeDatePicker
  232. } mutableCopy];
  233. });
  234. return _inlineRowDescriptorTypesForRowDescriptorTypes;
  235. }
  236. #pragma mark - XLFormDescriptorDelegate
  237. -(void)formRowHasBeenAdded:(XLFormRowDescriptor *)formRow atIndexPath:(NSIndexPath *)indexPath
  238. {
  239. [self.tableView beginUpdates];
  240. [self.tableView insertRowsAtIndexPaths:@[indexPath] withRowAnimation:[self insertRowAnimationForRow:formRow]];
  241. [self.tableView endUpdates];
  242. }
  243. -(void)formRowHasBeenRemoved:(XLFormRowDescriptor *)formRow atIndexPath:(NSIndexPath *)indexPath
  244. {
  245. [self.tableView beginUpdates];
  246. [self.tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:[self deleteRowAnimationForRow:formRow]];
  247. [self.tableView endUpdates];
  248. }
  249. -(void)formSectionHasBeenRemoved:(XLFormSectionDescriptor *)formSection atIndex:(NSUInteger)index
  250. {
  251. [self.tableView beginUpdates];
  252. [self.tableView deleteSections:[NSIndexSet indexSetWithIndex:index] withRowAnimation:[self deleteRowAnimationForSection:formSection]];
  253. [self.tableView endUpdates];
  254. }
  255. -(void)formSectionHasBeenAdded:(XLFormSectionDescriptor *)formSection atIndex:(NSUInteger)index
  256. {
  257. [self.tableView beginUpdates];
  258. [self.tableView insertSections:[NSIndexSet indexSetWithIndex:index] withRowAnimation:[self insertRowAnimationForSection:formSection]];
  259. [self.tableView endUpdates];
  260. }
  261. -(void)formRowDescriptorValueHasChanged:(XLFormRowDescriptor *)formRow oldValue:(id)oldValue newValue:(id)newValue
  262. {
  263. [self updateAfterDependentRowChanged:formRow];
  264. }
  265. -(void)formRowDescriptorPredicateHasChanged:(XLFormRowDescriptor *)formRow oldValue:(id)oldValue newValue:(id)newValue predicateType:(XLPredicateType)predicateType
  266. {
  267. if (oldValue != newValue) {
  268. [self updateAfterDependentRowChanged:formRow];
  269. }
  270. }
  271. -(void)updateAfterDependentRowChanged:(XLFormRowDescriptor *)formRow
  272. {
  273. NSMutableArray* revaluateHidden = self.form.rowObservers[[formRow.tag formKeyForPredicateType:XLPredicateTypeHidden]];
  274. NSMutableArray* revaluateDisabled = self.form.rowObservers[[formRow.tag formKeyForPredicateType:XLPredicateTypeDisabled]];
  275. for (id object in revaluateDisabled) {
  276. if ([object isKindOfClass:[NSString class]]) {
  277. XLFormRowDescriptor* row = [self.form formRowWithTag:object];
  278. if (row){
  279. [row evaluateIsDisabled];
  280. [self updateFormRow:row];
  281. }
  282. }
  283. }
  284. for (id object in revaluateHidden) {
  285. if ([object isKindOfClass:[NSString class]]) {
  286. XLFormRowDescriptor* row = [self.form formRowWithTag:object];
  287. if (row){
  288. [row evaluateIsHidden];
  289. }
  290. }
  291. else if ([object isKindOfClass:[XLFormSectionDescriptor class]]) {
  292. XLFormSectionDescriptor* section = (XLFormSectionDescriptor*) object;
  293. [section evaluateIsHidden];
  294. }
  295. }
  296. }
  297. #pragma mark - XLFormViewControllerDelegate
  298. -(NSDictionary *)formValues
  299. {
  300. return [self.form formValues];
  301. }
  302. -(NSDictionary *)httpParameters
  303. {
  304. return [self.form httpParameters:self];
  305. }
  306. -(void)didSelectFormRow:(XLFormRowDescriptor *)formRow
  307. {
  308. if ([[formRow cellForFormController:self] respondsToSelector:@selector(formDescriptorCellDidSelectedWithFormController:)]){
  309. [[formRow cellForFormController:self] formDescriptorCellDidSelectedWithFormController:self];
  310. }
  311. }
  312. -(UITableViewRowAnimation)insertRowAnimationForRow:(XLFormRowDescriptor *)formRow
  313. {
  314. if (formRow.sectionDescriptor.sectionOptions & XLFormSectionOptionCanInsert){
  315. if (formRow.sectionDescriptor.sectionInsertMode == XLFormSectionInsertModeButton){
  316. return UITableViewRowAnimationAutomatic;
  317. }
  318. else if (formRow.sectionDescriptor.sectionInsertMode == XLFormSectionInsertModeLastRow){
  319. return YES;
  320. }
  321. }
  322. return UITableViewRowAnimationFade;
  323. }
  324. -(UITableViewRowAnimation)deleteRowAnimationForRow:(XLFormRowDescriptor *)formRow
  325. {
  326. return UITableViewRowAnimationFade;
  327. }
  328. -(UITableViewRowAnimation)insertRowAnimationForSection:(XLFormSectionDescriptor *)formSection
  329. {
  330. return UITableViewRowAnimationAutomatic;
  331. }
  332. -(UITableViewRowAnimation)deleteRowAnimationForSection:(XLFormSectionDescriptor *)formSection
  333. {
  334. return UITableViewRowAnimationAutomatic;
  335. }
  336. -(UIView *)inputAccessoryViewForRowDescriptor:(XLFormRowDescriptor *)rowDescriptor
  337. {
  338. if ((self.form.rowNavigationOptions & XLFormRowNavigationOptionEnabled) != XLFormRowNavigationOptionEnabled){
  339. return nil;
  340. }
  341. if ([[[[self class] inlineRowDescriptorTypesForRowDescriptorTypes] allKeys] containsObject:rowDescriptor.rowType]) {
  342. return nil;
  343. }
  344. UITableViewCell<XLFormDescriptorCell> * cell = (UITableViewCell<XLFormDescriptorCell> *)[rowDescriptor cellForFormController:self];
  345. if (![cell formDescriptorCellCanBecomeFirstResponder]){
  346. return nil;
  347. }
  348. XLFormRowDescriptor * previousRow = [self nextRowDescriptorForRow:rowDescriptor
  349. withDirection:XLFormRowNavigationDirectionPrevious];
  350. XLFormRowDescriptor * nextRow = [self nextRowDescriptorForRow:rowDescriptor
  351. withDirection:XLFormRowNavigationDirectionNext];
  352. [self.navigationAccessoryView.previousButton setEnabled:(previousRow != nil)];
  353. [self.navigationAccessoryView.nextButton setEnabled:(nextRow != nil)];
  354. return self.navigationAccessoryView;
  355. }
  356. -(void)beginEditing:(XLFormRowDescriptor *)rowDescriptor
  357. {
  358. [[rowDescriptor cellForFormController:self] highlight];
  359. }
  360. -(void)endEditing:(XLFormRowDescriptor *)rowDescriptor
  361. {
  362. [[rowDescriptor cellForFormController:self] unhighlight];
  363. }
  364. -(XLFormRowDescriptor *)formRowFormMultivaluedFormSection:(XLFormSectionDescriptor *)formSection
  365. {
  366. if (formSection.multivaluedRowTemplate){
  367. return [formSection.multivaluedRowTemplate copy];
  368. }
  369. XLFormRowDescriptor * formRowDescriptor = [[formSection.formRows objectAtIndex:0] copy];
  370. formRowDescriptor.tag = nil;
  371. return formRowDescriptor;
  372. }
  373. -(void)multivaluedInsertButtonTapped:(XLFormRowDescriptor *)formRow
  374. {
  375. [self deselectFormRow:formRow];
  376. XLFormSectionDescriptor * multivaluedFormSection = formRow.sectionDescriptor;
  377. XLFormRowDescriptor * formRowDescriptor = [self formRowFormMultivaluedFormSection:multivaluedFormSection];
  378. [multivaluedFormSection addFormRow:formRowDescriptor];
  379. dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.02 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
  380. self.tableView.editing = !self.tableView.editing;
  381. self.tableView.editing = !self.tableView.editing;
  382. });
  383. UITableViewCell<XLFormDescriptorCell> * cell = (UITableViewCell<XLFormDescriptorCell> *)[formRowDescriptor cellForFormController:self];
  384. if ([cell formDescriptorCellCanBecomeFirstResponder]){
  385. [cell formDescriptorCellBecomeFirstResponder];
  386. }
  387. }
  388. -(void)ensureRowIsVisible:(XLFormRowDescriptor *)inlineRowDescriptor
  389. {
  390. XLFormBaseCell * inlineCell = [inlineRowDescriptor cellForFormController:self];
  391. NSIndexPath * indexOfOutOfWindowCell = [self.form indexPathOfFormRow:inlineRowDescriptor];
  392. if(!inlineCell.window || (self.tableView.contentOffset.y + self.tableView.frame.size.height <= inlineCell.frame.origin.y + inlineCell.frame.size.height)){
  393. [self.tableView scrollToRowAtIndexPath:indexOfOutOfWindowCell atScrollPosition:UITableViewScrollPositionBottom animated:YES];
  394. }
  395. }
  396. #pragma mark - Methods
  397. -(NSArray *)formValidationErrors
  398. {
  399. return [self.form localValidationErrors:self];
  400. }
  401. -(void)showFormValidationError:(NSError *)error
  402. {
  403. #if __IPHONE_OS_VERSION_MAX_ALLOWED < 80000
  404. UIAlertView * alertView = [[UIAlertView alloc] initWithTitle:NSLocalizedString(@"XLFormViewController_ValidationErrorTitle", nil)
  405. message:error.localizedDescription
  406. delegate:self
  407. cancelButtonTitle:NSLocalizedString(@"OK", nil)
  408. otherButtonTitles:nil];
  409. [alertView show];
  410. #else
  411. if ([UIAlertController class]){
  412. UIAlertController * alertController = [UIAlertController alertControllerWithTitle:NSLocalizedString(@"XLFormViewController_ValidationErrorTitle", nil)
  413. message:error.localizedDescription
  414. preferredStyle:UIAlertControllerStyleAlert];
  415. [alertController addAction:[UIAlertAction actionWithTitle:@"OK"
  416. style:UIAlertActionStyleDefault
  417. handler:nil]];
  418. [self presentViewController:alertController animated:YES completion:nil];
  419. }
  420. #ifndef XL_APP_EXTENSIONS
  421. else{
  422. UIAlertView * alertView = [[UIAlertView alloc] initWithTitle:NSLocalizedString(@"XLFormViewController_ValidationErrorTitle", nil)
  423. message:error.localizedDescription
  424. delegate:self
  425. cancelButtonTitle:NSLocalizedString(@"OK", nil)
  426. otherButtonTitles:nil];
  427. [alertView show];
  428. }
  429. #endif
  430. #endif
  431. }
  432. -(void)performFormSelector:(SEL)selector withObject:(id)sender
  433. {
  434. UIResponder * responder = [self targetForAction:selector withSender:sender];;
  435. if (responder) {
  436. #pragma GCC diagnostic push
  437. #pragma GCC diagnostic ignored "-Warc-performSelector-leaks"
  438. [responder performSelector:selector withObject:sender];
  439. #pragma GCC diagnostic pop
  440. }
  441. }
  442. #pragma mark - Private
  443. - (void)contentSizeCategoryChanged:(NSNotification *)notification
  444. {
  445. [self.tableView reloadData];
  446. }
  447. - (void)keyboardWillShow:(NSNotification *)notification
  448. {
  449. UIView * firstResponderView = [self.tableView findFirstResponder];
  450. UITableViewCell<XLFormDescriptorCell> * cell = [firstResponderView formDescriptorCell];
  451. if (cell){
  452. NSDictionary *keyboardInfo = [notification userInfo];
  453. _keyboardFrame = [self.tableView.window convertRect:[keyboardInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue] toView:self.tableView.superview];
  454. CGFloat newBottomInset = self.tableView.frame.origin.y + self.tableView.frame.size.height - _keyboardFrame.origin.y;
  455. UIEdgeInsets tableContentInset = self.tableView.contentInset;
  456. UIEdgeInsets tableScrollIndicatorInsets = self.tableView.scrollIndicatorInsets;
  457. _oldBottomTableContentInset = _oldBottomTableContentInset ?: @(tableContentInset.bottom);
  458. if (newBottomInset > [_oldBottomTableContentInset floatValue]){
  459. tableContentInset.bottom = newBottomInset;
  460. tableScrollIndicatorInsets.bottom = tableContentInset.bottom;
  461. [UIView beginAnimations:nil context:nil];
  462. [UIView setAnimationDuration:[keyboardInfo[UIKeyboardAnimationDurationUserInfoKey] doubleValue]];
  463. [UIView setAnimationCurve:[keyboardInfo[UIKeyboardAnimationCurveUserInfoKey] intValue]];
  464. self.tableView.contentInset = tableContentInset;
  465. self.tableView.scrollIndicatorInsets = tableScrollIndicatorInsets;
  466. NSIndexPath *selectedRow = [self.tableView indexPathForCell:cell];
  467. [self.tableView scrollToRowAtIndexPath:selectedRow atScrollPosition:UITableViewScrollPositionNone animated:NO];
  468. [UIView commitAnimations];
  469. }
  470. }
  471. }
  472. - (void)keyboardWillHide:(NSNotification *)notification
  473. {
  474. UIView * firstResponderView = [self.tableView findFirstResponder];
  475. UITableViewCell<XLFormDescriptorCell> * cell = [firstResponderView formDescriptorCell];
  476. if (cell){
  477. _keyboardFrame = CGRectZero;
  478. NSDictionary *keyboardInfo = [notification userInfo];
  479. UIEdgeInsets tableContentInset = self.tableView.contentInset;
  480. UIEdgeInsets tableScrollIndicatorInsets = self.tableView.scrollIndicatorInsets;
  481. tableContentInset.bottom = [_oldBottomTableContentInset floatValue];
  482. tableScrollIndicatorInsets.bottom = tableContentInset.bottom;
  483. _oldBottomTableContentInset = nil;
  484. [UIView beginAnimations:nil context:nil];
  485. [UIView setAnimationDuration:[keyboardInfo[UIKeyboardAnimationDurationUserInfoKey] doubleValue]];
  486. [UIView setAnimationCurve:[keyboardInfo[UIKeyboardAnimationCurveUserInfoKey] intValue]];
  487. self.tableView.contentInset = tableContentInset;
  488. self.tableView.scrollIndicatorInsets = tableScrollIndicatorInsets;
  489. [UIView commitAnimations];
  490. }
  491. }
  492. #pragma mark - Helpers
  493. -(void)deselectFormRow:(XLFormRowDescriptor *)formRow
  494. {
  495. NSIndexPath * indexPath = [self.form indexPathOfFormRow:formRow];
  496. if (indexPath){
  497. [self.tableView deselectRowAtIndexPath:indexPath animated:YES];
  498. }
  499. }
  500. -(void)reloadFormRow:(XLFormRowDescriptor *)formRow
  501. {
  502. NSIndexPath * indexPath = [self.form indexPathOfFormRow:formRow];
  503. if (indexPath){
  504. [self.tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone];
  505. }
  506. }
  507. -(XLFormBaseCell *)updateFormRow:(XLFormRowDescriptor *)formRow
  508. {
  509. XLFormBaseCell * cell = [formRow cellForFormController:self];
  510. [self configureCell:cell];
  511. [cell setNeedsUpdateConstraints];
  512. [cell setNeedsLayout];
  513. return cell;
  514. }
  515. -(void)configureCell:(XLFormBaseCell*) cell
  516. {
  517. [cell update];
  518. [cell.rowDescriptor.cellConfig enumerateKeysAndObjectsUsingBlock:^(NSString *keyPath, id value, BOOL * __unused stop) {
  519. [cell setValue:(value == [NSNull null]) ? nil : value forKeyPath:keyPath];
  520. }];
  521. if (cell.rowDescriptor.isDisabled){
  522. [cell.rowDescriptor.cellConfigIfDisabled enumerateKeysAndObjectsUsingBlock:^(NSString *keyPath, id value, BOOL * __unused stop) {
  523. [cell setValue:(value == [NSNull null]) ? nil : value forKeyPath:keyPath];
  524. }];
  525. }
  526. }
  527. #pragma mark - UITableViewDataSource
  528. -(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
  529. {
  530. return [self.form.formSections count];
  531. }
  532. - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
  533. {
  534. if (section >= self.form.formSections.count){
  535. @throw [NSException exceptionWithName:NSInvalidArgumentException reason:@"" userInfo:nil];
  536. }
  537. return [[[self.form.formSections objectAtIndex:section] formRows] count];
  538. }
  539. - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
  540. {
  541. XLFormRowDescriptor * rowDescriptor = [self.form formRowAtIndex:indexPath];
  542. [self updateFormRow:rowDescriptor];
  543. return [rowDescriptor cellForFormController:self];
  544. }
  545. -(BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath
  546. {
  547. XLFormRowDescriptor *rowDescriptor = [self.form formRowAtIndex:indexPath];
  548. if (rowDescriptor.isDisabled || !rowDescriptor.sectionDescriptor.isMultivaluedSection){
  549. return NO;
  550. }
  551. XLFormBaseCell * baseCell = [rowDescriptor cellForFormController:self];
  552. if ([baseCell conformsToProtocol:@protocol(XLFormInlineRowDescriptorCell)] && ((id<XLFormInlineRowDescriptorCell>)baseCell).inlineRowDescriptor){
  553. return NO;
  554. }
  555. return YES;
  556. }
  557. - (BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath {
  558. XLFormRowDescriptor *rowDescriptor = [self.form formRowAtIndex:indexPath];
  559. XLFormSectionDescriptor * section = rowDescriptor.sectionDescriptor;
  560. if (section.sectionOptions & XLFormSectionOptionCanReorder && section.formRows.count > 1) {
  561. if (section.sectionInsertMode == XLFormSectionInsertModeButton && section.sectionOptions & XLFormSectionOptionCanInsert){
  562. if (section.formRows.count <= 2 || rowDescriptor == section.multivaluedAddButton){
  563. return NO;
  564. }
  565. }
  566. XLFormBaseCell * baseCell = [rowDescriptor cellForFormController:self];
  567. return !([baseCell conformsToProtocol:@protocol(XLFormInlineRowDescriptorCell)] && ((id<XLFormInlineRowDescriptorCell>)baseCell).inlineRowDescriptor);
  568. }
  569. return NO;
  570. }
  571. - (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)sourceIndexPath toIndexPath:(NSIndexPath *)destinationIndexPath
  572. {
  573. XLFormRowDescriptor * row = [self.form formRowAtIndex:sourceIndexPath];
  574. XLFormSectionDescriptor * section = row.sectionDescriptor;
  575. #pragma GCC diagnostic push
  576. #pragma GCC diagnostic ignored "-Warc-performSelector-leaks"
  577. [section performSelector:NSSelectorFromString(@"moveRowAtIndexPath:toIndexPath:") withObject:sourceIndexPath withObject:destinationIndexPath];
  578. #pragma GCC diagnostic pop
  579. // update the accessory view
  580. [self inputAccessoryViewForRowDescriptor:row];
  581. dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.05 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
  582. self.tableView.editing = !self.tableView.editing;
  583. self.tableView.editing = !self.tableView.editing;
  584. });
  585. }
  586. -(void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
  587. {
  588. if (editingStyle == UITableViewCellEditingStyleDelete){
  589. XLFormRowDescriptor * multivaluedFormRow = [self.form formRowAtIndex:indexPath];
  590. // end editing
  591. UIView * firstResponder = [[multivaluedFormRow cellForFormController:self] findFirstResponder];
  592. if (firstResponder){
  593. [self.tableView endEditing:YES];
  594. }
  595. [multivaluedFormRow.sectionDescriptor removeFormRowAtIndex:indexPath.row];
  596. dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.02 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
  597. self.tableView.editing = !self.tableView.editing;
  598. self.tableView.editing = !self.tableView.editing;
  599. });
  600. if (firstResponder){
  601. UITableViewCell<XLFormDescriptorCell> * firstResponderCell = [firstResponder formDescriptorCell];
  602. XLFormRowDescriptor * rowDescriptor = firstResponderCell.rowDescriptor;
  603. [self inputAccessoryViewForRowDescriptor:rowDescriptor];
  604. }
  605. }
  606. else if (editingStyle == UITableViewCellEditingStyleInsert){
  607. XLFormSectionDescriptor * multivaluedFormSection = [self.form formSectionAtIndex:indexPath.section];
  608. if (multivaluedFormSection.sectionInsertMode == XLFormSectionInsertModeButton && multivaluedFormSection.sectionOptions & XLFormSectionOptionCanInsert){
  609. [self multivaluedInsertButtonTapped:multivaluedFormSection.multivaluedAddButton];
  610. }
  611. else{
  612. XLFormRowDescriptor * formRowDescriptor = [self formRowFormMultivaluedFormSection:multivaluedFormSection];
  613. [multivaluedFormSection addFormRow:formRowDescriptor];
  614. dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.02 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
  615. self.tableView.editing = !self.tableView.editing;
  616. self.tableView.editing = !self.tableView.editing;
  617. });
  618. [self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:indexPath.row + 1 inSection:indexPath.section] atScrollPosition:UITableViewScrollPositionBottom animated:YES];
  619. UITableViewCell<XLFormDescriptorCell> * cell = (UITableViewCell<XLFormDescriptorCell> *)[formRowDescriptor cellForFormController:self];
  620. if ([cell formDescriptorCellCanBecomeFirstResponder]){
  621. [cell formDescriptorCellBecomeFirstResponder];
  622. }
  623. }
  624. }
  625. }
  626. #pragma mark - UITableViewDelegate
  627. -(NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
  628. {
  629. return [[self.form.formSections objectAtIndex:section] title];
  630. }
  631. -(NSString *)tableView:(UITableView *)tableView titleForFooterInSection:(NSInteger)section
  632. {
  633. return [[self.form.formSections objectAtIndex:section] footerTitle];
  634. }
  635. -(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
  636. {
  637. XLFormRowDescriptor *rowDescriptor = [self.form formRowAtIndex:indexPath];
  638. [rowDescriptor cellForFormController:self];
  639. CGFloat height = rowDescriptor.height;
  640. if (height != XLFormUnspecifiedCellHeight){
  641. return height;
  642. }
  643. return self.tableView.rowHeight;
  644. }
  645. -(CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath
  646. {
  647. XLFormRowDescriptor *rowDescriptor = [self.form formRowAtIndex:indexPath];
  648. [rowDescriptor cellForFormController:self];
  649. CGFloat height = rowDescriptor.height;
  650. if (height != XLFormUnspecifiedCellHeight){
  651. return height;
  652. }
  653. if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(@"8.0")){
  654. return self.tableView.estimatedRowHeight;
  655. }
  656. return 44;
  657. }
  658. -(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
  659. {
  660. XLFormRowDescriptor * row = [self.form formRowAtIndex:indexPath];
  661. if (row.isDisabled) {
  662. return;
  663. }
  664. UITableViewCell<XLFormDescriptorCell> * cell = (UITableViewCell<XLFormDescriptorCell> *)[row cellForFormController:self];
  665. if (!([cell formDescriptorCellCanBecomeFirstResponder] && [cell formDescriptorCellBecomeFirstResponder])){
  666. [self.tableView endEditing:YES];
  667. }
  668. [self didSelectFormRow:row];
  669. }
  670. -(UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath
  671. {
  672. XLFormRowDescriptor * row = [self.form formRowAtIndex:indexPath];
  673. XLFormSectionDescriptor * section = row.sectionDescriptor;
  674. if (section.sectionOptions & XLFormSectionOptionCanInsert){
  675. if (section.formRows.count == indexPath.row + 2){
  676. if ([[XLFormViewController inlineRowDescriptorTypesForRowDescriptorTypes].allKeys containsObject:row.rowType]){
  677. UITableViewCell<XLFormDescriptorCell> * cell = [row cellForFormController:self];
  678. UIView * firstResponder = [cell findFirstResponder];
  679. if (firstResponder){
  680. return UITableViewCellEditingStyleInsert;
  681. }
  682. }
  683. }
  684. else if (section.formRows.count == (indexPath.row + 1)){
  685. return UITableViewCellEditingStyleInsert;
  686. }
  687. }
  688. if (section.sectionOptions & XLFormSectionOptionCanDelete){
  689. return UITableViewCellEditingStyleDelete;
  690. }
  691. return UITableViewCellEditingStyleNone;
  692. }
  693. - (NSIndexPath *)tableView:(UITableView *)tableView targetIndexPathForMoveFromRowAtIndexPath:(NSIndexPath *)sourceIndexPath
  694. toProposedIndexPath:(NSIndexPath *)proposedDestinationIndexPath
  695. {
  696. if (sourceIndexPath.section != proposedDestinationIndexPath.section) {
  697. return sourceIndexPath;
  698. }
  699. XLFormSectionDescriptor * sectionDescriptor = [self.form formSectionAtIndex:sourceIndexPath.section];
  700. XLFormRowDescriptor * proposedDestination = [sectionDescriptor.formRows objectAtIndex:proposedDestinationIndexPath.row];
  701. XLFormBaseCell * proposedDestinationCell = [proposedDestination cellForFormController:self];
  702. if (([proposedDestinationCell conformsToProtocol:@protocol(XLFormInlineRowDescriptorCell)] && ((id<XLFormInlineRowDescriptorCell>)proposedDestinationCell).inlineRowDescriptor) || ([[XLFormViewController inlineRowDescriptorTypesForRowDescriptorTypes].allKeys containsObject:proposedDestinationCell.rowDescriptor.rowType] && [[proposedDestinationCell findFirstResponder] formDescriptorCell] == proposedDestinationCell)) {
  703. if (sourceIndexPath.row < proposedDestinationIndexPath.row){
  704. return [NSIndexPath indexPathForRow:proposedDestinationIndexPath.row + 1 inSection:sourceIndexPath.section];
  705. }
  706. else{
  707. return [NSIndexPath indexPathForRow:proposedDestinationIndexPath.row - 1 inSection:sourceIndexPath.section];
  708. }
  709. }
  710. if ((sectionDescriptor.sectionInsertMode == XLFormSectionInsertModeButton && sectionDescriptor.sectionOptions & XLFormSectionOptionCanInsert)){
  711. if (proposedDestinationIndexPath.row == sectionDescriptor.formRows.count - 1){
  712. return [NSIndexPath indexPathForRow:(sectionDescriptor.formRows.count - 2) inSection:sourceIndexPath.section];
  713. }
  714. }
  715. return proposedDestinationIndexPath;
  716. }
  717. - (BOOL)tableView:(UITableView *)tableView shouldIndentWhileEditingRowAtIndexPath:(NSIndexPath *)indexPath
  718. {
  719. UITableViewCellEditingStyle editingStyle = [self tableView:tableView editingStyleForRowAtIndexPath:indexPath];
  720. if (editingStyle == UITableViewCellEditingStyleNone){
  721. return NO;
  722. }
  723. return YES;
  724. }
  725. - (void)tableView:(UITableView *)tableView willBeginReorderingRowAtIndexPath:(NSIndexPath *)indexPath
  726. {
  727. // end editing if inline cell is first responder
  728. UITableViewCell<XLFormDescriptorCell> * cell = [[self.tableView findFirstResponder] formDescriptorCell];
  729. if ([[self.form indexPathOfFormRow:cell.rowDescriptor] isEqual:indexPath]){
  730. if ([[XLFormViewController inlineRowDescriptorTypesForRowDescriptorTypes].allKeys containsObject:cell.rowDescriptor.rowType]){
  731. [self.tableView endEditing:YES];
  732. }
  733. }
  734. }
  735. #pragma mark - UITextFieldDelegate
  736. - (BOOL)textFieldShouldClear:(UITextField *)textField
  737. {
  738. return YES;
  739. }
  740. - (BOOL)textFieldShouldReturn:(UITextField *)textField
  741. {
  742. // called when 'return' key pressed. return NO to ignore.
  743. UITableViewCell<XLFormDescriptorCell> * cell = [textField formDescriptorCell];
  744. XLFormRowDescriptor * currentRow = cell.rowDescriptor;
  745. XLFormRowDescriptor * nextRow = [self nextRowDescriptorForRow:currentRow
  746. withDirection:XLFormRowNavigationDirectionNext];
  747. if (nextRow){
  748. UITableViewCell<XLFormDescriptorCell> * nextCell = (UITableViewCell<XLFormDescriptorCell> *)[nextRow cellForFormController:self];
  749. if ([nextCell formDescriptorCellCanBecomeFirstResponder]){
  750. [nextCell formDescriptorCellBecomeFirstResponder];
  751. return YES;
  752. }
  753. }
  754. [self.tableView endEditing:YES];
  755. return YES;
  756. }
  757. - (BOOL)textFieldShouldBeginEditing:(UITextField *)textField
  758. {
  759. UITableViewCell<XLFormDescriptorCell>* cell = textField.formDescriptorCell;
  760. XLFormRowDescriptor * nextRow = [self nextRowDescriptorForRow:textField.formDescriptorCell.rowDescriptor
  761. withDirection:XLFormRowNavigationDirectionNext];
  762. if ([cell conformsToProtocol:@protocol(XLFormReturnKeyProtocol)]) {
  763. textField.returnKeyType = nextRow ? ((id<XLFormReturnKeyProtocol>)cell).nextReturnKeyType : ((id<XLFormReturnKeyProtocol>)cell).returnKeyType;
  764. }
  765. else {
  766. textField.returnKeyType = nextRow ? UIReturnKeyNext : UIReturnKeyDefault;
  767. }
  768. return YES;
  769. }
  770. - (BOOL)textFieldShouldEndEditing:(UITextField *)textField
  771. {
  772. return YES;
  773. }
  774. - (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
  775. return YES;
  776. }
  777. - (void)textFieldDidBeginEditing:(UITextField *)textField
  778. {
  779. }
  780. -(void)textFieldDidEndEditing:(UITextField *)textField
  781. {
  782. }
  783. #pragma mark - UITextViewDelegate
  784. - (BOOL)textViewShouldBeginEditing:(UITextView *)textView
  785. {
  786. return YES;
  787. }
  788. -(void)textViewDidBeginEditing:(UITextView *)textView
  789. {
  790. }
  791. -(void)textViewDidEndEditing:(UITextView *)textView
  792. {
  793. }
  794. - (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text {
  795. return YES;
  796. }
  797. #pragma mark - UIScrollViewDelegate
  798. - (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
  799. {
  800. //dismiss keyboard
  801. if (NO == self.form.endEditingTableViewOnScroll) {
  802. return;
  803. }
  804. UIView * firstResponder = [self.tableView findFirstResponder];
  805. if ([firstResponder conformsToProtocol:@protocol(XLFormDescriptorCell)]){
  806. id<XLFormDescriptorCell> cell = (id<XLFormDescriptorCell>)firstResponder;
  807. if ([[XLFormViewController inlineRowDescriptorTypesForRowDescriptorTypes].allKeys containsObject:cell.rowDescriptor.rowType]){
  808. return;
  809. }
  810. }
  811. [self.tableView endEditing:YES];
  812. }
  813. #pragma mark - Segue
  814. -(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
  815. {
  816. if ([sender isKindOfClass:[XLFormRowDescriptor class]]){
  817. UIViewController * destinationViewController = segue.destinationViewController;
  818. XLFormRowDescriptor * rowDescriptor = (XLFormRowDescriptor *)sender;
  819. if (rowDescriptor.rowType == XLFormRowDescriptorTypeSelectorPush || rowDescriptor.rowType == XLFormRowDescriptorTypeSelectorPopover){
  820. NSAssert([destinationViewController conformsToProtocol:@protocol(XLFormRowDescriptorViewController)], @"Segue destinationViewController must conform to XLFormRowDescriptorViewController protocol");
  821. UIViewController<XLFormRowDescriptorViewController> * rowDescriptorViewController = (UIViewController<XLFormRowDescriptorViewController> *)destinationViewController;
  822. rowDescriptorViewController.rowDescriptor = rowDescriptor;
  823. }
  824. else if ([destinationViewController conformsToProtocol:@protocol(XLFormRowDescriptorViewController)]){
  825. UIViewController<XLFormRowDescriptorViewController> * rowDescriptorViewController = (UIViewController<XLFormRowDescriptorViewController> *)destinationViewController;
  826. rowDescriptorViewController.rowDescriptor = rowDescriptor;
  827. }
  828. }
  829. }
  830. #pragma mark - Navigation Between Fields
  831. -(void)rowNavigationAction:(UIBarButtonItem *)sender
  832. {
  833. [self navigateToDirection:(sender == self.navigationAccessoryView.nextButton ? XLFormRowNavigationDirectionNext : XLFormRowNavigationDirectionPrevious)];
  834. }
  835. -(void)rowNavigationDone:(UIBarButtonItem *)sender
  836. {
  837. [self.tableView endEditing:YES];
  838. }
  839. -(void)navigateToDirection:(XLFormRowNavigationDirection)direction
  840. {
  841. UIView * firstResponder = [self.tableView findFirstResponder];
  842. UITableViewCell<XLFormDescriptorCell> * currentCell = [firstResponder formDescriptorCell];
  843. NSIndexPath * currentIndexPath = [self.tableView indexPathForCell:currentCell];
  844. XLFormRowDescriptor * currentRow = [self.form formRowAtIndex:currentIndexPath];
  845. XLFormRowDescriptor * nextRow = [self nextRowDescriptorForRow:currentRow withDirection:direction];
  846. if (nextRow) {
  847. UITableViewCell<XLFormDescriptorCell> * cell = (UITableViewCell<XLFormDescriptorCell> *)[nextRow cellForFormController:self];
  848. if ([cell formDescriptorCellCanBecomeFirstResponder]){
  849. NSIndexPath * indexPath = [self.form indexPathOfFormRow:nextRow];
  850. [self.tableView scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionNone animated:NO];
  851. [cell formDescriptorCellBecomeFirstResponder];
  852. }
  853. }
  854. }
  855. -(XLFormRowDescriptor *)nextRowDescriptorForRow:(XLFormRowDescriptor*)currentRow withDirection:(XLFormRowNavigationDirection)direction
  856. {
  857. if (!currentRow || (self.form.rowNavigationOptions & XLFormRowNavigationOptionEnabled) != XLFormRowNavigationOptionEnabled) {
  858. return nil;
  859. }
  860. XLFormRowDescriptor * nextRow = (direction == XLFormRowNavigationDirectionNext) ? [self.form nextRowDescriptorForRow:currentRow] : [self.form previousRowDescriptorForRow:currentRow];
  861. if (!nextRow) {
  862. return nil;
  863. }
  864. if ([[nextRow cellForFormController:self] conformsToProtocol:@protocol(XLFormInlineRowDescriptorCell)]) {
  865. id<XLFormInlineRowDescriptorCell> inlineCell = (id<XLFormInlineRowDescriptorCell>)[nextRow cellForFormController:self];
  866. if (inlineCell.inlineRowDescriptor){
  867. return [self nextRowDescriptorForRow:nextRow withDirection:direction];
  868. }
  869. }
  870. XLFormRowNavigationOptions rowNavigationOptions = self.form.rowNavigationOptions;
  871. if (nextRow.isDisabled && ((rowNavigationOptions & XLFormRowNavigationOptionStopDisableRow) == XLFormRowNavigationOptionStopDisableRow)){
  872. return nil;
  873. }
  874. if (!nextRow.isDisabled && ((rowNavigationOptions & XLFormRowNavigationOptionStopInlineRow) == XLFormRowNavigationOptionStopInlineRow) && [[[XLFormViewController inlineRowDescriptorTypesForRowDescriptorTypes] allKeys] containsObject:nextRow.rowType]){
  875. return nil;
  876. }
  877. UITableViewCell<XLFormDescriptorCell> * cell = (UITableViewCell<XLFormDescriptorCell> *)[nextRow cellForFormController:self];
  878. if (!nextRow.isDisabled && ((rowNavigationOptions & XLFormRowNavigationOptionSkipCanNotBecomeFirstResponderRow) != XLFormRowNavigationOptionSkipCanNotBecomeFirstResponderRow) && (![cell formDescriptorCellCanBecomeFirstResponder])){
  879. return nil;
  880. }
  881. if (!nextRow.isDisabled && [cell formDescriptorCellCanBecomeFirstResponder]){
  882. return nextRow;
  883. }
  884. return [self nextRowDescriptorForRow:nextRow withDirection:direction];
  885. }
  886. #pragma mark - properties
  887. -(void)setForm:(XLFormDescriptor *)form
  888. {
  889. _form.delegate = nil;
  890. [self.tableView endEditing:YES];
  891. _form = form;
  892. _form.delegate = self;
  893. [_form forceEvaluate];
  894. if ([self isViewLoaded]){
  895. [self.tableView reloadData];
  896. }
  897. }
  898. -(XLFormDescriptor *)form
  899. {
  900. return _form;
  901. }
  902. -(XLFormRowNavigationAccessoryView *)navigationAccessoryView
  903. {
  904. if (_navigationAccessoryView) return _navigationAccessoryView;
  905. _navigationAccessoryView = [XLFormRowNavigationAccessoryView new];
  906. _navigationAccessoryView.previousButton.target = self;
  907. _navigationAccessoryView.previousButton.action = @selector(rowNavigationAction:);
  908. _navigationAccessoryView.nextButton.target = self;
  909. _navigationAccessoryView.nextButton.action = @selector(rowNavigationAction:);
  910. _navigationAccessoryView.doneButton.target = self;
  911. _navigationAccessoryView.doneButton.action = @selector(rowNavigationDone:);
  912. _navigationAccessoryView.tintColor = self.view.tintColor;
  913. return _navigationAccessoryView;
  914. }
  915. @end