Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

like image 760
Wim Haanstra Avatar asked Sep 30 '10 10:09

Wim Haanstra


2 Answers

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
like image 167
memmons Avatar answered Oct 04 '22 01:10

memmons


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.

like image 36
Vladimir Avatar answered Oct 04 '22 01:10

Vladimir