21

I've added subview (ViewController) to my ViewController:

Location *location = [[Location alloc] initWithNibName:@"Location" bundle:[NSBundle mainBundle]];
[self.subView addSubview:location.view];

How can I latter remove this subview?

I know that for removing all subviews is:

for (UIView *subview in [self.view subviews]) {

  [subview removeFromSuperview];

}
iWizard
  • 6,816
  • 19
  • 67
  • 103
  • You shouldn't really be adding another viewController's view to an existing viewController's view like that – Paul.s May 03 '12 at 16:20
  • there is no way that I could remove that addedd subview? – iWizard May 03 '12 at 16:21
  • Well you can, but I was just making an observation, about a potential pain point and root of many tricky bugs. If you want to remove that particular view then why not make an ivar that will give you a reference to either the `location` viewController of the `view` itself and then you don't need to climb through the subviews... – Paul.s May 03 '12 at 16:23
  • Can you explain that in more details pls because I don't understand or give mi link to an example? – iWizard May 03 '12 at 16:25

3 Answers3

51

Quick and dirty: Give your view a tag, so you can later identify it:

Location *location = [[Location alloc] initWithNibName:@"Location" bundle:[NSBundle mainBundle]];
UIView *viewToAdd = location.view;
viewToAdd.tag = 17; //you can use any number you like
[self.view addSubview:viewToAdd];

Then, to remove:

UIView *viewToRemove = [self.view viewWithTag:17];
[viewToRemove removeFromSuperview];

A cleaner, faster, easier to read and to maintain alternative would be to create a variable or property to access the view:

In the interface:

@property (nonatomic, weak) UIView *locationView;

In the implementation:

Location *location = [[Location alloc] initWithNibName:@"Location" bundle:[NSBundle mainBundle]];
UIView *viewToAdd = location.view;
self.locationView = viewToAdd;
[self.view addSubview:viewToAdd];

Then, to remove:

[self.locationView removeFromSuperview];

That said, heed the warnings from commenters about playing with other ViewControllers' Views. Read up on ViewController containment if you want to do it.

fzwo
  • 9,842
  • 3
  • 37
  • 57
  • `viewWithTag:` = Yuk. If you need a handle to a view, why not just give yourself a proper one without wading through a view hierarchy asking every view for an arbitrary number that anyone can accidentally change. – Paul.s May 03 '12 at 16:33
  • 2
    Because this answers the generic question the asker had without having to introduce iVars or changing anything else other than those two calls. I very rarely tag my views (and if so, mostly for debugging), but here it finally has a use. – fzwo May 03 '12 at 16:36
  • That is just encouraging bad design... (Also when you reply to people do that @ thing or they don't get notified and may never know you responded...) – Paul.s May 03 '12 at 16:37
  • @Paul.s I agree it's not great, but I don't think your post explains what (and why) well enough to encourage better design (Don't you get notified because your comment is directly above mine, and you commented on my post? I admit I forgot). – fzwo May 03 '12 at 16:42
  • Nope it seems a bit silly, but I would only get notified if you commented on my question/answer or use @myName. Ah well I'm going on the assumption that if the OP doesn't quite understand something they'll look it up, which is the best way to learn. You could always up the standard of your answer slightly by adding constants instead of magic numbers ;) at least then I could shudder a little less – Paul.s May 03 '12 at 16:48
  • @Paul.s I understand, but I wanted to keep it as short and easy to understand as possible (which is why I've inserted the unnecessary local variables for the views). Constants or defines for the numbers would have added confusion, I fear. – fzwo May 03 '12 at 16:50
7

Create an ivar that either gives you a reference to the new viewController or just the view. I'll go for the viewController here

Add a property and synthesize it

// .h
@property (nonatomic, strong) Location *location;

// .m
@synthesize location = _location;

Now when you create location set the ivar

Location *location = [[Location alloc] initWithNibName:@"Location" bundle:[NSBundle mainBundle]];
self.location = location;

[self.subView addSubview:location.view];

Now later to remove it

[self.location.view removeFromSuperview];

Side note

Generally it is a painful path to be adding a view controller's view to the view of another like this. For some light reading about this see Abusing UIViewControllers

Your naming of Location is probably not excellent, it may be more appropriate to call it something like LocationViewController or similar. Consistant naming in this way allows anyone else (or future you) to be able to easily read and grasp that this is a viewController without opening up the header.

Community
  • 1
  • 1
Paul.s
  • 38,494
  • 5
  • 70
  • 88
  • Excellent point about the naming (and the ViewControllers). This solution is cleaner than mine below. – fzwo May 03 '12 at 16:45
2

You could simply set a unique tag to your view that identifies it. And then when you want to remove it. Use the viewWithTag:(NSInteger)tag method to get it back and remove only this one.

Location *location = [[Location alloc] initWithNibName:@"Location" bundle:[NSBundle mainBundle]];
location.tag = 8001; // 8001 is an exemple
[self.subView addSubview:location.view];

And then

UIView * v = [self.subView viewWithTag:8001];
if (nil != v) {
    [v removeFromSuperview];
}
Vaseltior
  • 1,226
  • 15
  • 27
  • The nil check is unnecessary in Objective-C; otherwise, this is just a duplicate of my answer - and @Paul.s already gave the cleanest answer anyway. – fzwo May 03 '12 at 16:46
  • We did it almost at the same moment. But good writing explain things ;-) I don't like abusing system by not checking the message that is send. Don't send a message to the object if you don't have to, or if it is not necessary. This is useless and can become of heavy cost in a big loop environment. – Vaseltior May 03 '12 at 16:50
  • Sending messages to nil has almost no greater overhead than checking if (foo != nil), because objc_msgSend checks that first and returns early. This is *not* abusing the system, it is using the beauty of the system. Checking for Null is for other languages. – fzwo May 03 '12 at 17:00
  • Sometimes you have to do high performance computations, sometimes not. This is why I keep this hybrid and still fundamental way of coding. And this will help your successor understand what you're doing. Do you have any clue or link showing that `objc_msgSend` checks that first? I'm really interested in the basements of ObjC. – Vaseltior May 03 '12 at 21:28
  • Do not fall prey to premature optimization: Optimizing away objc_msgSend only makes sense after you've made every other optimization. [Here](http://www.friday.com/bbum/2009/12/18/objc_msgsend-part-1-the-road-map/) is a great in-depth article about it by @bbum, and a [performance evaluation](http://www.mikeash.com/pyblog/performance-comparisons-of-common-operations-iphone-edition.html) by Mike Ash on an iPhone 1. Also see the excellent answers to http://stackoverflow.com/questions/2962466/how-much-overhead-does-a-msg-send-call-incur - especially also the two non-accepted answers. – fzwo May 03 '12 at 21:58
  • Anyway, my point was: Nil-Checking like in your example is bad style in Objective-C, and adds negligible performance benefit (for instance, using an iVar for the view instead of tagging it would give orders of magnitude higher performance increase - and still good style). I don't think one should post such unnecessary "optimizations" in an example for a beginner. – fzwo May 03 '12 at 22:05
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/10864/discussion-between-vaseltior-and-fzwo) – Vaseltior May 03 '12 at 22:39