3
votes

I'm trying to take advantage of ABPersonViewController's simplicity while at the same time customizing it's appearance slightly to match the look and feel of my app.

I subclassed ABPersonViewController, and using this technique, I loop through the view hierarchy of ABPersonViewController, and here is my output:

UIView: 0x5a45d80; frame = (0 0; 320 416); autoresize = W+H; layer = >
   | ABPersonTableView: 0x6041a00; baseClass = UITableView; frame = (0 0; 320 416); clipsToBounds = YES; autoresize = W+H; layer = ; contentOffset: {0, 0}>
   |    | ABMultiCell: 0x5a50080; baseClass = UITableViewCell; frame = (0 94; 320 45); autoresize = W; tag = 10; layer = >
   |    |    | UIGroupTableViewCellBackground: 0x5a678d0; frame = (9 0; 302 45); autoresize = W; layer = >
   |    |    | UITableViewCellContentView: 0x5a62670; frame = (10 1; 300 43); layer = >
   |    |    |    | ABMultiCellContentView_Simple: 0x5a4fef0; frame = (0 0; 300 43); text = '(555) 555-5555'; clipsToBounds = YES; layer = >
   |    |    |    |    | UILabel: 0x5a61590; frame = (5 14.54; 70 16); text = 'mobile'; clipsToBounds = YES; userInteractionEnabled = NO; layer = >
   |    |    |    |    | ABDividerView: 0x5a62ff0; frame = (79 0; 1 44); alpha = 0; layer = >
   |    |    |    |    |    | ABSteadfastLineView: 0x5a53ab0; frame = (0 0; 1 44); layer = >
   |    |    |    |    | ABHighlightingTextField: 0x5a53c30; baseClass = UITextField; frame = (85 12; 210 21); text = '(555) 555-5555'; clipsToBounds = YES; opaque = NO; userInteractionEnabled = NO; layer = >
   |    |    |    |    |    | UITextFieldLabel: 0x5a53db0; frame = (0 0; 210 19); text = '(530) 305-0835'; clipsToBounds = YES; opaque = NO; userInteractionEnabled = NO; layer = >
   |    | UIView: 0x5a5b300; frame = (0 0; 320 94); layer = >
   |    |    | ABPersonTableHeaderView: 0x5a444b0; frame = (0 0; 320 94); clipsToBounds = YES; autoresize = W; layer = >
   |    |    |    | ABPersonImageView: 0x5a449c0; baseClass = UIControl; frame = (19 15; 64 64); clipsToBounds = YES; autoresize = RM+BM; layer = >
   |    |    |    |    | ABClippingImageView: 0x5a6aae0; frame = (0 0; 64 64); opaque = NO; userInteractionEnabled = NO; layer = >
   |    |    |    |    | ABImageWellLabelView: 0x5a6c0b0; frame = (0 0; 64 64); alpha = 0; opaque = NO; userInteractionEnabled = NO; layer = >
   |    |    |    | ABPersonNameEditingViewContainer: 0x5a4a9e0; frame = (101 15; 530 64); alpha = 0; autoresize = W+BM; layer = >
   |    |    |    |    | ABMultiCellContentView_Name: 0x5a4b480; frame = (1 0; 208 64); clipsToBounds = YES; layer = >
   |    |    |    |    |    | ABHighlightingTextField: 0x5a4d7c0; baseClass = UITextField; frame = (-5 12; 10 21); text = 'First Name'; clipsToBounds = YES; opaque = NO; userInteractionEnabled = NO; tag = 1; layer = >
   |    |    |    |    |    |    | UITextFieldLabel: 0x5a4b3e0; frame = (-10 0; 10 19); text = 'First Name'; clipsToBounds = YES; opaque = NO; userInteractionEnabled = NO; layer = >
   |    |    |    |    |    | ABHighlightingTextField: 0x5a50a90; baseClass = UITextField; frame = (-5 56; 10 21); text = 'Last Name'; clipsToBounds = YES; opaque = NO; userInteractionEnabled = NO; tag = 2; layer = >
   |    |    |    |    |    |    | UITextFieldLabel: 0x5a4e5d0; frame = (-10 0; 10 19); text = 'Last Name'; clipsToBounds = YES; opaque = NO; userInteractionEnabled = NO; layer = >
   |    |    |    |    |    | ABDividerView: 0x5a52300; frame = (0 44; 0 1); layer = >
   |    |    |    |    |    |    | ABSteadfastLineView: 0x5a525d0; frame = (0 0; 0 1); layer = >
   |    |    |    |    |    | ABHighlightingTextField: 0x5a52400; baseClass = UITextField; frame = (-5 100; 10 21); clipsToBounds = YES; opaque = NO; userInteractionEnabled = NO; tag = 3; layer = >
   |    |    |    |    |    |    | UITextFieldLabel: 0x5a5a4e0; frame = (-8 0; 10 19); text = 'Company'; clipsToBounds = YES; opaque = NO; userInteractionEnabled = NO; layer = >
   |    |    |    |    |    | ABDividerView: 0x5a53f00; frame = (0 88; 0 1); layer = >
   |    |    |    |    |    |    | ABSteadfastLineView: 0x5a53f80; frame = (0 0; 0 1); layer = >
   |    |    |    | ABPersonNameDisplayView: 0x5a45cf0; baseClass = UIControl; frame = (97 15; 204 64); autoresize = W; layer = >
   |    |    |    |    | UILabel: 0x5a4bbd0; frame = (0 21; 109 23); text = 'First Name Last Name'; clipsToBounds = YES; autoresize = W; userInteractionEnabled = NO; layer = >
   |    |    |    |    | UILabel: 0x5a4c020; frame = (0 0; 204 0); clipsToBounds = YES; autoresize = W; userInteractionEnabled = NO; layer = >
   |    | UIImageView: 0xcc032a0; frame = (0 409; 320 7); alpha = 0; opaque = NO; autoresize = TM; userInteractionEnabled = NO; layer = >
   |    | UIImageView: 0xcc03350; frame = (313 262; 7 154); alpha = 0; opaque = NO; autoresize = LM; userInteractionEnabled = NO; layer = >

Sweet! So I should be able to recursively loop though the root UIView's subviews, and alter to my hearts content.

The problem is, that when performing this loop, the root UIView only shows one subview (ABPersonTableView), and I can not go any deeper.

Any ideas why? Is ABPersonTableView a private class that hides everything it creates/contains?

I am able to alter the background color of the table by doing this inside my recursive loop:

if ([subview isKindOfClass:[UITableView class]]) {
    ((UITableView *)subview).backgroundColor = [UIColor purpleColor];
}

However, I can't access any of the UILabels because I can't get to any of the subviews of ABPersonTableView.

Any ideas?

4

4 Answers

4
votes

You are stuck because Apple offers only two ways:

  1. Apple's built-in, automatic, super easy look and feel way (which is non-cutomizeable)
  2. Roll-your-own you do everything using low level calls to the AddressBook

ABPersonViewController and its subviews aren't meant to be modified and doing so isn't guaranteed to work with future software updates.

The reason is that Apple wants a consistent look and feel.

If you really need to match your apps look and feel, you will have to go with method 2) above and re-implement the whole thing from scratch. This is not necessarily hard, but certainly time consuming.

Edit: see comments for final solution to poster's Q

1
votes

Note that Apple explicitly warns against modifying the appearance of standard user interface elements and/or manipulating the subviews of standard views and controls. If you're just building this for yourself, no problem, but prepare for rejection if you're planning on submitting this app to the app store.

All the same, it's a good question.

1
votes

You can access ABPersonTableView in viewDidLoad after calling layoutSubViews :) example:

- (void)viewDidLoad
{
  [super viewDidLoad];

  // get ABPersonTableView and forced it layout subviews
  UIView *personTableView = [self.view.subviews objectAtIndex:0];
  [personTableView layoutSubviews];

  // loop through subviews and customize all exceps contacts image
  NSArray *subViews = personTableView.subviews;
  UIView *view;
  for(int i = 0; i < subViews.count-1; i++) {
    view = [subViews objectAtIndex:i];
    //...customize cells
  }    
}
0
votes

Are you doing this in -viewDidLoad? You might have to wait until the table view’s finished calling into whatever it internally uses as a data source to create all its cells. You could do that by calling -reloadData on the table, or by waiting one cycle (using -performSelector:withObject:afterDelay: with a delay of 0), and then trying to mess with its cell contents.