Wim Haanstra
Wim Haanstra

Reputation: 5998

Find all controls of a type in a UIView

I am looking for a way to automatically localize texts on buttons/textfields etc and for this method I need to find all (for example) UIButton's on a UIView.

I tried the following 2 methods, but they both do no work like I want them to work:

for (UIView* subView in self.view.subviews)
{
    NSLog(@"object class : %@", [subView class]);

    if ([subView isMemberOfClass:[UIButton class]])
        NSLog(@"Button found!");
}

The problem with this piece of code is that a RoundedRectButton does not match the UIButton class, while it really is just a UIButton.

I also tried the following:

for (UIButton* button in self.view.subviews)
{
// Do my stuff
}

But the stupid thing is, is that cocoa-touch actually just lists all subviews in that for-loop (also the UITextFields etc).

Is there a way to actually just get all UIButtons from a view? Or do I really need to find controls by looking at their selectors.

Upvotes: 7

Views: 6352

Answers (2)

memmons
memmons

Reputation: 40492

Why write one-off code like this when you can dial up the awesomeness by adding a category method to UIView using blocks? Take a look at the code at the very bottom. Using this recursive method with blocks you can do things like disable all UITextFields in a view controller's view:

[self.view forEachUITextFieldDoBlock:^(UITextField *textField) {
   textfield.enabled = NO;
}];

Or fade out all UITextFields in a view controller's view:

[UIView animateWithDuration:0.25 animations:^{

   [self.view forEachUITextFieldDoBlock:^(UITextField *textField) {
      textField.alpha = 0.0;
   }];

}
completion:^(BOOL finished){
   // nothing to do for now
}];

Blocks are pretty amazing in that you can even pass other methods inside a block. For example, the code below passes each UITextField found to my inserAdornmentImage:forTextView method, which adds a custom background image to each text view:

[self.view forEachUITextFieldDoBlock:^(UITextField *textField) {
    [self insertAdornmentImage:textFieldBGImage forTextField:textField];
}];

Blocks make the method incredibly flexible so you aren't having to write a specialized method each time you want to do something new with the controls you find. Here's the magic sauce:

@implementation UIView (Helper)

- (void) forEachUITextFieldDoBlock:(void (^)(UITextField *textField))block
{
   for (UIView *subview in self.subviews)
   {
      if ([subview isKindOfClass:[UITextField class]])
      {

        block((UITextField *)subview);

      } else {

         [subview forEachUITextFieldDoBlock:block];

      }
   }
}

@end

Upvotes: 10

Vladimir
Vladimir

Reputation: 170839

the first method is correct, except you need to change isMemberOfClass function to isKindOfClass:

isKindOfClass: Returns a Boolean value that indicates whether the receiver is an instance of given class or an instance of any class that inherits from that class.

Upvotes: 9

Related Questions