iOS Recipes - Matt Drance [77]
}
The parameter-less ‑prp_printSubviews method is included as a convenience. If we pass a custom indentString to our initial call, it appears at the beginning of every line of output.
PrintSubviews/Classes/UIView+PRPSubviewTraversal.m
- (void)prp_printSubviews {
[self prp_printSubviewsWithIndentString:nil];
}
Remember that UIWindow is a subclass of UIView, so sending [view.window prp_printSubviews] will print the full hierarchy of a view’s enclosing window.
Review the following sample output. The output from this method instantly shows how deep and broad our current hierarchy is. This information can help us manage layout complexity, understand the path of touch events through our UI, and more.
It is worth noting that views at a certain level are printed in z-order: a sibling that appears later in the output is technically above its prior siblings. This is important information for overlapping views, because the ordering could affect drawing or touch event handling.
+-UIView
+-UIScrollView
| +-PRPCustomView
| | +-PRPCustomView
| | +-PRPCustomView
| | +-UIView
| | +-UIView
| | +-UIView
| +-UIImageView
| +-UIImageView
+-UITableView
| +-UITableViewCell
| | +-UIGroupTableViewCellBackground
| | +-UITableViewCellContentView
| | | +-UILabel
| | | +-UITableViewLabel
| | +-UIButton
| | +-UIImageView
| +-UITableViewCell
| | +-UIGroupTableViewCellBackground
| | | +-UIView
| | +-UITableViewCellContentView
| | | +-UILabel
| | | +-UITableViewLabel
| | +-UIButton
| | +-UIImageView
| +-UIImageView
| +-UIImageView
+-UIRoundedRectButton
| +-UIButtonLabel
+-UISwitch
| +-UIView
| +-UIView
+-UIView
+-PRPLabel
+-PRPLabel
+-PRPLabel
+-PRPLabel
We can now see at will how our view hierarchies are laid out, which can help us figure out why a certain view is (or isn’t) showing on-screen when or where we might expect it to be. Perhaps it’s been added to the wrong superview, or perhaps we’ve forgotten to clean up views we created under specific runtime conditions.
Next, we add some methods for locating a certain type of view within a given hierarchy. Cocoa Touch provides -[UIView viewWithTag:] to conveniently access one explicit view instance we know we’re looking for, but this doesn’t scale well for dynamically constructed hierarchies. It also doesn’t allow for multiple matches. Our solution meets both of these additional needs with the final three category methods.
PrintSubviews/Classes/UIView+PRPSubviewTraversal.h
- (NSArray *)prp_subviewsMatchingClass:(Class)aClass;
- (NSArray *)prp_subviewsMatchingOrInheritingClass:(Class)aClass;
- (void)prp_populateSubviewsOfClass:(Class)aClass
inArray:(NSMutableArray *)array
exactMatch:(BOOL)exactMatch;
The core method, prp_populateSubviewsMatchingClass:inArray:exactMatch:, recurses the tree in a similar fashion to ‑printSubviewsWithIndentString:. Instead of printing every view, however, it checks each subview’s class against the passed Class and adds matches to the passed array, which is passed along to the next recursive call. The exactMatch parameter determines whether subclasses of the specified class should be considered when searching.
PrintSubviews/Classes/UIView+PRPSubviewTraversal.m
- (void)prp_populateSubviewsMatchingClass:(Class)aClass
inArray:(NSMutableArray *)array
exactMatch:(BOOL)exactMatch {
if (exactMatch) {
if ([self isMemberOfClass:aClass]) {
[array addObject:self];
}
} else {
if ([self isKindOfClass:aClass]) {
[array addObject:self];
}
}
for (UIView *subview in self.subviews) {
[subview prp_populateSubviewsMatchingClass:aClass
inArray:array
exactMatch:exactMatch];
}
}
To make things a little more accessible, we define two clearer and less verbose variants, which handle the exactMatch details and return the final array of matches instead of requiring the caller to supply one.
PrintSubviews/Classes/UIView+PRPSubviewTraversal.m
- (NSArray *)prp_subviewsMatchingClass:(Class)aClass {
NSMutableArray *array = [NSMutableArray array];
[self prp_populateSubviewsMatchingClass:aClass
inArray:array
exactMatch:YES];