1. Recovering a View Hierarchy Tree
Each view knows both its parent ([aView superview]) and its children ([aView subviews]). Build a view tree like the one shown in Listing 1 by recursively walking through a view’s subviews. Recipe 1
does exactly that. It builds a visual tree by noting the class of each
view and increasing the indentation level every time it moves down from a
parent view to its children. The results are stored into a mutable
string and returned from the calling method.
The code shown in Recipe 6-1 was used to create the tree shown in Listing 1.You can use this routine to duplicate the results
of Listing 1, or you can copy it to other applications to view their hierarchies.
Recipe 1. Extracting a View Hierarchy Tree
// Recursively travel down the view tree, increasing the
// indentation level for children
- (void) dumpView: (UIView *) aView atIndent: (int) indent
into:(NSMutableString *) outstring
{
for (int i = 0; i < indent; i++)
[outstring appendString:@"—"];
[outstring appendFormat:@"[%2d] %@\n", indent,
[[aView class] description]];
for (UIView *view in [aView subviews])
[self dumpView:view atIndent:indent + 1 into:outstring];
}
// Start the tree recursion at level 0 with the root view
- (NSString *) displayViews: (UIView *) aView
{
NSMutableString *outstring = [[NSMutableString alloc] init];
[self dumpView:aView atIndent:0 into:outstring];
return [outstring autorelease];
}
2. Querying Subviews
Views store arrays of their children. Retrieve this array by calling [aView subviews].
Onscreen, the child views are always drawn after the parent, in the
order that they appear in the subviews array. These views draw in order
from back to front, and the subviews array mirrors that drawing pattern.
Views that appear later in the array are drawn after views that appear
earlier.
The subviews method returns just those
views that are immediate children of a given view. At times, you may
want to retrieve a more exhaustive list of subviews including the
children’s children. Recipe 6-2 introduces allSubviews(),
a simple recursive function that returns a full list of descendants for
any view. Call this function with view.window to return a complete set
of views appearing in the UIWindow that hosts that view. This list proves useful when you want to search for a particular view, like a specific slider or button.
Although it is not typical, iPhone applications
may include several windows, each of which can contain many views.
Recover an exhaustive list of all application views by iterating through
each available window. The allApplicationSubviews() function in Recipe 6-2 does exactly that. A call to [[UIApplication sharedApplication] windows] returns the array of application windows. The function iterates through these, adding their subviews to the collection.
In addition to knowing its subviews, each view knows the window it belongs to. The view’s window property points to the window that owns it. Recipe 2 also includes a simple function called pathToView() that returns an array of superviews, from the window down to the view in question. It does this by calling superview repeatedly until arriving at that window.
Views can also check their superview ancestry in another way. The isDescendantOfView:
method determines whether a view lives within another view, even if
that view is not its direct superview. This method returns a simple
Boolean value. YES means the view descends from the view passed as a parameter to the method.
Recipe 2. Subview Utility Functions
// Return an exhaustive descent of the view's subviews
NSArray *allSubviews(UIView *aView)
{
NSArray *results = [aView subviews];
for (UIView *eachView in [aView subviews])
{
NSArray *riz = allSubviews(eachView);
if (riz) results = [results arrayByAddingObjectsFromArray:riz];
}
return results;
}
// Return all views throughout the application
NSArray *allApplicationViews()
{
NSArray *results = [[UIApplication sharedApplication] windows];
for (UIWindow *window in [[UIApplication sharedApplication]
windows])
{
NSArray *riz = allSubviews(window);
if (riz) results = [results arrayByAddingObjectsFromArray:
riz];
}
return results;
}
// Return an array of parent views from the window down to the view
NSArray *pathToView(UIView *aView)
{
NSMutableArray *array = [NSMutableArray arrayWithObject:aView];
UIView *view = aView;
UIWindow *window = aView.window;
while (view != window)
{
view = [view superview];
[array insertObject:view atIndex:0];
}
return array;
}
|