123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403 |
- //
- // XLFormSectionDescriptor.m
- // XLForm ( https://github.com/xmartlabs/XLForm )
- //
- // Copyright (c) 2015 Xmartlabs ( http://xmartlabs.com )
- //
- //
- // Permission is hereby granted, free of charge, to any person obtaining a copy
- // of this software and associated documentation files (the "Software"), to deal
- // in the Software without restriction, including without limitation the rights
- // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- // copies of the Software, and to permit persons to whom the Software is
- // furnished to do so, subject to the following conditions:
- //
- // The above copyright notice and this permission notice shall be included in
- // all copies or substantial portions of the Software.
- //
- // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- // THE SOFTWARE.
- #import "XLForm.h"
- #import "XLFormSectionDescriptor.h"
- #import "NSPredicate+XLFormAdditions.h"
- #import "NSString+XLFormAdditions.h"
- #import "UIView+XLFormAdditions.h"
- @interface XLFormDescriptor (_XLFormSectionDescriptor)
- @property (readonly) NSDictionary* allRowsByTag;
- -(void)addRowToTagCollection:(XLFormRowDescriptor*)rowDescriptor;
- -(void)removeRowFromTagCollection:(XLFormRowDescriptor*) rowDescriptor;
- -(void)showFormSection:(XLFormSectionDescriptor*)formSection;
- -(void)hideFormSection:(XLFormSectionDescriptor*)formSection;
- -(void)addObserversOfObject:(id)sectionOrRow predicateType:(XLPredicateType)predicateType;
- -(void)removeObserversOfObject:(id)sectionOrRow predicateType:(XLPredicateType)predicateType;
- @end
- @interface XLFormSectionDescriptor()
- @property NSMutableArray * formRows;
- @property NSMutableArray * allRows;
- @property BOOL isDirtyHidePredicateCache;
- @property (nonatomic) NSNumber* hidePredicateCache;
- @end
- @implementation XLFormSectionDescriptor
- @synthesize hidden = _hidden;
- @synthesize hidePredicateCache = _hidePredicateCache;
- -(instancetype)init
- {
- self = [super init];
- if (self){
- _formRows = [NSMutableArray array];
- _allRows = [NSMutableArray array];
- _sectionInsertMode = XLFormSectionInsertModeLastRow;
- _sectionOptions = XLFormSectionOptionNone;
- _title = nil;
- _footerTitle = nil;
- _hidden = @NO;
- _hidePredicateCache = @NO;
- _isDirtyHidePredicateCache = YES;
- [self addObserver:self forKeyPath:@"formRows" options:(NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld) context:0];
- }
- return self;
- }
- -(instancetype)initWithTitle:(NSString *)title sectionOptions:(XLFormSectionOptions)sectionOptions sectionInsertMode:(XLFormSectionInsertMode)sectionInsertMode{
- self = [self init];
- if (self){
- _sectionInsertMode = sectionInsertMode;
- _sectionOptions = sectionOptions;
- _title = title;
- if ([self canInsertUsingButton]){
- _multivaluedAddButton = [XLFormRowDescriptor formRowDescriptorWithTag:nil rowType:XLFormRowDescriptorTypeButton title:@"Add Item"];
- [_multivaluedAddButton.cellConfig setObject:@(NSTextAlignmentNatural) forKey:@"textLabel.textAlignment"];
- _multivaluedAddButton.action.formSelector = NSSelectorFromString(@"multivaluedInsertButtonTapped:");
- [self insertObject:_multivaluedAddButton inFormRowsAtIndex:0];
- [self insertObject:_multivaluedAddButton inAllRowsAtIndex:0];
- }
- }
- return self;
- }
- +(instancetype)formSection
- {
- return [[self class] formSectionWithTitle:nil];
- }
- +(instancetype)formSectionWithTitle:(NSString *)title
- {
- return [[self class] formSectionWithTitle:title sectionOptions:XLFormSectionOptionNone];
- }
- +(instancetype)formSectionWithTitle:(NSString *)title multivaluedSection:(BOOL)multivaluedSection
- {
- return [[self class] formSectionWithTitle:title sectionOptions:(multivaluedSection ? XLFormSectionOptionCanInsert | XLFormSectionOptionCanDelete : XLFormSectionOptionNone)];
- }
- +(instancetype)formSectionWithTitle:(NSString *)title sectionOptions:(XLFormSectionOptions)sectionOptions
- {
- return [[self class] formSectionWithTitle:title sectionOptions:sectionOptions sectionInsertMode:XLFormSectionInsertModeLastRow];
- }
- +(instancetype)formSectionWithTitle:(NSString *)title sectionOptions:(XLFormSectionOptions)sectionOptions sectionInsertMode:(XLFormSectionInsertMode)sectionInsertMode
- {
- return [[[self class] alloc] initWithTitle:title sectionOptions:sectionOptions sectionInsertMode:sectionInsertMode];
- }
- -(BOOL)isMultivaluedSection
- {
- return (self.sectionOptions != XLFormSectionOptionNone);
- }
- -(void)addFormRow:(XLFormRowDescriptor *)formRow
- {
- [self insertObject:formRow inAllRowsAtIndex:([self canInsertUsingButton] ? MAX(0, [self.formRows count] - 1) : [self.allRows count])];
- }
- -(void)addFormRow:(XLFormRowDescriptor *)formRow afterRow:(XLFormRowDescriptor *)afterRow
- {
- NSUInteger allRowIndex = [self.allRows indexOfObject:afterRow];
- if (allRowIndex != NSNotFound) {
- [self insertObject:formRow inAllRowsAtIndex:allRowIndex+1];
- }
- else { //case when afterRow does not exist. Just insert at the end.
- [self addFormRow:formRow];
- return;
- }
- }
- -(void)addFormRow:(XLFormRowDescriptor *)formRow beforeRow:(XLFormRowDescriptor *)beforeRow
- {
-
- NSUInteger allRowIndex = [self.allRows indexOfObject:beforeRow];
- if (allRowIndex != NSNotFound) {
- [self insertObject:formRow inAllRowsAtIndex:allRowIndex];
- }
- else { //case when afterRow does not exist. Just insert at the end.
- [self addFormRow:formRow];
- return;
- }
- }
- -(void)removeFormRowAtIndex:(NSUInteger)index
- {
- if (self.formRows.count > index){
- XLFormRowDescriptor *formRow = [self.formRows objectAtIndex:index];
- NSUInteger allRowIndex = [self.allRows indexOfObject:formRow];
- [self removeObjectFromFormRowsAtIndex:index];
- [self removeObjectFromAllRowsAtIndex:allRowIndex];
- }
- }
- -(void)removeFormRow:(XLFormRowDescriptor *)formRow
- {
- NSUInteger index = NSNotFound;
- if ((index = [self.formRows indexOfObject:formRow]) != NSNotFound){
- [self removeFormRowAtIndex:index];
- }
- else if ((index = [self.allRows indexOfObject:formRow]) != NSNotFound){
- if (self.allRows.count > index){
- [self removeObjectFromAllRowsAtIndex:index];
- }
- };
- }
- - (void)moveRowAtIndexPath:(NSIndexPath *)sourceIndex toIndexPath:(NSIndexPath *)destinationIndex
- {
- if ((sourceIndex.row < self.formRows.count) && (destinationIndex.row < self.formRows.count) && (sourceIndex.row != destinationIndex.row)){
- XLFormRowDescriptor * row = [self objectInFormRowsAtIndex:sourceIndex.row];
- XLFormRowDescriptor * destRow = [self objectInFormRowsAtIndex:destinationIndex.row];
- [self.formRows removeObjectAtIndex:sourceIndex.row];
- [self.formRows insertObject:row atIndex:destinationIndex.row];
-
- [self.allRows removeObjectAtIndex:[self.allRows indexOfObject:row]];
- [self.allRows insertObject:row atIndex:[self.allRows indexOfObject:destRow]];
- }
- }
- -(void)dealloc
- {
- [self.formDescriptor removeObserversOfObject:self predicateType:XLPredicateTypeHidden];
- @try {
- [self removeObserver:self forKeyPath:@"formRows"];
- }
- @catch (NSException * __unused exception) {}
- }
- #pragma mark - Show/hide rows
- -(void)showFormRow:(XLFormRowDescriptor*)formRow{
-
- NSUInteger formIndex = [self.formRows indexOfObject:formRow];
- if (formIndex != NSNotFound) {
- return;
- }
- NSUInteger index = [self.allRows indexOfObject:formRow];
- if (index != NSNotFound){
- while (formIndex == NSNotFound && index > 0) {
- XLFormRowDescriptor* previous = [self.allRows objectAtIndex:--index];
- formIndex = [self.formRows indexOfObject:previous];
- }
- if (formIndex == NSNotFound){ // index == 0 => insert at the beginning
- [self insertObject:formRow inFormRowsAtIndex:0];
- }
- else {
- [self insertObject:formRow inFormRowsAtIndex:formIndex+1];
- }
-
- }
- }
- -(void)hideFormRow:(XLFormRowDescriptor*)formRow{
- NSUInteger index = [self.formRows indexOfObject:formRow];
- if (index != NSNotFound){
- [self removeObjectFromFormRowsAtIndex:index];
- }
- }
- #pragma mark - KVO
- -(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
- {
- if (!self.formDescriptor.delegate) return;
- if ([keyPath isEqualToString:@"formRows"]){
- if ([self.formDescriptor.formSections containsObject:self]){
- if ([[change objectForKey:NSKeyValueChangeKindKey] isEqualToNumber:@(NSKeyValueChangeInsertion)]){
- NSIndexSet * indexSet = [change objectForKey:NSKeyValueChangeIndexesKey];
- XLFormRowDescriptor * formRow = [((XLFormSectionDescriptor *)object).formRows objectAtIndex:indexSet.firstIndex];
- NSUInteger sectionIndex = [self.formDescriptor.formSections indexOfObject:object];
- [self.formDescriptor.delegate formRowHasBeenAdded:formRow atIndexPath:[NSIndexPath indexPathForRow:indexSet.firstIndex inSection:sectionIndex]];
- }
- else if ([[change objectForKey:NSKeyValueChangeKindKey] isEqualToNumber:@(NSKeyValueChangeRemoval)]){
- NSIndexSet * indexSet = [change objectForKey:NSKeyValueChangeIndexesKey];
- XLFormRowDescriptor * removedRow = [[change objectForKey:NSKeyValueChangeOldKey] objectAtIndex:0];
- NSUInteger sectionIndex = [self.formDescriptor.formSections indexOfObject:object];
- [self.formDescriptor.delegate formRowHasBeenRemoved:removedRow atIndexPath:[NSIndexPath indexPathForRow:indexSet.firstIndex inSection:sectionIndex]];
- }
- }
- }
- }
- #pragma mark - KVC
- -(NSUInteger)countOfFormRows
- {
- return self.formRows.count;
- }
- - (id)objectInFormRowsAtIndex:(NSUInteger)index
- {
- return [self.formRows objectAtIndex:index];
- }
- - (NSArray *)formRowsAtIndexes:(NSIndexSet *)indexes
- {
- return [self.formRows objectsAtIndexes:indexes];
- }
- - (void)insertObject:(XLFormRowDescriptor *)formRow inFormRowsAtIndex:(NSUInteger)index
- {
- formRow.sectionDescriptor = self;
- [self.formRows insertObject:formRow atIndex:index];
- }
- - (void)removeObjectFromFormRowsAtIndex:(NSUInteger)index
- {
- [self.formRows removeObjectAtIndex:index];
- }
- #pragma mark - KVC ALL
- -(NSUInteger)countOfAllRows
- {
- return self.allRows.count;
- }
- - (id)objectInAllRowsAtIndex:(NSUInteger)index
- {
- return [self.allRows objectAtIndex:index];
- }
- - (NSArray *)allRowsAtIndexes:(NSIndexSet *)indexes
- {
- return [self.allRows objectsAtIndexes:indexes];
- }
- - (void)insertObject:(XLFormRowDescriptor *)row inAllRowsAtIndex:(NSUInteger)index
- {
- row.sectionDescriptor = self;
- [self.formDescriptor addRowToTagCollection:row];
- [self.allRows insertObject:row atIndex:index];
- row.disabled = row.disabled;
- row.hidden = row.hidden;
- }
- - (void)removeObjectFromAllRowsAtIndex:(NSUInteger)index
- {
- XLFormRowDescriptor * row = [self.allRows objectAtIndex:index];
- [self.formDescriptor removeRowFromTagCollection:row];
- [self.formDescriptor removeObserversOfObject:row predicateType:XLPredicateTypeDisabled];
- [self.formDescriptor removeObserversOfObject:row predicateType:XLPredicateTypeHidden];
- [self.allRows removeObjectAtIndex:index];
- }
- #pragma mark - Helpers
- -(BOOL)canInsertUsingButton
- {
- return (self.sectionInsertMode == XLFormSectionInsertModeButton && self.sectionOptions & XLFormSectionOptionCanInsert);
- }
- #pragma mark - Predicates
- -(NSNumber *)hidePredicateCache
- {
- return _hidePredicateCache;
- }
- -(void)setHidePredicateCache:(NSNumber *)hidePredicateCache
- {
- NSParameterAssert(hidePredicateCache);
- self.isDirtyHidePredicateCache = NO;
- if (!_hidePredicateCache || ![_hidePredicateCache isEqualToNumber:hidePredicateCache]){
- _hidePredicateCache = hidePredicateCache;
- }
- }
- -(BOOL)isHidden
- {
- if (self.isDirtyHidePredicateCache) {
- return [self evaluateIsHidden];
- }
- return [self.hidePredicateCache boolValue];
- }
- -(BOOL)evaluateIsHidden
- {
- if ([_hidden isKindOfClass:[NSPredicate class]]) {
- if (!self.formDescriptor) {
- self.isDirtyHidePredicateCache = YES;
- } else {
- @try {
- self.hidePredicateCache = @([_hidden evaluateWithObject:self substitutionVariables:self.formDescriptor.allRowsByTag ?: @{}]);
- }
- @catch (NSException *exception) {
- // predicate syntax error.
- self.isDirtyHidePredicateCache = YES;
- };
- }
- }
- else{
- self.hidePredicateCache = _hidden;
- }
- if ([self.hidePredicateCache boolValue]){
- if ([self.formDescriptor.delegate isKindOfClass:[XLFormViewController class]]){
- XLFormBaseCell* firtResponder = (XLFormBaseCell*) [((XLFormViewController*)self.formDescriptor.delegate).tableView findFirstResponder];
- if ([firtResponder isKindOfClass:[XLFormBaseCell class]] && firtResponder.rowDescriptor.sectionDescriptor == self){
- [firtResponder resignFirstResponder];
- }
- }
- [self.formDescriptor hideFormSection:self];
- }
- else{
- [self.formDescriptor showFormSection:self];
- }
- return [self.hidePredicateCache boolValue];
- }
- -(id)hidden
- {
- return _hidden;
- }
- -(void)setHidden:(id)hidden
- {
- if ([_hidden isKindOfClass:[NSPredicate class]]){
- [self.formDescriptor removeObserversOfObject:self predicateType:XLPredicateTypeHidden];
- }
- _hidden = [hidden isKindOfClass:[NSString class]] ? [hidden formPredicate] : hidden;
- if ([_hidden isKindOfClass:[NSPredicate class]]){
- [self.formDescriptor addObserversOfObject:self predicateType:XLPredicateTypeHidden];
- }
- [self evaluateIsHidden]; // check and update if this row should be hidden.
- }
- @end
|