This excellent series of online lectures is available through iTunes U.
You can download slides from the lectures and associated assignments.
This repository contains my solution to Assignment IV.
Can zoom and pan using gestures. What is really cool, IMHO, is my use of CGAffineTransforms to completely describe the coordinate system used for plotting, and how gestures modify it. There is no clutter of scale and translation instance variables, just a coordSys ivar containing a CGAffineTransform. Changes to the coordinate system are done by just composing transforms; there’s no awkward coordinate manipulation scattered throughout GUI code. I also developed a C function, lineWidthScale
, which calculates how the thickness of a line changes when scaling of the transform changes. Using it, I worked out a way to do the drawing for the plot completely within the coordinate system defined by the gestures. Without lineWidthScale
, whenever (albeit not yet implemented) scales in X and Y differed, the line width would vary depending on the steepness of the plot.
In retrospect, I think it would have been simpler to explicitly transform the draw coordinates for each draw operation, rather than to set the CTM (Current Transformation Matrix) on the context just once before drawing the entire path. Then there would have been no line width problem. Nonetheless, there is a certain elegance in making use of the CTM, and it was an interesting exercise!
Handles double- and triple-taps. A double-tap translates the graph so the origin is at the middle of the view. A triple-tap restores the default coordinate system, which also happens to have the origin at the middle. The effect is that when the double-taps, he sees the graph shift to the middle, and on the third tap, the scale returns to the default.
Can run on the iPad. Notice that very little of my code needs to know which platform it’s running in – less than that in Paul Hegarty’s demo. Method graphPressed
has to decide whether to push the graphViewController
or just redraw the graph in the view already showing, and method restoreFromUserDefaults:
has to redraw the graph if we’re on the iPad. That’s it! This is in keeping with my goal of doing as much initialization in Interface Builder as possible. I found myself a bit “off the map”, however, when it came to using Xcode 4 to pull it this together, as explained below.
Saves and restores state Nearly the entire state of the calculator is persisted using NSUserDefaults. Even if the user is in the process of entering a number or has not completed a calculation, the user is returned exactly to where he/she left off. This is hard to see using the simulator. Look instead at the unit tests in CalculatorTests.m.
One problem was figuring out the conventions for creating a Universal app from an iPhone app in Xcode 4. It was puzzling that I couldn’t even find a Split View Controller object in Interface Builder. But of course, that’s because this was an iPhone/iPod Touch XIB, not an iPad XIB. I discovered when I selected the Calculator root in the project view’s tree, selected the Calculator target, and selected iPhone/iPad for the Targeted Device Family, that lo and behold, Xcode offered to create the interface files for iPad. Easy, once you know what to do!
But editing the MainWindow-iPad.xib in Interface Builder presented another puzzle. The assignment called for using UINavigationControllers in both sides of the UISplitViewController, which is probably a good idea. Tutorials I found on the web confirmed my guess that I should just be able to drag a Navigation Controller object onto the second icon under the split view controller icon. The existing view controller icon should then just change into a navigation controller icon, as desired. But my drag operation just wouldn’t take. The navigation controller icon would just snap back to the objects list. I tried everything.
Finally, I resorted to editing MainWindow-iPad.xib in the Interface Builder 3 application! Dragging worked like a charm there, just like the tutorials showed. I saved the file and went back to Xcode 4, where MainWindow-iPad.xib looked good, showing navigation controller icons for both sides of the split view controller.
So far so good, but I couldn’t get the navigation bar button item to appear, the one that opens the popover calculator. I finally realized I couldn’t use the navigation controller’s backBarButtonItem
. The back button item only appears when a second view controller is pushed onto the navigation controller. The button item finally showed up when I used the leftBarButtonItem
property instead.
One big time-eater was a learning experience earned once again from not initializing things in code as in the lectures. (No, I’m not dissuaded from using Interface Builder to do as much initialization as possible.) When you set up a navigation controller in Interface Builder, you assign its top view controller. For the left one, I had to manually assign a title to its navigation bar – or rather, as I discovered, to it’s top view controller’s navigation item. In lecture 9, Paul Hegarty used method pushViewController:animated:
to push the single view controller into the left navigation controller. Evidently, this results in the navigation controller itself (since it is a view controller) acquiring the title. Instead, I had to drill down to the navigation controller’s topViewController.navigationItem.title
propert