IT tutorials

iPhone Developer : Assembling Views and Animations - Managing Subviews - Tagging and Retrieving Views

5/28/2013 7:36:22 PM
- How To Install Windows Server 2012 On VirtualBox
- How To Bypass Torrent Connection Blocking By Your ISP
- How To Install Actual Facebook App On Kindle Fire

The UIView class offers numerous methods that help build and manage views. These methods let you add, order, remove, and query the view hierarchy. Since this hierarchy controls what you see onscreen, updating the way that views relate to each other changes what you see on the iPhone. Here are some approaches for typical view-management tasks.

Adding Subviews

Call [parentView addSubview:child] to add new subviews to a parent. Newly added subviews are always placed frontmost on your screen; the iPhone adds them on top of any existing views. To insert a subview into the view hierarchy at a particular location other than the front, the SDK offers a trio of utility methods:

  • insertSubview:atIndex:

  • insertSubview:aboveSubview:

  • insertSubview:belowSubview:

These methods control where view insertion happens. That insertion can remain relative to another view, or it can move into a specific index of the subviews array. The above and below methods add subviews in front of or behind a given child. Insertion pushes other views forward and does not replace any views that are already there.

Reordering and Removing Subviews

Applications often need to reorder and remove views as users interact with the screen. The iPhone SDK offers several easy ways to do this, allowing you to change the view order and contents.

  • Use [parentView exchangeSubviewAtIndex:i withSubviewAtIndex:j] to exchange the positions of two views.

  • Move subviews to the front or back using bringSubviewToFront: and sendSubviewToBack.

  • To remove a subview from its parent, call [childView removeFromSuperview]. If the child view had been onscreen, it disappears. Removing a child from the superview calls a release on the subview, allowing its memory to be freed if its retain count has returned to zero.

When you reorder, add, or remove views, the screen automatically redraws to show the new view presentation.

View Callbacks

When the view hierarchy changes, callbacks can be sent to the views in question. The iPhone SDK offers six callback methods. These callbacks may help your application keep track of views that are moving and changing parents.

  • didAddSubview: is sent to a view after a successful invocation of addSubview: lets subclasses of UIView perform additional actions when new views are added.

  • didMoveToSuperview: informs views that they’ve been re-parented to a new superview. The view may want to respond to that new parent in some way. When the view was removed from its superview, the new parent is nil.

  • willMoveToSuperview: is sent before the move occurs.

  • didMoveToWindow: provides the callback equivalent of didMoveToSuperview but when the view moves to a new Window hierarchy instead of to just a new superview.

  • willMoveToWindow: is, again, sent before the move occurs.

  • willRemoveSubview: informs the parent view that the child view is about to be removed.

1. Tagging and Retrieving Views

The iPhone SDK offers a built-in search feature that lets you recover views by tagging them. Tags are just numbers, usually positive integers, that identify a view. Assign them using the view’s tag property, for example, myView.tag = 101. In Interface Builder, you can set a view’s tag in the attributes inspector. As Figure 1 shows, you specify the tag in the View section.

Figure 1. Set the tag for any view in Interface Builder’s attributes inspector.

Tags are completely arbitrary. The only “reserved” tag is 0, which is the default property setting for all newly created views. It’s up to you to decide how you want to tag your views and which values to use. You can tag any instance that is a child of UIView, including windows and controls. So if you have many onscreen buttons and switches, adding tags helps tell them apart when users trigger them. You can add a simple switch statement to your callback methods that looks at the tag and determines how to react.

Apple rarely tags subviews. The only instance I have ever found of their view tagging has been in UIAlertViews where the buttons use tags of 1, 2, and so forth. (I’m half convinced they left this tagging in there as a mistake.) If you worry about conflicting with Apple tags, start your numbering at 10 or 100, or some other number higher than any value Apple might use.

Using Tags to Find Views

Tags let you avoid passing user interface elements around your program by making them directly accessible from any parent view. The viewWithTag: method recovers a tagged view from a child hierarchy. The search is recursive, so the tagged item need not be an immediate child of the view in question. You can search from the window with [window viewWithTag:101] and find a view that is several branches down the hierarchy tree. When more than one view uses the same tag, viewWithTag: returns the first item it finds.

The problem with viewWithTag: is that it returns a UIView object. This means you often have to cast it to the proper type before you can use it. Say you want to retrieve a label and set its text.

UILabel *label = (UILabel *)[self.view.window viewWithTag:101];
label.text = @"Hello World";

It would be far easier to use a call that returned an already typed object and then be able to use that object right away, as these calls do:

- (IBAction)updateTime:(id)sender
    // set the label to the current time
    [self.view.window labelWithTag:LABEL_TAG].text =
        [[NSDate date] description];

- (IBAction)updateSwitch:(id)sender
    // toggle the switch from its current setting
    UISwitch *s = [self.view.window switchWithTag:SWITCH_TAG];
    [s setOn:!s.isOn];

Recipe 1 extends the behavior of UIView to introduce a new category, TagExtensions. This category adds just two typed tag methods, for UILabel and UISwitch. . The additional classes were omitted for space considerations; they follow the same pattern of casting from viewWithTag:. Access the full collection by including the UIView-TagExtensions files in your projects.

Recipe 1. Recovering Tagged Views with Properly Cast Objects
@interface UIView (TagExtensions)
- (UILabel *) labelWithTag: (NSInteger) aTag;
- (UISwitch *) switchWithTag: (NSInteger) aTag;

@implementation UIView (TagExtensions)
- (UILabel *) labelWithTag: (NSInteger) aTag
    return (UILabel *)[self viewWithTag:aTag];

- (UISwitch *) switchWithTag: (NSInteger) aTag
    return (UISwitch *)[self viewWithTag:aTag];

2. Naming Views

Although tagging offers a thorough approach to identifying views, some developers may prefer to work with names rather than numbers. Using names adds an extra level of meaning to your view identification schemes. Instead of referring to “the view with a tag of 101,” a switch named “Ignition Switch” describes its role and adds a level of self-documentation missing from a plain number.

// Toggle switch
UISwitch *s = [self.view switchNamed:@"Ignition Switch"];
[s setOn:!s.isOn];

It’s relatively easy to design a class that associates strings with view tags. This custom class needs to store a dictionary that matches names with tags, allowing views to register and unregister those names. Recipe 6-4 shows how to build that view name manager, which uses a singleton instance ([ViewIndexer sharedInstance]) to store its tag and name dictionary.

The class demands unique names. If a view name is already registered, a new registration request will fail. If a view was already registered under another name, a second registration request will unregister the first name. There are ways to fool this of course. If you change a view’s tag and then register it again, the indexer has no way of knowing that the view had been previously registered. So if you decide to use this approach, set your tags in Interface Builder or let the registration process automatically tag the view but otherwise leave the tags be.

If you build your views by hand, register them at the same point you create them and add them into your overall view hierarchy. When using an IB-defined view, register your names in viewDidLoad using the tag numbers you set in the attributes inspector.

- (void) viewDidLoad
    [[self.view viewWithTag:LABEL_TAG] registerName:@"my label"];
    [[self.view viewWithTag:SWITCH_TAG] registerName:@"my switch"];

Recipe 2 hides the view indexer class from public view. It wraps its calls inside a UIView category for name extensions. This allows you to register, retrieve, and unregister views without using ViewIndexer directly. 

Recipe 2. Creating a View Name Manager
@interface ViewIndexer : NSObject {
    NSMutableDictionary *tagdict;
    NSInteger count;
@property (nonatomic, retain) NSMutableDictionary *tagdict;

@implementation ViewIndexer
@synthesize tagdict;

static ViewIndexer *sharedInstance = nil;

+(ViewIndexer *) sharedInstance {
    if(!sharedInstance) sharedInstance = [[self alloc] init];
    return sharedInstance;

- (id) init
    if (!(self = [super init])) return self;
    self.tagdict = [NSMutableDictionary dictionary];
    count = 10000;
    return self;

- (void) dealloc
    self.tagdict = nil;
    [super dealloc];

// Pull a new number and increase the count
- (NSInteger) pullNumber
    return count++;

// Check to see if name exists in dictionary
- (BOOL) nameExists: (NSString *) aName
    return [self.tagdict objectForKey:aName] != nil;

// Pull out first matching name for tag
- (NSString *) nameForTag: (NSInteger) aTag
    NSNumber *tag = [NSNumber numberWithInt:aTag];
    NSArray *names = [self.tagdict allKeysForObject:tag];
    if (!names) return nil;
    if ([names count] == 0) return nil;
    return [names objectAtIndex:0];

// Return the tag for a registered name. 0 if not found
- (NSInteger) tagForName: (NSString *)aName
    NSNumber *tag = [self.tagdict objectForKey:aName];
    if (!tag) return 0;
    return [tag intValue];

// Unregistering reverts tag to 0
- (BOOL) unregisterName: (NSString *) aName forView: (UIView *) aView
    NSNumber *tag = [self.tagdict objectForKey:aName];

    // tag not found
    if (!tag) return NO;

    // tag does not match registered name
    if (aView.tag != [tag intValue]) return NO;

    aView.tag = 0;
    [self.tagdict removeObjectForKey:aName];
    return YES;

// Register a new name. Names will not re-register. (Unregister first,
// please). If a view is already registered, it is unregistered and
// re-registered
- (NSInteger) registerName:(NSString *)aName forView: (UIView *) aView
    // You cannot re-register an existing name
    if ([[ViewIndexer sharedInstance] nameExists:aName]) return 0;

    // Check to see if the view is named already. If so, unregister.
    NSString *currentName = [self nameForTag:aView.tag];
    if (currentName) [self unregisterName:currentName forView:aView];

    // Register the existing tag or pull a new tag if aView.tag is 0
    if (!aView.tag) aView.tag = [[ViewIndexer sharedInstance]
    [self.tagdict setObject:[NSNumber numberWithInt:aView.tag]
        forKey: aName];
    return aView.tag;

@implementation UIView (NameExtensions)

- (NSInteger) registerName: (NSString *) aName
    return [[ViewIndexer sharedInstance] registerName: aName
        forView: self];

- (BOOL) unregisterName: (NSString *) aName
    return [[ViewIndexer sharedInstance] unregisterName: aName

- (UIView *) viewNamed: (NSString *) aName
    NSInteger tag = [[ViewIndexer sharedInstance] tagForName: aName];
    return [self viewWithTag: tag];
- iPhone Developer : Assembling Views and Animations - Recovering a View Hierarchy Tree, Querying Subviews
- Java ME on Symbian OS : Handling Diversity - Handling Screen and Display Diversity
- Java ME on Symbian OS : Handling Diversity - Handling Diverse Multimedia Formats and Protocols
- Java ME on Symbian OS : Handling Diversity - Supporting Diverse Input Mechanisms
- Windows Phone 7 : Building 2D Games with the XNA Framework - AlienShooter Game Play (part 6) - Updated GameplayScreen Class, Collision Detection and Memory Management
- Windows Phone 7 : Building 2D Games with the XNA Framework - AlienShooter Game Play (part 5) - Missile Class, Game Status Board Class
- Windows Phone 7 : Building 2D Games with the XNA Framework - AlienShooter Game Play (part 4) - Hero Ship Class
- Windows Phone 7 : Building 2D Games with the XNA Framework - AlienShooter Game Play (part 3) - Enemy Class
- Windows Phone 7 : Building 2D Games with the XNA Framework - AlienShooter Game Play (part 2) - Game Object Class
- Windows Phone 7 : Building 2D Games with the XNA Framework - AlienShooter Game Play (part 1) - Sprite Animation
Top 10
- Microsoft Visio 2013 : Adding Structure to Your Diagrams - Finding containers and lists in Visio (part 2) - Wireframes,Legends
- Microsoft Visio 2013 : Adding Structure to Your Diagrams - Finding containers and lists in Visio (part 1) - Swimlanes
- Microsoft Visio 2013 : Adding Structure to Your Diagrams - Formatting and sizing lists
- Microsoft Visio 2013 : Adding Structure to Your Diagrams - Adding shapes to lists
- Microsoft Visio 2013 : Adding Structure to Your Diagrams - Sizing containers
- Microsoft Access 2010 : Control Properties and Why to Use Them (part 3) - The Other Properties of a Control
- Microsoft Access 2010 : Control Properties and Why to Use Them (part 2) - The Data Properties of a Control
- Microsoft Access 2010 : Control Properties and Why to Use Them (part 1) - The Format Properties of a Control
- Microsoft Access 2010 : Form Properties and Why Should You Use Them - Working with the Properties Window
- Microsoft Visio 2013 : Using the Organization Chart Wizard with new data
programming4us programming4us
Popular tags
Video Tutorail Microsoft Access Microsoft Excel Microsoft OneNote Microsoft PowerPoint Microsoft Project Microsoft Visio Microsoft Word Active Directory Biztalk Exchange Server Microsoft LynC Server Microsoft Dynamic Sharepoint Sql Server Windows Server 2008 Windows Server 2012 Windows 7 Windows 8 Adobe Indesign Adobe Flash Professional Dreamweaver Adobe Illustrator Adobe After Effects Adobe Photoshop Adobe Fireworks Adobe Flash Catalyst Corel Painter X CorelDRAW X5 CorelDraw 10 QuarkXPress 8 windows Phone 7 windows Phone 8 BlackBerry Android Ipad Iphone iOS