Teaching Announcements
The Welcome View
The Daily Table View
The Module Detail View
The (Modal) Customise View
Note: This has been updated on 11th Nov 2012 to modify requirements and include extra hints.
The aim of the second assignment is to demonstrate an understanding of TableViews (and the drawing of custom cells), Delegates, and the ability to manipulate data structures consisting of both mutable and non-mutable combinations of NSDictionary and NSArray objects, and apply it by creating an iOS iPhone app. The app should be a Timetable application using the iOS 6.0 API. Examples of the final version are given below.
Your solution should utilise a variety of different ViewControllers and TableViewControllers to manage the different views, and only use associated .xib files where absolutely necessary (for example, the welcome view, the module detail view, and the custom cells). It should also demonstrate your understanding of many of the issues raised in the recent labs, including the use of JSON data, the lazy instantiation pattern, using mapKit and map annotations, as well as a working knowledge of tables, navigation controllers and modal views.
Three JSON data sources should be used as part of your solution:
These resources provide information on the timetable, and basic module details, and lecture theatre locations respectively. In addition, the following images may be of use:
•Lecture Image (137-presentation.png)
•Lab Image (174-imac.png)
•Tutorial Image (96-book.png)
•Welcome Background Image (WelcomeBackground.png)
The last image can be (optionally) used as a background to the welcome view. Three of these images have been taken from the royalty free Glyphish set of mobile app icons, which is a good repository for button and tab-bar icons. If you prefer to use your own images, then you can do so.
Detailed Requirements
The full requirements are given (below):
1.The application should be created with the following characteristics:
a.Each class you create should be prefixed with your username.
b.Use ARC to manage memory.
c.DO NOT use storyboards.
d.DO NOT include Unit Tests.
e.Start with an empty application project.
f.Set the target device as iphone.
g.Name your project TimeTable2012
h.Select only portrait orientations for your project.
i.All xib screens should be designed for an iPhone Retina 3.5” full display.
2.The application should start by presenting a “Welcome View” within a UINavigationController, listing the days Monday-Friday. Each day corresponds to one of the arrays of elements within the TimeTable12.json file. These can be represented using either a UITableView or UIButtons. In addition, in “Information” button should appear, which will take the user to the “Customise” View.
3.When a day is selected, the events for each day should be presented in a UITableViewController managed view (i.e. the “Daily Table View”), which is pushed onto the UINavigationController stack. The events should be formatted to present the module code, module title, module location, and an image to represent whether the event is a lecture, lab or tutorial. Note that this will involve exploiting data from both the TimeTable12.json and Semester1Modules.json files.
4.If any of the modules within the “Daily Table View” are selected, then basic information on that module should then be presented by pushing an additional view (the “Module Detail View”) onto the UINavigationView stack, which presents the basic information on the respective module found in the Semester1Modules.json file, and a map (which can either display a satellite, hybrid or normal map - your choice) illustrating a pin denoting the location of the relevant lecture theatre. You can choose (if you wish) to show the user’s current position, but the map should initially be centered around the pin location.
5.It should be possible to determine which modules should appear in the “Daily Table View”. Create a “Customise View” modally, by combining a UITableViewController and a separate UINavigationController, to list the modules found in the Semester1Modules.json file. Present these with the UITableViewCellAccessoryCheckmark to represent the case where a module is selected, and the absence of a check mark should represent where a module is not selected. Ensure that when a table entry is selected, this selection is toggled (i.e. if not selected, it will become selected, and vice versa). Note that this view should appear modally, and be dismissed by selecting the Done button.
6.Ensure that only those modules selected by the “Customise View” appear in the data structures used to present the “Daily Table View”.
The work should be based on the material presented in the KingsQueens Navigation Lab (Part A) and the WhereAmI Lab work (Labs 6 and 7). Additional hints will be given in the lecture material.
You can use any version of Xcode 4.4 (including the recently released Xcode 4.5) running under any version of iOS 6.
Marking Scheme
The purpose of this assignment is to demonstrate that you know how to develop native iPhone app using TableViews (and the drawing of custom cells), Modal Views, MapKit, and the ability to manipulate data structures consisting of both mutable and non-mutable combinations of NSDictionary and NSArray. You do not need to hand in any design documentation but your code MUST be well commented so that it explains what is happening in the code. The following marking scheme will be used to assess each submission:
•Navigation and Table Views: (40 marks total)
•Demonstrate an understanding of using a UINavigationView and TableViewControllers, by allowing the user to navigate a day’s events (using a UITableViewController) and find out more details about a selected module (using a UIViewController and associated NIB file), as well as using navigator-generated back buttons. (20 marks)
•Create and utilise custom cells for each of the entries in the “Daily Table View” (10 marks)
•Use Mapkit and Map annotations to illustrate the location of the lecture theatre. (10 marks)
•Modal View: (15 marks total)
•Create a UITableViewController to present the modules, and provide functionality to allow users to select or deselect modules for later presentation. (10 marks)
•Present and manage the view modally. (5 marks)
•Data Manipulation: (20 marks total)
•Manage the loading of Module data from JSON, and creating structures to support the selection and deselection of modules in the “Customise Modal View”. (10 marks)
•Manipulate a new Timetable data structure to only include modules selected from the “Customise Modal View”, prior to using this new data to drive the presentation of the “Daily Table View” (10 marks)
•General: (25 marks total)
•Adhering to all of the requirements and submission guidelines, and including all resources and sources in your submission (10 marks)
•Demonstrate an understanding and use of lazy instantiation (5 marks)
•Layout and Design (10 marks)
This assignment contributes 15% to your overall mark for COMP327.
The code should compile without warning or errors.
SUBMISSION INSTRUCTIONS
Firstly, check that you have adhered to the following list:
1.Your project should be self contained within a single zip file containing all of the files in the XCode project. The file's name MUST be 'TimeTable12.zip'. Ensure that all of your source files are included in the project (hint, unpack it in a different directory and check it still builds).
2.Your project is developed within XCode, not some other language or environment.
3.Your program compiles and runs on a machine within the computer science department’s Mac Lab, either under Xcode 4.5, using the iOS SDK 6 (recently released) or an earlier version. If you have developed your code elsewhere (e.g. your own mac), ensure that it also works on our system before submission. It is your responsibility to check that you can log onto the department’s system well in advance of the submission deadline.
4.Your program does not bear undue resemblance to anybody else's! Electronic checks for code similarity will be performed on all submissions and instances of plagiarism will be severely dealt with. The rules on plagiarism and collusion are explicit: do not copy anything from anyone else’s code, do not let anyone else copy from your code and do not hand in 'jointly developed' solutions.
To submit your solution you must SUBMIT IT ELECTRONICALLY, and adhere to the following instructions:
Electronic submission:
•Your code must be submitted to the departmental electronic submission system at: http://cgi.csc.liv.ac.uk/cgi-bin/submit.pl?module=comp327
•You need to login in to the above system and select ‘Assignment 2’ from the drop-down menu. You then locate the file containing your program that you wish to submit, check the box stating that you have read and understood the university’s policy on plagiarism and collusion, then click the ‘Upload File’ button.
Work will be accepted only if it is submitted electronically following the above instructions.
Finally, please remember that it is always better to hand in an incomplete piece of work, which will result in some marks being awarded, as opposed to handing in nothing, which will guarantee a mark of 0 being awarded. Demonstrators will be on hand during the COMP327 practical sessions to provide assistance, should you need it.
COMP327 - Practical Assignment 2 2012
09/11/2012
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.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.
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.
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:
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;
}
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;
}
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;
}
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];
}
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.