How to remove customviews added programmatically? - iphone

In one of the iPad Application, I am working I have added custom views to a view. This works fine but now I want to remove all the custom views added. How do I do that?
Following is my code for adding custom views
for (int col=0; col<colsInRow; col++) {
// NSLog(#"Column Number is%d",col);
x=gapMargin+col*width+gapH*col;
//self.styleButton=[[UIButton alloc] initWithFrame:CGRectMake(x, y, width, height)];
ComponentCustomView *componentCustomobject=[[ComponentCustomView alloc] initWithFrame:CGRectMake(x, y, width, height)];
componentCustomobject.backgroundColor=[UIColor redColor];
componentCustomobject.componentLabel.text=[appDelegate.componentsArray objectAtIndex:row];
[self.formConatinerView addSubview:componentCustomobject];
tempCount1++;
}

You can remove all the subviews of type ComponentCustomView from your parent views
for (UIView *view in self.formConatinerView.subviews) {
if ([view isKindOfClass:[ComponentCustomView class]) {
[view removeFromSuperview];
}
}

I'm not sure if removing objects from the array being iterated (in this case subviews) was safe or not (I remember reading something about a discrepancy between Mac OS X and iOS, but not sure...); Unless property subviews returns a copy of the internal array (very likely since the internal array needs to be mutable), a 100% safe, just-in-case way to do it would be this:
NSArray* copyOfSubviews = [[NSMutableArray alloc] initWithArray:[myView subviews]];
// Explicitly made mutable in an attempt to prevent Cocoa from returning
// the same array, instead of making a copy. Another, tedious option would
// be to create an empty mutable array and add the elements in subviews one by one.
for(UIView* view in copyOfSubviews){
if ([view isKindOfClass:[ComponentCustomView class]){
[view removeFromSuperview];
}
}
// (This is for non-ARC only:)
[copyOfSubviews release];

NSArray *arr = [self.view subViews];
for (UIView *view in arr) {
if ([view isKindOfClass:[ComponentCustomView class]) {
[view removeFromSuperview];
}
}

Related

iOS: Removing one view from superview causes another to get removed?

This is a very odd problem. I have 5 subviews added to a UIViewController. One of them needs to be removed, but when I do this, one of the remaining 4 subviews is also removed. This necessitates that I re-add it using addSubview. The two views in question are not related in any way.
Is this a known iOS SDK bug? It happens for sure running on the simulator with iOS 6.1.
Thanks.
Here, In Your Question not mention That which Method you use for remove subView so,I give you simple suggestion for remove subView.
Give Tag of Each subView such like,
self.subView1.tag = 1;
self.subView2.tag = 2;
.
.
.
.
self.subViewN.tag = N;
And You can access(Remove) any subView base on its Tag, such like
[[self.view viewWithTag:1] removeFromSuperview];
This tips might helpful for you.
You can remove single subview using the following code.
[subview_Name removeFromSuperview];
if you want to remove all subviews form the view then use this.
NSArray *subViewArray = [self.view subviews];
for (id obj in subViewArray)
{
[obj removeFromSuperview];
}
if you want to remove all subview of particular class then use this.
NSArray *subViewArray = [self.view subviews];
for (id obj in subViewArray)
{
if([obj isKindOfClass:[classname class]])
{
[obj removeFromSuperview];
}
}
example : if you want to remove subview of UIImageView class then replace if condition with this.
[obj isKindOfClass:[UIImageView class]]

On iOS, is there a way to search ONLY subviews with a certain tag?

Because right now, viewWithTag actually search for itself first, and then all subviews recursively down the whole subtree, for a view with that tag.
But what if I set the tags of the subviews to 100, 101, etc, and later on, look for tag 100, but the parent of this current view sets the current view's tag to 100? Then viewWithTag will return the current view instead of any subview.
It is also strange that if the code is
[fooView viewWithTag: 123]
why would the code want to search the subtree including fooView itself? It is like, the code doesn't know fooView good enough to want to search for it too. Or put it another way, fooView is told to search itself... which is strange. A view doesn't know itself? (need to do a search to look for itself?)
So is there a way to search for subviews and grand-subviews only (without searching for self)?
Take advantage of the recursive nature of -viewWithTag:
- (UIView *)viewWithTagNotCountingSelf:(NSInteger)tag
{
UIView *toReturn = nil;
for (UIView *subView in self.subviews) {
toReturn = [subView viewWithTag:tag];
if (toReturn) {
break;
}
}
return toReturn;
}
Edit: this will drill down farther than "grand-subviews": it will get any view within the hierarchy that is not self. Also this is to be implemented in a category on UIView.
let result = view.subviews.filter{$0.tag == tag}.first
After reviewing the documentation for -viewWithTag: and running a few tests, it appears the answer to OP's question is - This behavior is already provided.
Return Value
The view in the receiver’s hierarchy whose tag property matches the
value in the tag parameter.
Discussion
This method searches the current view and all of its subviews for the
specified view.
I am concluding this to mean that 'view' is also a 'subview', thus limiting the scope of the search.
do this:
NSMutableArray *arrSameViewTag = [NSMutableArray array];
for(UIView *subview in [yourView subviews]) //your view to find subview
{
if(subview.tag == 123) //specific tah here
{
[arrSameViewTag addObject:subview]; //view found add in array
}
}
NSlog(#"arrSameViewTag : %#",arrSameViewTag);
To find specific like UIButton or any UIElement then like this:
NSMutableArray *arrSameViewTag = [NSMutableArray array];
for(id *subview in [yourView subviews]) //your view to find subview
{
if([subview isKindofClass[UIButton class]) //any UIElement of specific type here
{
UIButton *btn = (UIButton *)subview; //same UIElement mentioned for checking it
if(btn.tag == 123) //specific tah here
{
[arrSameViewTag addObject:subview]; //view found add in array
}
}
}
NSlog(#"arrSameViewTag : %#",arrSameViewTag)
For 1 level:
UIView *view;
for (int i = 0; i < viewToSearch.subviews.count; i++){
UIView *subview = viewToSearch.subviews[i];
if (subview.tag == tagToSeach){
view = subview;
break;
}
}
To search a view hierarchy with multiple levels:
__block UIView *view;
BOOL (^__block searchViewForTag)(UIView *,NSInteger) = ^(UIView *aView, NSInteger tag){
for (UIView *subview in aView.subviews){
if (subview.tag == tag){
view = subview;
return YES;
}
if (searchViewForTag(subview,tag)) return YES;
}
return NO;
};
NSInteger tagToSearchFor = 1;
searchViewForTag(viewToSearch,tagToSearchFor);
//Do something with view

Move buttons down in UIAlertView

I have an app which displays in landscape mode and I've overwritten the height of a UIAlertView with the following code:
- (void) willPresentAlertView:(UIAlertView *)alertView
{
CGRect screenBounds = [[UIScreen mainScreen] bounds];
CGRect frame = [alertView frame];
alertView.frame = CGRectMake(frame.origin.x, 0, frame.size.width, screenBounds.size.width);
}
This almost works. The UIAlertView is taller, however, the buttons in the alertview don't get pushed down.
Does anyone know how I can push the buttons in a UIAlertView down when I change the alert view's height?
I think it is more elegant and less risky to replace UIAlertView with some independent AlertView instead of messing around with it.With independent I mean not inheriting form UIAlertView.
TSAlertView is such a replacement.
http://dl.dropbox.com/u/47535/TSAlertView/1-thumb.png
http://dl.dropbox.com/u/47535/TSAlertView/3-thumb.png
http://dl.dropbox.com/u/47535/TSAlertView/2-thumb.png
I'm guessing that you'll probably have to subclass / manipulate the UIAlertView class to achieve that, which can be risky as Apple can change their implementations so wrap your code in appropriate checks.
A start would be to do:
NSLog(#"UIAlertView subviews: %#",alertView.subviews);
That'll print some output for the various elements making up the UIAlertView, you will probably see a few UIButtons in there which you can then manipulate by using something like:
UIButton* button1 = (UIButton*)[alertView.subviews objectAtIndex:N];
[button1 setFrame:CGRect(button1.frame.origin.x, button1.frame.origin.y+10, button1.frame.size.width, button1.frame.size.height)]
A sensible check would be to confirm that the objectAtIndex is the correct type of element before you perform operations on it, this isn't foolproof however as Apple could add more UIButtons or something..
if ([[alertView.subviews objectAtIndex:N] isKindOfClass:[UIButton class]]) {
...
}
Depending on your situation you may also want to iterate over the subviews array and move all UIButtons down rather than just hardcoding in some specific objectsAtIndex but that's a whole other answer. :-)
This link should help. In my opinion, I would not try messing around with the view hierarchies. This can lead to rejection from the App Store. Either build a new AlertView from scratch, or leave it as it is.
NSArray *subViewArray=[alertView subviews];
for (NSUInteger ind=0 ; ind < [subViewArray count]; ind++) {
if ([[alertView.subviews objectAtIndex:ind] isKindOfClass:[UIButton class]]) {
UIButton* button1 = (UIButton*)[alertView.subviews objectAtIndex:ind];
[button1 setFrame:CGRectMake(button1.frame.origin.x, button1.frame.origin.y+100, button1.frame.size.width, button1.frame.size.height)];
NSLog(#"button1.frame.origin.y=[%1.0f]",button1.frame.origin.y);
}
}

Need some help cleaning up a bit of my code

I have 5 buttons named opt1, opt2, opt3, etc. If I want to hide/show/do something to them, could I create a simple for statement instead of doing opt1.hidden = YES, opt2.hidden....? If so, what would it look like? Thanks
EDIT: This is the code I am trying to clean:
opt1.hidden = NO;
opt2.hidden = NO;
opt3.hidden = NO;
opt4.hidden = NO;
opt5.hidden = NO;
Is there a simple for statement I could use that would hide all of them without having to hide each one manually since the only difference in their name is the number at the end? It doesn't seem like a lot of buttons but I will have to add lots more soon so I would rather not have 20 lines of code just to hide a bunch of buttons.
You can do this.
NSArray *myButtons = [NSArray arrayWithObjects:b1, b2, b3, b4, nil];
for (UIButton *button in myButtons)
{
button.hidden = YES;
}
David's suggestion is a good one if you know and have a pointer to all your buttons.
An alternative would be to loop through all of your UIView subviews and hide buttons as you find them:
for (id subview in self.view)
{
if ([subview isKindOfClass:[UIButton class]])
[(UIButton*)subview setHidden:YES];
}
If you want to be selective with the buttons you are hiding, simply add a specific tag to it upon creation (i.e. button1.tag = 999) and use:
for (id subview in self.view)
{
if ([subview isKindOfClass:[UIButton class]] && subview.tag == 999)
[(UIButton*)subview setHidden:YES];
}
David's answer is probably best, and is how I would do it for a small number of controls. You could also use KVC.
NSArray *myButtons = [NSArray arrayWithObjects:#"b1", #"b2", #"b3", #"b4", nil];
for (NSString *str in myButtons)
{
id cont = [self valueForKey:key] ;
if ([cont isKindOfClass:[UIButton class]]) {
[cont setHidden:YES] ;
}
}
The reason I am showing you this method is it can be used to create "bindings" between a DB and your controls. Imagine if the myButtons array contained the names of DB fields. You could then name your UI controls in your controller with the same name. Then all you need is a simple for loop, and maybe some isKindOfClass test, to move all the control data into your DB. Here is an example from one of my projects.
NSArray *fn = [AOSConfig sharedInstance].fieldNames ;
for (NSString* name in fn) {
#try {
id uifield = [self valueForKey:name] ;
if ([cont isKindOfClass:[UITextField class]]) {
[aosShotData setValue:[uifield valueForKey:#"text"] forKey:name]
}
}
#catch(NSException *e) {
}
}
That's it to save all the text data to a CoreData managed object. You would need to get creative if you need various data types. If the DB is complex in terms of the data type to control mapping it may be better to just write it brute force.

Warning generated by UIButton setting code

I have a for loop setting the background images on buttons, basically the buttons are thumbnail previews of different items & can't be set statically, however the code gives an warning because it runs through all the UIViews but then calls setBackgroundImage which does not apply to all views. The warning is an irritation, I understand what it's complaining about, how can I get rid of it? (I don't want to turn off the warning, I want to fix the problem)
// For loop to set button images
for (UIView *subview in [self.view subviews]) // Loop through all subviews
{
// Look for the tagged buttons, only the 8 tagged buttons & within array bounds
if((subview.tag >= 1) && (subview.tag <= 8) && (subview.tag < totalBundles))
{
// Retrieve item in array at position matching button tag (array is 0 indexed)
NSDictionary *bundlesDataItem = [bundlesDataSource objectAtIndex:(subview.tag - 1)];
// Set button background to thumbnail of current bundle
NSString *picAddress = [NSString stringWithFormat:#"http://some.thing.com/data/images/%#/%#", [bundlesDataItem objectForKey:#"Nr"], [bundlesDataItem objectForKey:#"Thumb"]];
NSURL *picURL = [NSURL URLWithString:picAddress];
NSData *picData = [NSData dataWithContentsOfURL:picURL];
// Warning is generated here
[subview setBackgroundImage:[UIImage imageWithData:picData] forState:UIControlStateNormal];
}
}
You can either do this:
for (id subview in [self.view subviews])
So that the id type will stop any type checking... or check whether the object responds to the selector and call it like this:
if ([subview respondsToSelector:#selector(setBackgroundImage:forState:)]) {
[subview performSelector:#selector(setBackgroundImage:forState:)
withObject:[UIImage imageWithData:picData]
withObject:UIControlStateNormal];
}
Note that I coded that last part from memory.
A dangerous bit of code with so many assumptions, but...you would first do well to check the class of the UIView before sending it a setBackgroundImage message and then just simply cast your UIView to remove the warning:
if ([subview class] == [UIButton class]) {
[((UIButton *)subview) setBackgroundImage:[UIImage imageWithData:picData] forState:UIControlStateNormal];
}