Maxime Capelle
Maxime Capelle

Reputation: 927

Pattern to valid a dynamic form using a UITableView

I have got a dynamic form similar to contact form on iphone and ipad. A contact has "static" informations like last name, first name, address, ... And he also has "dynamic" informations like friends, Hobbies, whatever ...

So I created an UITableView which is composed of difference types of cell like StaticInformationCell, FriendCell, HobbyCell. Each type of cells has one or several UItextfield regarding the informations it needs.

It works pretty well for self cell validation. I mean when the user edits the UITextField of one particular cell, this one can valid or not the value and display or not the overview in the UITextField.

My problem is that the UITableView doesn't know if the entire form (all the cells) is valid when the user submits the form. I don't know how the UITableView can "ask" each cell if it is valid or not.

I tried to store in a NSMutableSet the cells when they were created and send a message isValid to each cell. Unfortunetly I don't know why some cells are "duplicated in this" Set :

<FriendEditCell: 0x8ddf9e0; baseClass = UITableViewCell; frame = (0 383; 768 70); alpha = 0; hidden = YES; autoresize = W; layer = <CALayer: 0x8db4010>>,
<FriendEditCell: 0x8dcfd80; baseClass = UITableViewCell; frame = (0 383; 768 70); autoresize = W; layer = <CALayer: 0x8d90920>>

I can't use a static UITableView because an user can add as many friends as he wants.

Do you have an idea how I can fix my validation problem ?

Upvotes: 1

Views: 1412

Answers (3)

Maxime Capelle
Maxime Capelle

Reputation: 927

Finally I refactor the way I validate my form.

I modified my model to include the logic validation. For example in the class Contact I have these methods :

- (NSString*)isFirstnameValid; // check length or unicity for example and return what's wrong in the current value
- (NSString*)isLastnameValid;
- (BOOL)isContactValid;

The two first methods are useful for live validation by the custom cells. (Add an overlay if needed) The last one uses the two methods above and the same methods in each Friend and Hobby to know if all the contact's values are valid.

When the user submits the form I simply check [self.contact isContactValid]before saving the data.

I like this way to do because validation logic is in the classes and can be reused in an other form for example.

Upvotes: 0

Bilal Saifudeen
Bilal Saifudeen

Reputation: 1677

You probably got cells repeated because of the UITableView Re-Use, i mean you may be allocating the cells again in cellForRowAtIndexPath: which is not what you need.

To be understood, UITableView calls cellForRowAtIndexPath: repeatedly while it scrolls for the same indexPath. Also, it implements Cell Re-Usability using identifiers, which is very good for minimum memory usage and smoothness while a huge set of data to be displayed.

But in a scenario like yours, I'll implement the following design pattern.

Write SubClasses of UITableViewCell for each kind of cell like FriendCell, HobbyCell etc.

Implement isContentValid method in every class with appropriate implementation.

@interface StaticInformationCell : UITableViewCell

@end
@interface FriendCell : UITableViewCell

- (id)initWithFriend:(id)friend;

- (BOOL)isContentValid;

@end

@interface HobbyCell : UITableViewCell

- (id)initWithHobby:(id)hobby;

- (BOOL)isContentValid;

@end

Allocate needed cells as per your data models in viewDidLoad and keep in an Array or Array of arrays(in case of sectioned tableView)

@interface MyTableViewController ()
{
    NSMutableArray *cells;
}
@end


- (void)viewDidLoad
{
    [super viewDidLoad];

    cells = [NSMutableArray array];

    //Section0
    StaticInformationCell * aStaticInformationCell = [[StaticInformationCell alloc] init];
    [cells addObject: @[aStaticInformationCell]];


    //Section1
    NSMutableArray *friendCells = [NSMutableArray array];

    for(Friend *friend in user.friends){

        FriendCell *friendCell = [[FriendCell alloc] initWithFriend:friend];
        //Set needed properties
        [friendCells addObject:friendCell];
    }
    [cells addObject:friendCells];

    //Section2
    NSMutableArray *hobbyCells = [NSMutableArray array];

    for(Hobby *hobby in user.hobbies){


        HobbyCell *hobbyCell = [[HobbyCell alloc] initWithHobby:hobby];
        //Set needed properties
        [hobbyCells addObject:hobbyCell];

    }
    [cells addObject:hobbyCells];
}

Provide cells for the UITableView directly from the cells array

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
    // Return the number of sections.
    return [cells count];
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    // Return the number of rows in the section.
    return [[cells objectAtIndex:section] count];
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    //Return the cell directly from array
    return [[cells objectAtIndex:indexPath.section] objectAtIndex:indexPath.row];
}

Check overall validity

- (BOOL)allContentsValid{
    //You should iterate through each cell and check overall validity
    BOOL valid = TRUE;
    for(id cell in cells){

        if(![cell isContentValid]){
            valid = FALSE;
            break;
        }
    }
    return valid;
}

You can dynamically insert new cells, for example to add more friends

- (void)addNewFriend{
    Friend *newFriend = [[Friend alloc] init];
    //[newFriend settheProperties:];

    FriendCell *friendCell = [[FriendCell alloc] initWithFriend:newFriend];
    //Set needed properties

    [[cells objectAtIndex:1] addObject:friendCell];
    NSIndexPath *indexPath = [NSIndexPath indexPathForRow:([[cells objectAtIndex:1] count] - 1) inSection:1];
    [[self.tableView insertRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic]];

}

Upvotes: 1

Yoav Schwartz
Yoav Schwartz

Reputation: 2027

If I understand the question correctly, and Im not entirely sure I do, I would subclass UITableViewCell, and make the custom cells of that type. for that cell I would create a "validate" method that checks the content of the uitextfield depending on what you call a "valid" cell. this function would return TRUE if the cell is valid. When submitting the form I would loop over all the cells and set some kind of flag to fall if one of the "validates" returns a false and stop the submit.

Upvotes: 1

Related Questions