3

To allow for flexible layouts, I wanted to create a subclass UIView that overrides layoutSubviews: to layout all of its subviews under each other automagically and would continue to do this every time one of its subviews got resized.

However, the only way that I can think of to let the superview know that it should call layoutSubviews: is by overriding that method in each of its subviews, something that I would like to try and avoid (I want people to be able to add arbitrary UIViews to the superview and have this taken care of).

Is there a way for the superview to call layoutSubviews: whenever a subview changes its size, without adding any code to the subview in question?

SpacyRicochet
  • 2,269
  • 2
  • 24
  • 39

2 Answers2

0

You can add a category to the class and try overriding layoutSubviews: from within the category. (This technique has been suggested for customizing navigation bars, and it may well work here too.)

Here's how you'd make a category, taken from my answer here. In your case, remember to substitute UIView for UINavigationController.

Hit Command+N or open the "New File" dialog. Next, choose "Objective-C category" from the Cocoa Touch menu:

Creating a category

Click Next and you will be prompted to enter the name of the class that you would like to add methods to as a category. It should look something like this:

Making a Category on the NavBar

Then, you should end up with a save file dialog. A quick note about convention here. Convention is to name a category after the original class, the plus sign, and then a description of what you're adding. Here's what yours might look like:

Category Naming Convention

Once you save your file, you will need get something like this:

Empty Category

Edit:

If you want to go ahead and do this without a category, then your best bet is to make a subclass of UIView and then subclass that class wherever you want your custom behavior. Another advantage over a category is that your method will only work where you explicitly use the custom class. In categories, the method gets added everywhere.

Good luck!

Community
  • 1
  • 1
Moshe
  • 57,511
  • 78
  • 272
  • 425
  • Good answer, but to the wrong question. I already know how to make categories and I edited my original question because I have since learned that overriding methods in categories is a big no-no. Subclassing is how you should do this. The real question is: Is there a way for the superview to automatically call `layoutSubviews:` when a subview changes its size, _without_ adding any code to the subviews? – SpacyRicochet Aug 10 '11 at 13:38
  • 1
    I'd suggest making a "two" level hierarchy then. Customize the method once in the first subclass and then subclass `that`. – Moshe Aug 10 '11 at 13:52
  • That's what I figured as well, but I was wondering if there was a more elegant solution than changing all the subviews to subclass `that`. I'll give it some more time to see if someone else has another idea. An answer from you that says 'No, it's not.' would get accepted after that ;) Thanks for the comment. – SpacyRicochet Aug 10 '11 at 13:59
  • @Spacy Sure, no problem. I'll edit my answer, so you can accept it if you end up going with that. ;-) – Moshe Aug 10 '11 at 14:00
0

You could use KVO to observe the frame property of each of your subviews. You would need to add yourself as an observer each time a subview is added and remove the observation when a subview is removed – you can override didAddSubview: and willRemoveSubview: in your superview to do that.

- (void)didAddSubview:(UIView *)subview {
    [subview addObserver:self forKeyPath:@"frame" options:NSKeyValueObservingOptionNew context:nil];
}

- (void)willRemoveSubview:(UIView *)subview {
    [subview removeObserver:self forKeyPath:@"frame"];
}

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
    if ([keyPath isEqualToString:@"frame"]) {
         // Do your layout here...
    }
}

- (void)dealloc {
    // You might need to remove yourself as an observer here, in case
    // your subviews are still used by others
}
omz
  • 53,243
  • 5
  • 129
  • 141