iOS Recipes - Matt Drance [78]
return array;
}
PrintSubviews/Classes/UIView+PRPSubviewTraversal.m
- (NSArray *)prp_subviewsMatchingOrInheritingClass:(Class)aClass {
NSMutableArray *array = [NSMutableArray array];
[self prp_populateSubviewsMatchingClass:aClass
inArray:array
exactMatch:NO];
return array;
}
These two methods are the ones that should be used by consumers.
Using these methods is now straightforward: when you pass [UIScrollView class]] to ‑subviewsMatchingClass:, you get an array of UIScrollView objects, while ‑prp_subviewsMatchingOrInheritingClass: returns an array of UIScrollView, UITableView, UITextView, and any other object whose class is a subclass of UIScrollView.
Let’s look at this in practice. The accompanying PrintSubviews project includes a busy hierarchy that for the sake of this demo was constructed in Interface Builder. Many of the elements are standard buttons, views, and labels, but we’ve put a few custom view and label subclasses in there as well. For something this deep, especially if it was constructed dynamically, it would be great to get a quick snapshot of its current layout to see where everything sits. Using the methods we’ve constructed in this category, we can even get information on a given type of view if we want.
PrintSubviews/Classes/PrintSubviewsViewController.m
- (IBAction)printView:(id)sender {
Class labelClass = [UILabel class];
NSArray *uiLabels = [self.view
prp_subviewsMatchingClass:labelClass];
Class PRPLabelClass = [PRPLabel class];
NSArray *prpLabels = [self.view
prp_subviewsMatchingClass:PRPLabelClass];
NSArray *allLabels = [self.view
prp_subviewsMatchingOrInheritingClass:labelClass];
Class PRPCustomViewClass = [PRPCustomView class];
NSArray *customViews = [self.view
prp_subviewsMatchingClass:PRPCustomViewClass];
NSLog(@"%d UILabels", [uiLabels count]);
NSLog(@"%d PRPLabels", [prpLabels count]);
NSLog(@"%d UILabels", [allLabels count]);
NSLog(@"%d PRPCustomViews", [customViews count]);
[self.view prp_printSubviews];
}
Run the PrintSubviews and tap the Print View Hierarchy button to see the relevant output presented to the console. The resulting output demonstrates a number of interesting details. We see, for example, that there are nine UILabel objects in this view, but only two of them are actually instances of UILabel; the other seven are a combination of four PRPLabel objects, plus instances of another unknown subclass (presumably from the table cells you see on the screen).
2011-04-06 10:10:05.087 PrintSubviews[13538:207] 2 UILabels
2011-04-06 10:10:05.089 PrintSubviews[13538:207] 4 PRPLabels
2011-04-06 10:10:05.090 PrintSubviews[13538:207] 9 UILabels + subclasses
2011-04-06 10:10:05.091 PrintSubviews[13538:207] 3 PRPCustomViews
Using this category alongside the ‑recursiveDescription method allows you to study your hierarchies closely and find out where you may have some dead code or elements that we’ve forgotten to remove. Note that this is neither the same as nor a replacement for Instruments, which gives you a full report of all objects of a given type that have been allocated (or leaked) in the entire app. With the utilities in this recipe, you can drill down to a specific view hierarchy you’re interested in studying.
A final note: like ‑recursiveDescription, these methods are meant to be debugging tools only. Blindly traversing subviews and printing out large ASCII trees can get very expensive; you don’t want to perform any of these tasks in a shipping application. But for development and debugging, where we spend most of our time, they can be very valuable tools.
Recipe 39 Initialize a Basic Data Model
Problem
Every project you start inevitably involves copying and pasting administrative Core Data code from previous work or from Apple sample code templates. You need a solid, droppable starting point for every new project.
Solution
Core Data does a wonderful job of reducing the amount of database code we need to write. However, there is still a good deal of redundant work in every project that uses Core Data. We need to set up our persistent