Autolayout crash when applying transform: Assertion failure in -[layoutSublayersOfLayer:] - ios

I have a custom view that display a UILabel in the bottom right corner. The view is setup in a method called from both initWithCoder: and initWithFrame: like this:
MCLabel* likesLabel = [[MCLabel alloc] init];
likesLabel.mc_textPadding = UIEdgeInsetsMake(0, 10, 0, 10);
likesLabel.font = [UIFont fontWithName:#"FontAwesome" size:12.f];
[likesLabel setText:#"test"];
likesLabel.numberOfLines = 2;
likesLabel.backgroundColor = [UIColor colorWithWhite:1 alpha:.8];
likesLabel.textColor = UIColor.blackColor;
likesLabel.translatesAutoresizingMaskIntoConstraints = NO;
likesLabel.textAlignment = NSTextAlignmentCenter;
likesLabel.mc_verticalTextAlignment = MCVerticalTextAlignmentTop;
[self addSubview:likesLabel];
self.likesLabel = likesLabel;
NSLayoutConstraint* widthConstraint = [NSLayoutConstraint constraintWithItem:likesLabel attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeWidth multiplier:1 constant:1];
NSLayoutConstraint* heightConstraint = [NSLayoutConstraint constraintWithItem:likesLabel attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:likesLabel attribute:NSLayoutAttributeWidth multiplier:2/5.f constant:1];
NSLayoutConstraint* horizontalPosition = [NSLayoutConstraint constraintWithItem:likesLabel attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeRight multiplier:1 constant:1];
NSLayoutConstraint* verticalPosition = [NSLayoutConstraint constraintWithItem:likesLabel attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeBottom multiplier:1 constant:1];
[likesLabel addConstraints:#[heightConstraint]];
[self addConstraints:#[widthConstraint, horizontalPosition, verticalPosition]];
Now if I leave everything like this I do not have any kind of problem but, as soon as I apply a transform to this label (that is a subclass of UILabel that simply add vertical alignment and edge insets if that matters) the app crashes with the error in console:
*** Assertion failure in -[MCView layoutSublayersOfLayer:], /SourceCache/UIKit/UIKit-2935.138/UIView.m:8794
Auto Layout still required after executing -layoutSubviews
The assertion hints that probably the subclass didn't call [super layoutSubviews] when overriding the methods but I did.
Since is clear that the problem here is the autolayout setup I'm afraid that I'm overlooking at something and maybe the layout is ambiguous hence the crash.
One more note: the same code does not crash on iOS 8 if I move the transform in the - (void)didMoveToSuperview method.
Anyone that can helps here?

I had this issue in iOS7 (but not iOS8) from putting constraint adjustments in viewWillLayoutSubviews. Moving constraint adjustment to viewWillAppear fixed it for me.

In iOS7 you need to do this on viewDidAppear. It will also work for iOS8. If you dont want to see the delayed element change on screen, hide your super view on viewDidLoad and unhide it on viewDidAppear, right after you apply your changes.

Related

XIB gave a constraint to launch the WKWebView, but there is an error A multiplier of 0 or a nil..... How can I fix this?

an error occurred while trying to run webview on xib using PageViewController.
Thread 1: "NSLayoutConstraint for <WKWebView: 0x14d0a8e00; frame = (0 0; 0 0); layer = <CALayer: 0x28109b600>>: A multiplier of 0 or a nil second item together with a location for the first attribute creates an illegal constraint of a location equal to a constant. Location attributes must be specified in pairs."
I made a view in xib (Not a ..cell. This is a view.) and made a wkwebview with code using that view, but I am having a hard time because of these errors. Is there a solution?
Below is the code I wrote.
CGRect rect = [[UIScreen mainScreen]bounds];
//CGRect rect = CGRectMake(0.0, 0.0, self.safeAreaContainerView.layer.frame.size.width, self.safeAreaContainerView.layer.frame.size.height); //I only received width and height as 0 and annotated it.
self.wkwebview = [[WKWebView alloc] initWithFrame:rect];
self.wkwebview.UIDelegate = self;
self.wkwebview.navigationDelegate = self;
self.wkwebview.translatesAutoresizingMaskIntoConstraints = NO;
[self.wkwebview sizeToFit];
self.wkwebview.scrollView.scrollEnabled= YES;
[self.safeAreaContainerView addSubview:self.wkwebview];
[self.safeAreaContainerView sendSubviewToBack:self.wkwebview];
//WKWebView Layout Setting
NSLayoutConstraint *firstViewConstraintTop = [NSLayoutConstraint constraintWithItem:self.wkwebview attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self.safeAreaContainerView attribute:NSLayoutAttributeTop multiplier:1 constant:0];
NSLayoutConstraint *firstViewConstraintBottom = [NSLayoutConstraint constraintWithItem:self.safeAreaContainerView attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:self.wkwebview attribute:NSLayoutAttributeBottom multiplier:1 constant:0];
NSLayoutConstraint *firstViewConstraintLeadingSpace = [NSLayoutConstraint constraintWithItem:self.wkwebview attribute:NSLayoutAttributeLeading relatedBy:NSLayoutRelationEqual toItem:self.safeAreaContainerView attribute:NSLayoutAttributeLeading multiplier:1 constant:0];
NSLayoutConstraint *firstViewConstraintTrailingSpace = [NSLayoutConstraint constraintWithItem:self.safeAreaContainerView attribute:NSLayoutAttributeTrailing relatedBy:NSLayoutRelationEqual toItem:self.wkwebview attribute:NSLayoutAttributeTrailing multiplier:1 constant:0];
[self.safeAreaContainerView addConstraints:[NSArray arrayWithObjects: firstViewConstraintTop, firstViewConstraintBottom, firstViewConstraintLeadingSpace, firstViewConstraintTrailingSpace, nil]];
Does it not work in View when coding the WKWebView to the XIB?
Is it only available on TableViewCell or CollectionViewCell?
I do something similar and use the code I posted here to embed the webview into its container view.
How to: Constrain subview to safeArea Obj-c
Apart from using that I do preciously little on the webview itself, basically the following
webView = [[WKWebView alloc] initWithFrame:GCRectZero];
webView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
and nothing else.
HIH

Autolayout for UITextview inside custom UITableviewcell

I have a UITextView inside a custom UITextviewCell. I want the cell to grow in size according to the content of UITextview. I have pinned top, bottom, right, left of textview to the content view of cell. When I pre-populate textview with text, it is working fine as expected. Its not working when I enter text with keyboard. Inside my custom cell.
UITextView * textView = [[UITextView alloc]init];
[textView setTranslatesAutoresizingMaskIntoConstraints:NO];
[textView setBackgroundColor:[UIColor redColor]];
[textView setScrollEnabled:NO];
[textView setDelegate:self];
[self.contentView addSubview:textView];
NSLayoutConstraint *xConstrain = [NSLayoutConstraint constraintWithItem:textView attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationEqual toItem:self.contentView attribute:NSLayoutAttributeLeft multiplier:1.0 constant:0];
NSLayoutConstraint *yConstrain = [NSLayoutConstraint constraintWithItem:textView attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self.contentView attribute:NSLayoutAttributeTop multiplier:1.0 constant:0];
NSLayoutConstraint *rightConstrain = [NSLayoutConstraint constraintWithItem:textView attribute:NSLayoutAttributeRight relatedBy:NSLayoutRelationEqual toItem:self.contentView attribute:NSLayoutAttributeRight multiplier:1.0 constant:0];
NSLayoutConstraint *bottomConstrain = [NSLayoutConstraint constraintWithItem:textView attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:self.contentView attribute:NSLayoutAttributeBottom multiplier:1.0 constant:0];
[self.contentView addConstraint:xConstrain];
[self.contentView addConstraint:yConstrain];
[self.contentView addConstraint:rightConstrain];
[self.contentView addConstraint:bottomConstrain];
In my tableview controller added following line
-(void)viewdidload
{
self.tableView.rowHeight = UITableViewAutomaticDimension;
self.tableView.estimatedRowHeight = 100.0f ;
}
There are a couple thing you can do here, one that comes to mind:
• As you are entering text, keep track of the height of the UITextView, as in keep an 'old height' variable and a 'new height' variable. To get the 'new height' you could use the sizeThatFits method of the UITextView with height set to for example 1000 (large enough that it won't constrain your result).
• The moment the new height is different than the old height, either smaller or larger, you reload the cell that houses the UITextView you are working on. You can event set the [cell setNeedsLayout], followed by [cell layoutIfNeeded] to make sure it resizes.
I'd imagine this should work, please let me know if it doesn't.

Change Autolayout at runtime

Is it possible to change autolayout constraints during runtime?
I know you can change the constant, but how would you change different attributes.
For example, NSLayoutAttributeTop to NSLayoutAttributeBottom?
Here is a simple sample of what I hope to achieve, it will set a label top left, then when you press a button it will set the label bottom right.
The initial constraints work as expected, tapping the button doesn't work as expected and throws the infamous "Unable to simultaneously satisfy constraints."
Here is the code I am using:
- (void)viewDidLoad
{
[super viewDidLoad];
[self.view setTranslatesAutoresizingMaskIntoConstraints:NO];
self.constraintA = [NSLayoutConstraint constraintWithItem:self.topLabel
attribute:NSLayoutAttributeTop
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeTop
multiplier:1.0
constant:0.0];
self.constraintB = [NSLayoutConstraint constraintWithItem:self.topLabel
attribute:NSLayoutAttributeLeft
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeLeft
multiplier:1.0
constant:0.0];
[self.view addConstraint:self.constraintA];
[self.view addConstraint:self.constraintB];
}
- (IBAction)tappedChange:(id)sender
{
[self.view removeConstraints:#[ self.constraintA, self.constraintB ]];
self.constraintA = [NSLayoutConstraint constraintWithItem:self.topLabel
attribute:NSLayoutAttributeBottom
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeBottom
multiplier:1.0
constant:0.0];
self.constraintB = [NSLayoutConstraint constraintWithItem:self.topLabel
attribute:NSLayoutAttributeRight
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeRight
multiplier:1.0
constant:0.0];
[self.view addConstraint:self.constraintA];
[self.view addConstraint:self.constraintB];
[self.view setNeedsLayout];
}
Thank you for your time.
You only need to perform the Remove/Recreate/Add constraint dance on iOS 7 and below. If you are writing for a modern iOS (8 and above) you can create all your constraints at once and then just set the .active property on whatever NSLayoutConstraint instance you want at any given time.
// Move to right
self.leadingConstraint.active = false;
self.trailingConstraint.active = true;
// Move to bottom
self.topConstraint.active = false;
self.bottomConstraint.active = true;
If you are using Interface Builder you can create all the constraints that will be needed (note the grayed out constraints that aren't active by default).
Then when the button is pressed you can deactivate the old constraints and activate the new ones.
If you are ever unsure about the views being shown you can pause the app execution and use the following private API in the debugger to print out a full list of views and constraints:
po [[UIWindow keyWindow] _autolayoutTrace]
Personally I like using Masonry to manage constraints in code - it's way less writing, easier to read what you wrote six months ago, and the learning curve isn't as head-splitting either.
For example:
// Define some constraints, making the top one a #property:
[view1 mas_makeConstraints:^(MASConstraintMaker *make) {
self.topConstraint = make.top.equalTo(superview.mas_top).with.offset(padding.top);
make.left.equalTo(superview.mas_left).with.offset(padding.left);
}];
// Update the top constraint to match the bottom of the superview instead of the top
[view1 mas_makeConstraints:^(MASConstraintMaker *make) {
self.topConstraint = make.top.equalTo(superview.mas_bottom).with.offset(padding.bottom);
}];

MKAnnotationView leftCalloutAccessoryView Autolayout

I am trying to add a custom view as the leftCalloutAccessoryView in my custom MKAnnotationView. All of the examples I have found use either an image, or some type of view where the frame is specified on init. I want to use autolayout for the view.
This is the code I have in my init method for myMKAnnotationView. I am just trying to use simple width and height constraints:
UIView* contentView = [[UIView alloc] init];
contentView.backgroundColor = [UIColor redColor];
contentView.translatesAutoresizingMaskIntoConstraints = NO;
NSLayoutConstraint* height = [NSLayoutConstraint constraintWithItem:contentView
attribute:NSLayoutAttributeHeight
relatedBy:NSLayoutRelationEqual
toItem:nil
attribute:NSLayoutAttributeNotAnAttribute
multiplier:1
constant:32];
NSLayoutConstraint* width = [NSLayoutConstraint constraintWithItem:contentView
attribute:NSLayoutAttributeWidth
relatedBy:NSLayoutRelationEqual
toItem:nil
attribute:NSLayoutAttributeNotAnAttribute
multiplier:1
constant:50];
[contentView addConstraints:#[height,width]];
self.leftCalloutAccessoryView = contentView;
I can never get the view to show up with constraints. If I remove the constraints and init the view with a frame it will show up. What do I need to do to get constraints working with the leftCalloutAccessoryView?
Also, if I change leftCalloutAccessoryView to detailCalloutAccessoryView, which was added in iOS 9, and it shows up correctly.

Center custom UIView vertically and horizontally using Auto Layout

I'm trying to build a rather simple animated custom UI using the Auto Layout API newly available iOS 6. The custom view I'm building has a circle that I want to be both vertically and horizontally centered.
Unfortunately I can't figure out why my constraints appear to work fine for UIButton, and UILabel elements but yield weird results when I use a custom view with and custom CALayer (in this case a circle, that will eventually be animated).
To be clear I don't want my view to expand to fill the whole screen, but rather to have dynamic "padding" so that the view is vertically centered both on the iPhone 4 and 5. I should also note that I'm very new to Cocoa and UIKit.
RootViewController.m:
...
- (void)viewDidLoad {
[super viewDidLoad];
// Create Circle View
CGRect circle_view_rect = CGRectMake(0, 0, 100, 100);
UIView *circle_view = [[UIView alloc] initWithFrame:circle_view_rect];
// Create Circle Layer
CircleLayer *circle_layer = [[CircleLayer alloc] init];
circle_layer.needsDisplayOnBoundsChange = YES;
circle_layer.frame = circle_view.bounds;
[circle_view.layer addSublayer:circle_layer];
// Enable Auto Layout
[circle_view setTranslatesAutoresizingMaskIntoConstraints:NO];
[self.view addSubview:circle_view];
// Center Vertically
NSLayoutConstraint *centerYConstraint =
[NSLayoutConstraint constraintWithItem:circle_view
attribute:NSLayoutAttributeCenterY
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeCenterY
multiplier:1.0
constant:0.0];
[self.view addConstraint:centerYConstraint];
// Center Horizontally
NSLayoutConstraint *centerXConstraint =
[NSLayoutConstraint constraintWithItem:circle_view
attribute:NSLayoutAttributeCenterX
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeCenterX
multiplier:1.0
constant:0.0];
[self.view addConstraint:centerXConstraint];
}
...
CircleLayer.m:
...
- (void)drawInContext:(CGContextRef)context {
CGContextAddArc(context, 50, 50, 50, 0.0, 2*M_PI, 0);
CGContextSetFillColorWithColor(context, [UIColor yellowColor].CGColor);
CGContextFillPath(context);
}
...
Basically the constraints I've implemented are:
center vertically inside parent view
center horizontally inside parent view
And this is the result I get:
Any help would be greatly appreciated, I've been pondering this one for a few days now.
Thanks
Try adding a height and width constraint to your circle_view. I couldn't even get just a pain square view to appear at all without adding those (using your code, minus the layer stuff).
NSLayoutConstraint *heightConstraint =
[NSLayoutConstraint constraintWithItem:circle_view
attribute:NSLayoutAttributeHeight
relatedBy:NSLayoutRelationEqual
toItem:nil
attribute:NSLayoutAttributeNotAnAttribute
multiplier:1.0
constant:100.0];
[circle_view addConstraint:heightConstraint];
NSLayoutConstraint *widthConstraint =
[NSLayoutConstraint constraintWithItem:circle_view
attribute:NSLayoutAttributeWidth
relatedBy:NSLayoutRelationEqual
toItem:nil
attribute:NSLayoutAttributeNotAnAttribute
multiplier:1.0
constant:100.0];
[circle_view addConstraint:widthConstraint];
Just to add to rdelmar's answer:
The core issue is that as soon as you go the NSLayoutConstraint route, and specify setTranslatesAutoresizingMaskIntoConstraints:NO, the frame you made with CGRectMake is rendered irrelevant for AutoLayout purposes. That's why it didn't use the info from the frame's height and width.

Resources