Dynamic Fields?

30 views
Skip to first unread message

Joseph F

unread,
Jul 24, 2013, 6:24:11 PM7/24/13
to ibaf...@googlegroups.com
Has anyone made a branch that permits the dynamic adding/removing of fields? For example, if I flip a bool switch, a section may add or remove fields (preferably with some kind of animation like in Contacts).

Thomas Elstner

unread,
Aug 8, 2013, 4:19:27 PM8/8/13
to ibaf...@googlegroups.com
Hi Joseph,

here's a small tutorial on how to implement this:

1. Add a property hidden to IBAFormField und IBAFormSection:

IBAFormField.h & IBAFormSection.h

@interface IBAFormField : NSObject {
...
    BOOL hidden_;
}
...
@property (nonatomic, assign) BOOL hidden;

IBAFormField.m & IBAFormSection.m

@synthesize hidden = hidden_;

2. modify section & field management in IBAFormDataSource to use the new FormSection.hidden & FormField.hidden property

IBAFormDataSource.h: add the following declaration to IBAFormDataSource:
- (NSIndexPath *)absoluteIndexPathForFormField:(IBAFormField *)formField;
- (NSInteger)absoluteSectionForFormSection:(IBAFormSection *)section;
 
IBAFormDataSource.m: add or replace the following methods

- (NSInteger)sectionCount {
    NSInteger sectionCount=0;
    for (IBAFormSection *section in self.sections) {
        if (!section.hidden) sectionCount++;
    }
    return sectionCount;
}

- (NSInteger)numberOfFormFieldsInSection:(NSInteger)visibleSectionLocation {
    NSInteger sectionLocation = -1;
    IBAFormSection *section = nil;
   
    for (NSUInteger i = 0; i < self.sections.count; i++) {
        section = [self.sections objectAtIndex:i];
        if (!section.hidden) {
            sectionLocation++;
            if (sectionLocation == visibleSectionLocation) {
                break;
            }
        }
    }
   
    if (section) {
        NSInteger fieldCount = 0;
        for (IBAFormField *field in section.formFields) {
            if (!field.hidden) fieldCount++;
        }
        return fieldCount;
    } else {
        return 0;
    }
}

- (IBAFormField *)formFieldAtIndexPath:(NSIndexPath *)indexPath {
    NSInteger sectionLocation = -1;
    IBAFormSection *section = nil;
    for (NSUInteger i = 0; i < self.sections.count; i++) {
        section = [self.sections objectAtIndex:i];
        if (!section.hidden) {
            sectionLocation++;
            if (sectionLocation == indexPath.section) {
                break;
            }
        }
    }

    if (section) {
        NSInteger count = 0;
        for (NSUInteger i = 0; i < section.formFields.count; i++) {
            IBAFormField *field = [section.formFields objectAtIndex:i];
            if (!field.hidden) {
                if (count == indexPath.row) {
                    return field;
                }
                count++;
            }
        }
    }
    return nil;
}

- (NSIndexPath *)indexPathForFormField:(IBAFormField *)formField {
    NSIndexPath *indexPath = nil;
    NSUInteger visibleSectionLocation = -1;
    NSUInteger sectionCount = [self sectionCount];
    for (NSUInteger sectionIndex = 0; sectionIndex < sectionCount; sectionIndex++) {
        IBAFormSection *section = [self.sections objectAtIndex:sectionIndex];
        if (!section.hidden) {
            visibleSectionLocation++;
            NSUInteger fieldLocation = [section.formFields indexOfObject:formField];
            if (fieldLocation != NSNotFound) {
                NSInteger visibleFieldLocation = -1;
                for (NSUInteger i = 0; i <= fieldLocation; i++) {
                    IBAFormField *field = [section.formFields objectAtIndex:i];
                    if (!field.hidden) {
                        visibleFieldLocation++;
                    }
                }
                if (visibleFieldLocation >= 0) {
                    indexPath = [NSIndexPath indexPathForRow:visibleFieldLocation inSection:visibleSectionLocation];
                }
            }
        }
    }
   
    return indexPath;
}

- (NSIndexPath *)absoluteIndexPathForFormField:(IBAFormField *)formField {
    NSIndexPath *indexPath = nil;
   
    NSUInteger sectionCount = [self sectionCount];
    for (NSUInteger sectionIndex = 0; sectionIndex < sectionCount; sectionIndex++) {
        IBAFormSection *section = [self.sections objectAtIndex:sectionIndex];
        NSUInteger fieldLocation = [section.formFields indexOfObject:formField];
        if (fieldLocation != NSNotFound) {
            indexPath = [NSIndexPath indexPathForRow:fieldLocation inSection:sectionIndex];
        }
    }
   
    return indexPath;
}

- (NSInteger)sectionForFormSection:(IBAFormSection *)section {
    NSUInteger sectionLocation = -1;
    for (NSUInteger i = 0; i < self.sections.count; i++) {
        IBAFormSection *currentSection = [self.sections objectAtIndex:i];
        if (!section.hidden) {
            sectionLocation++;
            if ([currentSection isEqual:section]) {
                return  sectionLocation;
                break;
            }
        }
    }
    return NSNotFound;
}

- (NSInteger)absoluteSectionForFormSection:(IBAFormSection *)section {
    return [self.sections indexOfObject:section];
}


// This method returns the next logical form field after the one provided. That may be the first field in the next
// section if the given field is the last in its section.
- (IBAFormField *)formFieldAfter:(IBAFormField *)field {
    IBAFormField *nextField = nil;
   
    NSIndexPath *visibleIndexPath = [self indexPathForFormField:field];
    if (visibleIndexPath) {
        if (visibleIndexPath.row + 1 == [self numberOfFormFieldsInSection:visibleIndexPath.section]) { // last formField, not in the last section => first formField in next section
            if (visibleIndexPath.section + 1 < [self sectionCount]) {
                nextField = [self formFieldAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:visibleIndexPath.section + 1]];
            }
        } else {
            nextField = [self formFieldAtIndexPath:[NSIndexPath indexPathForRow:visibleIndexPath.row + 1 inSection:visibleIndexPath.section]];
        }
   
    }

    return nextField;
}

- (IBAFormField *)formFieldBefore:(IBAFormField *)field {
    IBAFormField *previousField = nil;
   
    NSIndexPath *visibleIndexPath = [self indexPathForFormField:field];
    if (visibleIndexPath) {
        if (visibleIndexPath.row == 0) { // first formField, not in the first section => last formField in previous section
            if (visibleIndexPath.section > 0) {
                previousField = [self formFieldAtIndexPath:[NSIndexPath indexPathForRow:[self numberOfFormFieldsInSection:visibleIndexPath.section - 1] - 1 inSection:visibleIndexPath.section - 1]];
            }
        } else {
            previousField = [self formFieldAtIndexPath:[NSIndexPath indexPathForRow:visibleIndexPath.row - 1 inSection:visibleIndexPath.section]];
        }
       
    }

    return previousField;
}

- (UIView *)viewForHeaderInSection:(NSInteger)section {
    return [[self.sections objectAtIndex:section] headerView];
}

- (UIView *)viewForFooterInSection:(NSInteger)section {
    return [[self.sections objectAtIndex:section] footerView];
}


#pragma mark -
#pragma mark UITableViewDataSource

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    return [self sectionCount];
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return [self numberOfFormFieldsInSection:section];
}

- (UITableViewCell *)tableView:(UITableView *)theTableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    return [self cellForFormFieldAtIndexPath:(NSIndexPath *)indexPath];
}

- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)visibleSectionLocation {
    NSInteger sectionLocation = -1;
    IBAFormSection *section = nil;
   
    for (NSUInteger i = 0; i < self.sections.count; i++) {
        section = [self.sections objectAtIndex:i];
        if (!section.hidden) {
            sectionLocation++;
            if (sectionLocation == visibleSectionLocation) {
                return [section headerTitle];
                break;
            }
        }
    }
    return nil;
}

- (NSString *)tableView:(UITableView *)tableView titleForFooterInSection:(NSInteger)visibleSectionLocation {
    NSInteger sectionLocation = -1;
    IBAFormSection *section = nil;
   
    for (NSUInteger i = 0; i < self.sections.count; i++) {
        section = [self.sections objectAtIndex:i];
        if (!section.hidden) {
            sectionLocation++;
            if (sectionLocation == visibleSectionLocation) {
                return [section footerTitle];
                break;
            }
        }
    }
    return nil;
}


3. How to use it?

In your FormModelManager you can do things like this

- (void)setModelValue:(id)value forKeyPath:(NSString *)keyPath {
    [super setModelValue:value forKeyPath:keyPath];
    if ([keyPath isEqualToString:@"booleanSwitchValue"]) {
        BOOL switchValue = [(NSNumber*)value boolValue];
        NSIndexPath *indexPath = [self absoluteIndexPathForFormField:self.passwordFormField];
        self.passwordFormField.hidden = !
self.passwordFormField.hidden;
        if (self.passwordFormField.hidden) { // hide it
            [self.formViewController.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
        } else { // show it
            [self.formViewController.tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
        }
    } else if ([keyPath isEqualToString:@"booleanCheckValue"]) {
        BOOL checkValue = [(NSNumber*)value boolValue];
        NSInteger sectionLocation = [self absoluteSectionForFormSection:self.cameraSection];
        self.cameraSection.hidden = !
self.cameraSection.hidden;
        if (self.cameraSection.hidden) { // hide it
            [self.formViewController.tableView deleteSections:[NSIndexSet indexSetWithIndex:sectionLocation] withRowAnimation:UITableViewRowAnimationAutomatic];
        } else { // show it
            [self.formViewController.tableView insertSections:[NSIndexSet indexSetWithIndex:sectionLocation] withRowAnimation:UITableViewRowAnimationAutomatic];
        }
    }
}


So basically it is up to you to keep the datasource's FormSection.hidden and FormField.hidden in sync with the presentation state of the tableview - otherwise you'll get the wellknown NSInternalInconsistencyException.

Best regards,
Thomas
Reply all
Reply to author
Forward
0 new messages