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