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
Related
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]]
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
I'm using TTLauncherView as a sort of home screen for my app and I only have one page's worth of icons. How can I make it so the TTLauncherView won't let you drag icons to "the next page"? I want to set a maximum number of pages (in this case one.)
(EDIT: long story short, I subclassed beginEditing, see the answer below.)
I see where why it adds an extra page when beginEditing is called, but I don't want to edit the framework code. (That makes it hard to update to newer versions.) I'd also prefer not to subclass and override that one method, if I have to rely on how it's implemented. (I'm not against subclassing or adding a category if it's clean.)
I tried setting scrollView.scrollEnabled to NO in the callback method launcherViewDidBeginEditing in my TTLauncherViewDelegate, but that doesn't work while it's in editing mode and I don't know why.
I tried adding a blocker UIView to the scrollview to intercept the touch events by setting userInteractionEnabled=NO, which works OK. I still have to disable the dragging of TTLauncherItems to the next page somehow.
I also tried setting the contentSize of the scrollview to it's bounds in launcherViewDidBeginEditing, but that didn't seem to work either.
Is there a better way?
Tried to block gestures:
- (void)setLauncherViewScrollEnabled:(BOOL)scrollEnabled {
if (scrollEnabled) {
[self.scrollViewTouchInterceptor removeFromSuperview];
self.scrollViewTouchInterceptor = nil;
} else {
// iter through the kids to get the scrollview, put a gesturerecognizer view in front of it
UIScrollView *scrollView = [launcherView scrollViewSubview];
self.scrollViewTouchInterceptor = [UIView viewWithFrame:scrollView.bounds]; // property retains it
UIView *blocker = self.scrollViewTouchInterceptor;
[scrollView addSubview:scrollViewTouchInterceptor];
[scrollView sendSubviewToBack:scrollViewTouchInterceptor];
scrollViewTouchInterceptor.userInteractionEnabled = NO;
}
}
For reference: TTLauncherView.m:
- (void)beginEditing {
_editing = YES;
_scrollView.delaysContentTouches = YES;
UIView* prompt = [self viewWithTag:kPromptTag];
[prompt removeFromSuperview];
for (NSArray* buttonPage in _buttons) {
for (TTLauncherButton* button in buttonPage) {
button.editing = YES;
[button.closeButton addTarget:self action:#selector(closeButtonTouchedUpInside:)
forControlEvents:UIControlEventTouchUpInside];
}
}
// Add a page at the end
[_pages addObject:[NSMutableArray array]];
[_buttons addObject:[NSMutableArray array]];
[self updateContentSize:_pages.count];
[self wobble];
if ([_delegate respondsToSelector:#selector(launcherViewDidBeginEditing:)]) {
[_delegate launcherViewDidBeginEditing:self];
}
}
I think overriding beginEditing in TTLauncherView is your best bet. Since you'd only really be touching one method (and only a few lines in that method), upgrading it when the time comes shouldn't be too bad. Since that method explicitly adds the extra page, I'm not sure how you'd get around it w/o editing that specific piece of code
As Andrew Flynn suggested in his answer, I was able to make it work by subclassing and overriding the beginEditing method to remove the extra page TTLauncherView adds when it goes into editing mode.
One problem I'm having is I can't figure out how to remove the warning I get for calling the (private) method updateContentSize on my subclass. I tried casting it to id, but that didn't remove the warning. Is it possible?
edit: I was able to remove the warning by using performSelector to send the message to the private class. (I had previously create a category method performSelector:withInt that wraps NSInvocation so that I can pass primitives via performSelector methods, which makes this very convenient.)
// MyLauncherView.h
#interface MyLauncherView : TTLauncherView {
NSInteger _maxPages;
}
#property (nonatomic) NSInteger maxPages;
#end
// MyLauncherView.m
#implementation MyLauncherView
#synthesize maxPages = _maxPages;
- (void)beginEditing {
[super beginEditing];
// ignore unset or negative number of pages
if (self.maxPages <= 0) {
return;
}
// if we've got the max number of pages, remove the extra "new page" that is added in [super beginEditing]
if ([_pages count] >= self.maxPages ) {
[_pages removeLastObject];
[self updateContentSize:_pages.count]; // this has a compiler warning
// I added this method to NSObject via a category so I can pass primitives with performSelector
// [self performSelector:#selector(updateContentSize:) withInt:_pages.count waitUntilDone:NO];
}
}
I was wondering how to find out if a subview (in my case pageShadowView) has already been added to my view.
I've come up with this, but it doesn't really work:
if ([pageShadowView isKindOfClass:[self.view class]]) {
[self.view addSubview:pageShadowView];
}
Also, I'm still confused about the self.-thing. I know that this has to do with making clear that we are talking about the view of the current ViewController ... but do I really need it if there (1) are no other ViewControllers or (2) if it doesn't really matter because if I ever wanted to refer to another viewController, I'd make sure to call it?
I'm sorry if this is all very basic, but I'd be very grateful for your comments.
Here:
BOOL doesContain = [self.view.subviews containsObject:pageShadowView];
And yes, you need this self. There is no explicit ivar "view" on UIViewController. The self.view statement is actually a call on method [self view] which is a getter for UIViewController's view.
Give it a unique tag: view.tag = UNIQUE_TAG, then check the container view for existence:
BOOL alreadyAdded = [containerView viewWithTag:UNIQUE_TAG] != nil;
you can find a sub view like this
for(UIView *view in self.view.subviews)
{
if([view isKindOfClass:[UIView class]])
{
//here do your work
}
}
There's one more way to find, in Swift: isDescendant(of view: UIView) -> Bool or in Obj-C: - (BOOL)isDescendantOfView:(UIView *)view
Swift:
if myView.isDescendant(of: self.view) {
//myView is subview of self.view, remove it.
myView.removeFromSuperview()
} else {
//myView is not subview of self.view, add it.
self.view.addSubview(myView)
}
Obj-C:
if([myView isDescendantOfView:self.view]) {
//myView is subview of self.view, remove it.
[myView removeFromSuperView];
} else {
//myView is not subview of self.view, add it.
[self.view addSubView:myView];
}
SWIFT VERSION:
let doesContain = self.view?.subviews.contains(pageShadowView)
best and easy way to find that your view is exist or not , there are many way like you check a view is contain or not in super view or just view some time this method is failed because if the Uiview is already remove then error occur,
so code is here :
here errorView is my UiView
errorView.tag = 333
if ( self.view?.viewWithTag(333) != nil ){
print("contain")
}
else {
print("not contain")
}
To add to what coneybeare said, you could do the following. If you set your object.tag=100;
if ([self.view.superview viewWithTag:100] == nil){ //if statement executes if the object with tag 100 in view.superview is absent (nil)
if ([self.view viewWithTag:100] == nil){ //if statement executes if the object with tag 100 in view (not superview) is absent (nil)
add a retain value of the view
then check the retain value
if > 1 , then exist , if perfect should be 2
then release it once
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.