2012/13

 

The Welcome View





The Daily Table View





The Module Detail View





The (Modal) Customise View

 
 

Deadline:

12 noon Friday 23rd November 2012

Example Output

Hints

Most of what you need has already been covered in the labs or lecture notes.  The following provides a few additional hints for this assignment.


  1. 1.Think of each of the views as requiring a separate pair (implementation and header) of files.  Ensure that whenever a view is created, properties are use to pass any data it requires.


  1. 2.Much of the work can be managed by using lazy instantiation, especially when loading json data.  Use this to load data when necessary, and also to build new data structures.


  1. 3.Most of the data processing should be done in the welcome view.  The data from the Semester1Modules.json file consists of a dictionary of modules, where each module also consists of a dictionary of details.  You will need to manage this data in various ways.  The following code fragments can be used in your project, but will help:


  1. a.The first loads (using lazy instantiation) the module data into a dictionary, replacing each inner module dictionary with a mutable dictionary. It also adds an additional key:value pair to determine whether or not a module is selected. As the dictionary is mutable, this can be changed (e.g. when the customise modal view controller is viewed). Note that as we are storing whether or not the module is selected, we want to use a bool data type.  However, as Bool’s are not Objective-C objects, we need to wrap them in NSNumber objects.


static NSString *moduleSelectedKey =  @"moduleSelected";


// Module focussed accessor methods

// ------------------------------------

- (NSDictionary *)moduleSourceDict {

    if (_moduleSourceDict == nil) {

        // Generate the URL request for the JSON data

        NSURL *url = [NSURL URLWithString:myJSONModuleSrc];

       

        // Get the contents of the URL

        NSData *data = [NSData dataWithContentsOfURL:url];

        NSError *error;

        // Note that we are not calling the setter method here... as this would be recursive!!!

        // Store the original data - we now need to expand this by creating a mutable dictionary where we can change

        // the values of a "selected" element

        NSDictionary *moduleDataModel = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&error];

        NSMutableDictionary *mutableModuleDict = [[NSMutableDictionary alloc] initWithCapacity:[moduleDataModel count]];

       

        // for each value in the loaded json module

        for (NSString *dictKey in moduleDataModel) {

            // copy the dictionary object for the current module into a mutable dictionary

            NSMutableDictionary *mutableElementDict = [NSMutableDictionary

                                                       dictionaryWithDictionary:[moduleDataModel objectForKey:dictKey]];

            [mutableElementDict setObject:[NSNumber numberWithBool:YES] forKey:moduleSelectedKey];

            [mutableModuleDict setObject:mutableElementDict forKey:dictKey];

        }

       

        // Convert our new dictionary into an immutable variant

        _moduleSourceDict = [NSDictionary dictionaryWithDictionary:mutableModuleDict];

        // ==========

    }

    return _moduleSourceDict;

}



  1. b.The second code fragment builds an NSArray from the moduleSourceDict to drive the Table View Data Source of the “Customise View”.  Although using a dictionary makes sense for most problems, as you will want to look up details of a module based on its code, it is easier to use Arrays when managing tables.  This method simply creates a new array full of the same objects in the moduleSourceDict. As the objects are the same objects, by modifying the objects in the array, the changes will be reflected in the original dictionary.


- (NSArray *)moduleSelectionArray {

    if (_moduleSelectionArray == nil) {

        // If we have no module selection array - load from JSON data

       

        NSMutableArray *newModuleArray = [NSMutableArray arrayWithCapacity:[[self moduleSourceDict] count]];

       

        // iterate through the module data source to create a new array of modules

        // ensuring that each element contains a reference of the mutable module dictionary (dict)

        NSMutableDictionary *dict;

        NSString *dictKey;

        for (dictKey in [self moduleSourceDict]) {

            dict = [[self moduleSourceDict] objectForKey:dictKey];

            [newModuleArray addObject:dict];

        }

        // Convert our new dictionary into an immutable variant

        _moduleSelectionArray = [NSArray arrayWithArray:newModuleArray];

   

    }

    return _moduleSelectionArray;

}


  1. c.For the “Daily Table View”, it is better to create a new data structure that only contains the events corresponding to selected modules within the “Welcome View”, and pass this to the TableViewController, rather than trying to filter modules within the TableViewController itself. The final fragment is responsible for creating a new array of events for a single day, based on one of the arrays of events for a single day taken from the JSON source, and a modified module dictionary (i.e. with selection details).  This is called to filter the daily events prior to passing to the Daily Timetable view.


static NSString *timeKey = @"time";

static NSString *moduleSelectedKey =  @"moduleSelected";

static NSString *moduleKey = @"module";

static NSString *eventsKey = @"events";


- (NSArray *)filteredDailyTimetableArray:(NSArray *)dailyTimeTableArray forModuleDict:(NSDictionary *)moduleDict {

    // Create the new data structure given the selected modules.  If the day is clear, then

    // report that.  Otherwise, use the newly constructed model.

    // Each day consists of a time element, which is a section, and then the events.

   

    // =======================================================

    // Declare all of the new mutable structures

    NSMutableArray *newDailyTimeTableArray = nil;       // The top level array, replacing data model

    NSDictionary *newHourlyGroupDict = nil;             // The hourly group dictionary, replacing hourlyGroupDict

    NSMutableArray *newEventsArray = nil;               // Replacement for eventsArray, with selected events

   

    // =======================================================

    // Declare variables to iterate through the old structures

    NSDictionary *hourlyGroupDict;              // The dictionary for each hourly group (1st level item)

    NSArray *eventsArray;                       // Array of events for each hour (2nd level item

    NSDictionary *eventDict;                    // The dictionary for each event (3rd level item)

   

    // =======================================================

    // Declare temporary (other) variables

    NSString *codeStr;

   


    // For each Hour Element

    // NSLog(@"dailyTimeTableArray: %@", dailyTimeTableArray);

   

    for (hourlyGroupDict in dailyTimeTableArray) {

        eventsArray = [hourlyGroupDict objectForKey:eventsKey];

        newEventsArray = nil;

        //NSLog(@"eventsArray: %@", eventsArray);

       

        for (eventDict in eventsArray) {

            codeStr = [eventDict objectForKey:moduleKey];

            NSDictionary *moduleDetail = [moduleDict objectForKey:codeStr];

            //NSLog(@"eventDict: %@", eventDict);

           

            if ([[moduleDetail objectForKey:moduleSelectedKey] boolValue] == YES) {

               

                // NSLog(@"Including %@", [eventDict objectForKey:MODULE]);

                // Need to check to see if we have an entry for the time in the new data structure

                // otherwise, we need to create one.

                if (newEventsArray == nil) {

                    newEventsArray = [NSMutableArray array];

                }

                [newEventsArray addObject:eventDict];

            }

        }

        // At this point we have our completed array.  If not empty, then create our group dict

        if (newEventsArray != nil) {

            //NSLog(@"newEventsArray: %@", newEventsArray);           

            newHourlyGroupDict = @{timeKey:[hourlyGroupDict objectForKey:timeKey],eventsKey:newEventsArray};

            //NSLog(@"newHourlyGroupDict: %@", newHourlyGroupDict);

           

            // Add this to the top level array

            if (newDailyTimeTableArray == nil)

                newDailyTimeTableArray = [NSMutableArray array];

            [newDailyTimeTableArray addObject:newHourlyGroupDict];

        }

    }

   

    // NSLog(@"newDailyTimeTableArray: %@", newDailyTimeTableArray);

   

    return newDailyTimeTableArray;

}


  1. d.For the “Customise View”, you will need to modify both the contents of a cell (i.e. whether or not its disclosure indicator check mark appears or not), and the data source itself.  It is probably easier to just update the data in the xxx method, and get the method to force the table to reload just that cell.  The following code fragment illustrates how to do this:


- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath

{

    NSMutableDictionary *dict = [[self selectedModuleArray] objectAtIndex:[indexPath row]];


    // Update the data array

    if ([[dict objectForKey:moduleSelectedKey] boolValue] == YES) {

        [dict setObject:[NSNumber numberWithBool:NO] forKey:moduleSelectedKey];

    } else {

        [dict setObject:[NSNumber numberWithBool:YES] forKey:moduleSelectedKey];

    }

   

    // Update the table view

    [tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone];

}


  1. 5.When manipulating a new data structure containing only selected module entries, make good use of NSLog calls to check the elements you are manipulating.  Also, exploit the class factory methods for both dictionaries and arrays.