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

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]]

Related

NSGenericException', reason: 'Unable to install constraint on view

Terminating app due to uncaught exception 'NSGenericException'
Terminating app due to uncaught exception 'NSGenericException', reason: 'Unable to install constraint on view. Does the constraint
reference something from outside the subtree of the view? That's
illegal. constraint:
view:; layer = ; contentOffset: {0, 0}>'
You need to install the constraint on the "higher" of the two views. A good, general way to do this is like this:
NSLayoutConstraint* constraint = ...;
NSView* firstView = constraint.firstItem;
NSView* secondView = constraint.secondItem;
[[firstView ancestorSharedWithView: secondView] addConstraint: constraint];
Just a word of caution: It's good to remember here that constraint attributes are evaluated in the context of the view on which they are added. So for instance, the value of NSLayoutAttributeLeft of viewA, for a constraint installed on viewB, is interpreted in the coordinate space of viewB. For constraints that only reference sibling views or their superview, that fact is largely irrelevant, but there's no restriction that constraints can't reference two views that aren't siblings or direct parents.
Similar to neoneye I was getting this due to removing subviews with constraints. However I had a constraint that was positioning the parent view, and this was being removed if I called [self.view removeConstraints:self.view.constraints]; Instead I made this change,
Original Code:
for (UIView *subview in [view subviews]) {
[subview removeFromSuperview];
}
Fixed to remove constraints on subviews:
NSMutableArray * constraints_to_remove = [ #[] mutableCopy] ;
for( NSLayoutConstraint * constraint in view.constraints) {
if( [view.subviews containsObject:constraint.firstItem] ||
[view.subviews containsObject:constraint.secondItem] ) {
[constraints_to_remove addObject:constraint];
}
}
[view removeConstraints:constraints_to_remove];
for (UIView *subview in [view subviews]) {
[subview removeFromSuperview];
}
UPDATE: So I hit this error again - and it was due to removing a single view this time. Added a function to remove the view cleanly:
void cleanRemoveFromSuperview( UIView * view ) {
if(!view || !view.superview) return;
//First remove any constraints on the superview
NSMutableArray * constraints_to_remove = [NSMutableArray new];
UIView * superview = view.superview;
for( NSLayoutConstraint * constraint in superview.constraints) {
if( constraint.firstItem == view ||constraint.secondItem == view ) {
[constraints_to_remove addObject:constraint];
}
}
[superview removeConstraints:constraints_to_remove];
//Then remove the view itself.
[view removeFromSuperview];
}
I experienced this error on iOS6. In my case it was because I started removing subviews without first removing constraints.
// I had forgotten to remove constraints first. This caused the crash.
[self.view removeConstraints:self.view.constraints];
NSArray *subviews = self.view.subviews;
for (UIView *subview in subviews) {
[subview removeFromSuperview];
}
[self addYourSubviewsHere];
I got this problem using a UIPickerView like input of a UITextField (using Autolayout). When I push another viewController and thus a pop it to the viewController with the picker, the app crashes. I found the following solution, in the UIPickerViewController:
-(void)viewWillAppear:(BOOL)animated{
[self.pickerView removeFromSuperview];
[self.pickerView setTranslateAutoresizingMaskIntoContraints:YES];
[self.view addSubview];
}
You can also set the UIPickerViewPosition after removing from superview. I hope that can help you!
I found adding this one line of code fixed this issue for a cocoa ScrollView.
[scrollView setTranslatesAutoresizingMaskIntoConstraints:NO];
I think certain views add constraints at run time therefore conflicting when you add your own via objective c, so you need to disable this behaviour...
Same error, different solution here:
I got this error on starting up my app on iOS 6 after adding a new view and forgetting to switch off Use Auto Layout on it in the interface builder ... I hate it there's no standard setting to NOT use auto layout by default for new views ...
I had the same crash, and it turned out to be a floating-point precision problem with constraint multiplier values. I converted all my constraint multipliers to nice floating-point values (e.g. 0.375 instead of 0.35) and that fixed the crash.
AutoLayout: removeFromSuperview / removeConstraints throws exception and crashes hard

Creating a custom view in Interfacebuilder and using that in code

I have made a view in the application that shows a UITableView. It will be full of results (obviously) almost all the time, however when it does not have any results I want to show another view that inform the user about how he/she could populate the table.
I want to design that view in the interfacebuilder. I will have to check in the code whether the datasource is empty or not to toggle between the two different nibs. How do I instantiate and configure a view made in Interfacebuilder?
The easies way to do this is by adding the view in xib normally and make it visible
Design your both views, the table view and the other view, give the tableView a tag of 111 for example and give the otherview another tag 222 for example
Now in viewDidLoad
Get both the views
UIView *noDataView = [self.view viewWithTag:222];
UITableView *tableView = [self.view viewWithTag:111];
//Hide both of them or only the noDataView until you know if you have data from the dataSource or not
Check for your data source
//hasElements do you have any element to show?
if(hasElements)
{
noDatView.hidden = YES;
tableView.hidden = NO;
}
else
{
noDatView.hidden = NO;
tableView.hidden = YES;
}
You can load nib file based on condition.You can write category as follows:
self.view = (UIView *)[self loadNib:#"SecondView" inPlaceholder:self.view];
- (UIView *)viewFromNib:(NSString *)nibName
{
NSArray *xib = [[NSBundle mainBundle] loadNibNamed:nibName owner:self options:nil];
for (id view in xib) { // have to iterate; index varies
if ([view isKindOfClass:[UIView class]]) return view;
}
return nil;
}
- (UIView *)loadNib:(NSString *)nibName inPlaceholder:(UIView *)placeholder
{
UIView *nibView = [self viewFromNib:nibName];
[nibView setFrame:placeholder.frame];
self.view = nibView;
//[self.view insertSubview:nibView aboveSubview:placeholder];
//[placeholder removeFromSuperview];
return nibView;
}
The other answers give you possible technical solutions but I would propose that if you are using the standard Apple design guidelines, you probably don't even need to worry about it. For instance, somewhere on your screen you should have a bar button item with the identifier "Add" (which shows the plus icon). Then rather than giving a long (often poorly localised) description of how to add items, just have a header for an empty section which says "No items" replacing items with whatever pluralised noun is appropriate for your table's items. For example, for an Archery related app I am working on:
Notice how the Edit button is currently disabled too, thus no explanation is needed as the only thing they can do at this point is tap the Add button (screenshots on the Appstore will have shown them what they can expect to see after this point).

Add subview (rows) fast to UIScrollView while scrolling

I have UIScrollView with a lot of rows (~100) and I implemented dequeueReusableRow method for fast allocating and adding my subviews (rows). Everything work fine, but if I scroll very fast with decelerate some view don't added to scrollView on time only later.
- (UIView *)dequeueReusableRow
{
UIView *view = [reusableRows anyObject];
if(view)
{
[[view retain] autorelease];
[reusableRows removeObject:view];
}else{
view = [[UIView alloc] init....
}
return view;
}
- (void)addVisibleRows
{
UIView *row = [self dequeueReusableRow];
row.frame = ....
[scrollView addSubview:row]
}
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
[self addVisibleRows];
[self removeInvisibleRows];
}
Please, don't propose me use UITableView because structure of accordion looks like:
section
- section
-- section
--- row
- section
section
- row
dequeueReusableRow is not part of UIScrollViewController, so I assume dequeueReusableRow is something you implemented yourself. If so, can you show that code? It is likely where we can help with any inefficiencies.
Also, if your scrollview contains rows, why not just use a UITableView which will do much of the work for you? I know you asked not to propose it - can you explain why you need to use a ScrollView so we can help you better?
It's very hard to tell from that code snippet. Some more details would be appreciated.
One minor suggestion in the meantime: Call removeInvisibleRows before addVisibleRows

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);
}
}

Problem releasing object using "removeFromSuperView" iPhone

I have this concrete problem, but if You find my initial design idea crazy and have a better suggestion, please let me know:)
I have a UIView that acts as a container/background for adding other views. The important thing is that only one view is present at a time. So before doing any adding of views I do this:
for(UIView *v in [self.currentView subviews]) {
[v removeFromSuperview];
}
self.currentView is the view I add my subviews to.
After this I add a new UIView in this manner:
UIView *tempView;
switch (self.currentIndex) {
case 1:
tempView = [[AView alloc] initWithFrame:frame];
[self.currentView addSubview:tempView];
[tempView release];
break;
case 2:
tempView = [[AView alloc] initWithFrame:frame];
[self.currentView addSubview:tempView];
[tempView release];
break;
default:
break;
}
This way I remove all views, since I release the tempView straight after I add it to
the self.currentView I end up with a retain count of one on the the UIView currently living
in the currentView.
This all seems fine, but as I look at it with Instruments I can see that each time I run the above code a new AView object is allocated and the old one keeps hanging around with a retain count of 1, either I am missing some obvious retain action being performed on my object or else the "removeFromSuperView" does not call "release" on my view.
In real life my objects are not of type AView, but of many different types, but this way I can test if there is always only one AView instance allocated.
As I can read from the documentation "removeFromSuperView" should call "release" on the view so I am a bit confused as to why my Views are not deallocated.
Again, maybe I am going about this the wrong way and suggestions are really welcome.
The setup is that there is a number of button at the bottom of the screen and when the user clicks on the view changes.
Thanks for any help or pointers given:)
You are iterating a collection and simultaneously changing it
Try
while ([self.currentView subviews].count>0) {
[[[self.currentView subviews] objectAtIndex:0] removeFromSuperView];
}
instead.
you could try the "bringSubviewToFront" and "sendSubviewToBack" functions instead of creating a new UIView everytime. That ways, you won't be creating uiviews for every action and therefore be less pressing on the memory consumption of your application.