XLFormSectionDescriptor.m 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403
  1. //
  2. // XLFormSectionDescriptor.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 "XLForm.h"
  26. #import "XLFormSectionDescriptor.h"
  27. #import "NSPredicate+XLFormAdditions.h"
  28. #import "NSString+XLFormAdditions.h"
  29. #import "UIView+XLFormAdditions.h"
  30. @interface XLFormDescriptor (_XLFormSectionDescriptor)
  31. @property (readonly) NSDictionary* allRowsByTag;
  32. -(void)addRowToTagCollection:(XLFormRowDescriptor*)rowDescriptor;
  33. -(void)removeRowFromTagCollection:(XLFormRowDescriptor*) rowDescriptor;
  34. -(void)showFormSection:(XLFormSectionDescriptor*)formSection;
  35. -(void)hideFormSection:(XLFormSectionDescriptor*)formSection;
  36. -(void)addObserversOfObject:(id)sectionOrRow predicateType:(XLPredicateType)predicateType;
  37. -(void)removeObserversOfObject:(id)sectionOrRow predicateType:(XLPredicateType)predicateType;
  38. @end
  39. @interface XLFormSectionDescriptor()
  40. @property NSMutableArray * formRows;
  41. @property NSMutableArray * allRows;
  42. @property BOOL isDirtyHidePredicateCache;
  43. @property (nonatomic) NSNumber* hidePredicateCache;
  44. @end
  45. @implementation XLFormSectionDescriptor
  46. @synthesize hidden = _hidden;
  47. @synthesize hidePredicateCache = _hidePredicateCache;
  48. -(instancetype)init
  49. {
  50. self = [super init];
  51. if (self){
  52. _formRows = [NSMutableArray array];
  53. _allRows = [NSMutableArray array];
  54. _sectionInsertMode = XLFormSectionInsertModeLastRow;
  55. _sectionOptions = XLFormSectionOptionNone;
  56. _title = nil;
  57. _footerTitle = nil;
  58. _hidden = @NO;
  59. _hidePredicateCache = @NO;
  60. _isDirtyHidePredicateCache = YES;
  61. [self addObserver:self forKeyPath:@"formRows" options:(NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld) context:0];
  62. }
  63. return self;
  64. }
  65. -(instancetype)initWithTitle:(NSString *)title sectionOptions:(XLFormSectionOptions)sectionOptions sectionInsertMode:(XLFormSectionInsertMode)sectionInsertMode{
  66. self = [self init];
  67. if (self){
  68. _sectionInsertMode = sectionInsertMode;
  69. _sectionOptions = sectionOptions;
  70. _title = title;
  71. if ([self canInsertUsingButton]){
  72. _multivaluedAddButton = [XLFormRowDescriptor formRowDescriptorWithTag:nil rowType:XLFormRowDescriptorTypeButton title:@"Add Item"];
  73. [_multivaluedAddButton.cellConfig setObject:@(NSTextAlignmentNatural) forKey:@"textLabel.textAlignment"];
  74. _multivaluedAddButton.action.formSelector = NSSelectorFromString(@"multivaluedInsertButtonTapped:");
  75. [self insertObject:_multivaluedAddButton inFormRowsAtIndex:0];
  76. [self insertObject:_multivaluedAddButton inAllRowsAtIndex:0];
  77. }
  78. }
  79. return self;
  80. }
  81. +(instancetype)formSection
  82. {
  83. return [[self class] formSectionWithTitle:nil];
  84. }
  85. +(instancetype)formSectionWithTitle:(NSString *)title
  86. {
  87. return [[self class] formSectionWithTitle:title sectionOptions:XLFormSectionOptionNone];
  88. }
  89. +(instancetype)formSectionWithTitle:(NSString *)title multivaluedSection:(BOOL)multivaluedSection
  90. {
  91. return [[self class] formSectionWithTitle:title sectionOptions:(multivaluedSection ? XLFormSectionOptionCanInsert | XLFormSectionOptionCanDelete : XLFormSectionOptionNone)];
  92. }
  93. +(instancetype)formSectionWithTitle:(NSString *)title sectionOptions:(XLFormSectionOptions)sectionOptions
  94. {
  95. return [[self class] formSectionWithTitle:title sectionOptions:sectionOptions sectionInsertMode:XLFormSectionInsertModeLastRow];
  96. }
  97. +(instancetype)formSectionWithTitle:(NSString *)title sectionOptions:(XLFormSectionOptions)sectionOptions sectionInsertMode:(XLFormSectionInsertMode)sectionInsertMode
  98. {
  99. return [[[self class] alloc] initWithTitle:title sectionOptions:sectionOptions sectionInsertMode:sectionInsertMode];
  100. }
  101. -(BOOL)isMultivaluedSection
  102. {
  103. return (self.sectionOptions != XLFormSectionOptionNone);
  104. }
  105. -(void)addFormRow:(XLFormRowDescriptor *)formRow
  106. {
  107. [self insertObject:formRow inAllRowsAtIndex:([self canInsertUsingButton] ? MAX(0, [self.formRows count] - 1) : [self.allRows count])];
  108. }
  109. -(void)addFormRow:(XLFormRowDescriptor *)formRow afterRow:(XLFormRowDescriptor *)afterRow
  110. {
  111. NSUInteger allRowIndex = [self.allRows indexOfObject:afterRow];
  112. if (allRowIndex != NSNotFound) {
  113. [self insertObject:formRow inAllRowsAtIndex:allRowIndex+1];
  114. }
  115. else { //case when afterRow does not exist. Just insert at the end.
  116. [self addFormRow:formRow];
  117. return;
  118. }
  119. }
  120. -(void)addFormRow:(XLFormRowDescriptor *)formRow beforeRow:(XLFormRowDescriptor *)beforeRow
  121. {
  122. NSUInteger allRowIndex = [self.allRows indexOfObject:beforeRow];
  123. if (allRowIndex != NSNotFound) {
  124. [self insertObject:formRow inAllRowsAtIndex:allRowIndex];
  125. }
  126. else { //case when afterRow does not exist. Just insert at the end.
  127. [self addFormRow:formRow];
  128. return;
  129. }
  130. }
  131. -(void)removeFormRowAtIndex:(NSUInteger)index
  132. {
  133. if (self.formRows.count > index){
  134. XLFormRowDescriptor *formRow = [self.formRows objectAtIndex:index];
  135. NSUInteger allRowIndex = [self.allRows indexOfObject:formRow];
  136. [self removeObjectFromFormRowsAtIndex:index];
  137. [self removeObjectFromAllRowsAtIndex:allRowIndex];
  138. }
  139. }
  140. -(void)removeFormRow:(XLFormRowDescriptor *)formRow
  141. {
  142. NSUInteger index = NSNotFound;
  143. if ((index = [self.formRows indexOfObject:formRow]) != NSNotFound){
  144. [self removeFormRowAtIndex:index];
  145. }
  146. else if ((index = [self.allRows indexOfObject:formRow]) != NSNotFound){
  147. if (self.allRows.count > index){
  148. [self removeObjectFromAllRowsAtIndex:index];
  149. }
  150. };
  151. }
  152. - (void)moveRowAtIndexPath:(NSIndexPath *)sourceIndex toIndexPath:(NSIndexPath *)destinationIndex
  153. {
  154. if ((sourceIndex.row < self.formRows.count) && (destinationIndex.row < self.formRows.count) && (sourceIndex.row != destinationIndex.row)){
  155. XLFormRowDescriptor * row = [self objectInFormRowsAtIndex:sourceIndex.row];
  156. XLFormRowDescriptor * destRow = [self objectInFormRowsAtIndex:destinationIndex.row];
  157. [self.formRows removeObjectAtIndex:sourceIndex.row];
  158. [self.formRows insertObject:row atIndex:destinationIndex.row];
  159. [self.allRows removeObjectAtIndex:[self.allRows indexOfObject:row]];
  160. [self.allRows insertObject:row atIndex:[self.allRows indexOfObject:destRow]];
  161. }
  162. }
  163. -(void)dealloc
  164. {
  165. [self.formDescriptor removeObserversOfObject:self predicateType:XLPredicateTypeHidden];
  166. @try {
  167. [self removeObserver:self forKeyPath:@"formRows"];
  168. }
  169. @catch (NSException * __unused exception) {}
  170. }
  171. #pragma mark - Show/hide rows
  172. -(void)showFormRow:(XLFormRowDescriptor*)formRow{
  173. NSUInteger formIndex = [self.formRows indexOfObject:formRow];
  174. if (formIndex != NSNotFound) {
  175. return;
  176. }
  177. NSUInteger index = [self.allRows indexOfObject:formRow];
  178. if (index != NSNotFound){
  179. while (formIndex == NSNotFound && index > 0) {
  180. XLFormRowDescriptor* previous = [self.allRows objectAtIndex:--index];
  181. formIndex = [self.formRows indexOfObject:previous];
  182. }
  183. if (formIndex == NSNotFound){ // index == 0 => insert at the beginning
  184. [self insertObject:formRow inFormRowsAtIndex:0];
  185. }
  186. else {
  187. [self insertObject:formRow inFormRowsAtIndex:formIndex+1];
  188. }
  189. }
  190. }
  191. -(void)hideFormRow:(XLFormRowDescriptor*)formRow{
  192. NSUInteger index = [self.formRows indexOfObject:formRow];
  193. if (index != NSNotFound){
  194. [self removeObjectFromFormRowsAtIndex:index];
  195. }
  196. }
  197. #pragma mark - KVO
  198. -(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
  199. {
  200. if (!self.formDescriptor.delegate) return;
  201. if ([keyPath isEqualToString:@"formRows"]){
  202. if ([self.formDescriptor.formSections containsObject:self]){
  203. if ([[change objectForKey:NSKeyValueChangeKindKey] isEqualToNumber:@(NSKeyValueChangeInsertion)]){
  204. NSIndexSet * indexSet = [change objectForKey:NSKeyValueChangeIndexesKey];
  205. XLFormRowDescriptor * formRow = [((XLFormSectionDescriptor *)object).formRows objectAtIndex:indexSet.firstIndex];
  206. NSUInteger sectionIndex = [self.formDescriptor.formSections indexOfObject:object];
  207. [self.formDescriptor.delegate formRowHasBeenAdded:formRow atIndexPath:[NSIndexPath indexPathForRow:indexSet.firstIndex inSection:sectionIndex]];
  208. }
  209. else if ([[change objectForKey:NSKeyValueChangeKindKey] isEqualToNumber:@(NSKeyValueChangeRemoval)]){
  210. NSIndexSet * indexSet = [change objectForKey:NSKeyValueChangeIndexesKey];
  211. XLFormRowDescriptor * removedRow = [[change objectForKey:NSKeyValueChangeOldKey] objectAtIndex:0];
  212. NSUInteger sectionIndex = [self.formDescriptor.formSections indexOfObject:object];
  213. [self.formDescriptor.delegate formRowHasBeenRemoved:removedRow atIndexPath:[NSIndexPath indexPathForRow:indexSet.firstIndex inSection:sectionIndex]];
  214. }
  215. }
  216. }
  217. }
  218. #pragma mark - KVC
  219. -(NSUInteger)countOfFormRows
  220. {
  221. return self.formRows.count;
  222. }
  223. - (id)objectInFormRowsAtIndex:(NSUInteger)index
  224. {
  225. return [self.formRows objectAtIndex:index];
  226. }
  227. - (NSArray *)formRowsAtIndexes:(NSIndexSet *)indexes
  228. {
  229. return [self.formRows objectsAtIndexes:indexes];
  230. }
  231. - (void)insertObject:(XLFormRowDescriptor *)formRow inFormRowsAtIndex:(NSUInteger)index
  232. {
  233. formRow.sectionDescriptor = self;
  234. [self.formRows insertObject:formRow atIndex:index];
  235. }
  236. - (void)removeObjectFromFormRowsAtIndex:(NSUInteger)index
  237. {
  238. [self.formRows removeObjectAtIndex:index];
  239. }
  240. #pragma mark - KVC ALL
  241. -(NSUInteger)countOfAllRows
  242. {
  243. return self.allRows.count;
  244. }
  245. - (id)objectInAllRowsAtIndex:(NSUInteger)index
  246. {
  247. return [self.allRows objectAtIndex:index];
  248. }
  249. - (NSArray *)allRowsAtIndexes:(NSIndexSet *)indexes
  250. {
  251. return [self.allRows objectsAtIndexes:indexes];
  252. }
  253. - (void)insertObject:(XLFormRowDescriptor *)row inAllRowsAtIndex:(NSUInteger)index
  254. {
  255. row.sectionDescriptor = self;
  256. [self.formDescriptor addRowToTagCollection:row];
  257. [self.allRows insertObject:row atIndex:index];
  258. row.disabled = row.disabled;
  259. row.hidden = row.hidden;
  260. }
  261. - (void)removeObjectFromAllRowsAtIndex:(NSUInteger)index
  262. {
  263. XLFormRowDescriptor * row = [self.allRows objectAtIndex:index];
  264. [self.formDescriptor removeRowFromTagCollection:row];
  265. [self.formDescriptor removeObserversOfObject:row predicateType:XLPredicateTypeDisabled];
  266. [self.formDescriptor removeObserversOfObject:row predicateType:XLPredicateTypeHidden];
  267. [self.allRows removeObjectAtIndex:index];
  268. }
  269. #pragma mark - Helpers
  270. -(BOOL)canInsertUsingButton
  271. {
  272. return (self.sectionInsertMode == XLFormSectionInsertModeButton && self.sectionOptions & XLFormSectionOptionCanInsert);
  273. }
  274. #pragma mark - Predicates
  275. -(NSNumber *)hidePredicateCache
  276. {
  277. return _hidePredicateCache;
  278. }
  279. -(void)setHidePredicateCache:(NSNumber *)hidePredicateCache
  280. {
  281. NSParameterAssert(hidePredicateCache);
  282. self.isDirtyHidePredicateCache = NO;
  283. if (!_hidePredicateCache || ![_hidePredicateCache isEqualToNumber:hidePredicateCache]){
  284. _hidePredicateCache = hidePredicateCache;
  285. }
  286. }
  287. -(BOOL)isHidden
  288. {
  289. if (self.isDirtyHidePredicateCache) {
  290. return [self evaluateIsHidden];
  291. }
  292. return [self.hidePredicateCache boolValue];
  293. }
  294. -(BOOL)evaluateIsHidden
  295. {
  296. if ([_hidden isKindOfClass:[NSPredicate class]]) {
  297. if (!self.formDescriptor) {
  298. self.isDirtyHidePredicateCache = YES;
  299. } else {
  300. @try {
  301. self.hidePredicateCache = @([_hidden evaluateWithObject:self substitutionVariables:self.formDescriptor.allRowsByTag ?: @{}]);
  302. }
  303. @catch (NSException *exception) {
  304. // predicate syntax error.
  305. self.isDirtyHidePredicateCache = YES;
  306. };
  307. }
  308. }
  309. else{
  310. self.hidePredicateCache = _hidden;
  311. }
  312. if ([self.hidePredicateCache boolValue]){
  313. if ([self.formDescriptor.delegate isKindOfClass:[XLFormViewController class]]){
  314. XLFormBaseCell* firtResponder = (XLFormBaseCell*) [((XLFormViewController*)self.formDescriptor.delegate).tableView findFirstResponder];
  315. if ([firtResponder isKindOfClass:[XLFormBaseCell class]] && firtResponder.rowDescriptor.sectionDescriptor == self){
  316. [firtResponder resignFirstResponder];
  317. }
  318. }
  319. [self.formDescriptor hideFormSection:self];
  320. }
  321. else{
  322. [self.formDescriptor showFormSection:self];
  323. }
  324. return [self.hidePredicateCache boolValue];
  325. }
  326. -(id)hidden
  327. {
  328. return _hidden;
  329. }
  330. -(void)setHidden:(id)hidden
  331. {
  332. if ([_hidden isKindOfClass:[NSPredicate class]]){
  333. [self.formDescriptor removeObserversOfObject:self predicateType:XLPredicateTypeHidden];
  334. }
  335. _hidden = [hidden isKindOfClass:[NSString class]] ? [hidden formPredicate] : hidden;
  336. if ([_hidden isKindOfClass:[NSPredicate class]]){
  337. [self.formDescriptor addObserversOfObject:self predicateType:XLPredicateTypeHidden];
  338. }
  339. [self evaluateIsHidden]; // check and update if this row should be hidden.
  340. }
  341. @end