110

I have a view that needs to have its frame manipulated programmatically - it's a kind of document view that wraps to its content which is then scrolled and zoomed around a superview by manipulating the frame origin. Autolayout fights with this at runtime.

Disabling autolayout completely seems a bit harsh because it could reasonably be used to handle layout for the other views. It seems like what I might want is some kind of "null constraint".

terriblememory
  • 1,712
  • 3
  • 18
  • 25

14 Answers14

171

I had the same problem. But I have resolved it.
Yes, you can disable auto layout at runtime for a specific UIView, instead of disabling it for the whole xib or storyboard which is set by default in Xcode 4.3 and later.

Set translatesAutoresizingMaskIntoConstraints to YES, before you set the frame of your subview:

self.exampleView.translatesAutoresizingMaskIntoConstraints = YES;
self.exampleView.frame = CGRectMake(20, 20, 50, 50);
Leandros
  • 16,805
  • 9
  • 69
  • 108
Muhammad Aamir Ali
  • 20,419
  • 10
  • 66
  • 57
  • 2
    This looks like overkill for some more complicated cases with a lot of views. For something as simple as this, this is alright. – esh Mar 27 '14 at 07:31
  • @MuhammadAamirALi remove subview and then add it back may be decrease performance.Can we archive better solution? – Nhat Dinh Oct 22 '14 at 03:48
  • 2
    @DinhNhat I clarified the answer. Removing and re-adding the subview is not necessary. – Leandros Nov 24 '14 at 09:33
  • or in swift `self.exampleView.setTranslatesAutoresizingMaskIntoConstraints(true)` – TimWhiting Feb 25 '15 at 14:44
  • 5
    It shows me warning : "Unable to simultaneously satisfy constraints." – NSPratik Apr 27 '15 at 09:46
  • 1
    You saved my day ... translatesAutoresizingMaskIntoConstraints ... who invents properties like this ***shakinghead*** – Tintenklecks Oct 19 '15 at 16:22
  • If you are still having trouble with unexpected constraints also set `view.autoresizingMask = AutoresizingMask(rawValue: 0)`. – devios1 Apr 23 '19 at 18:53
  • If the view already has some constraints set it will throw the "Unable to simultaneously satisfy constraints." typical error. Before calling ´translatesAutoresizingMaskIntoConstraints´one needs to delete the view's constraints first. – crom87 Jul 27 '19 at 10:10
50

I had a similar issue where Autolayout was overriding some of my frame-setting at run time (I had a dynamic view that in some cases pushed a new view controller...pushing and then pressing Back would reset the initial view).

I got around this by putting my manipulation code in viewDidLayoutSubviews of my View Controller. This seems to get called after whatever constraint mojo gets called, but before viewDidAppear, so the user is none the wiser.

jmstone617
  • 5,707
  • 2
  • 24
  • 26
  • 1
    Doesn't work for self.navigationItem.titleView. Still doesn't respect the frame change. – Henrik Erlandsson Dec 09 '13 at 10:52
  • 1
    If you're trying to muck with the frame of a title view, I would put it inside of a container view and add the container view as a custom view of the navigationItem. – jmstone617 Dec 09 '13 at 23:27
  • 2
    This worked for me as well. I'd wasted 2h:22m banging my head against the proverbial wall before discovering this workaround. – TMc Jan 26 '14 at 03:02
  • Thanks, man. I'm in my second day struggling with this issue and now it works! – apostolov Jan 21 '16 at 16:00
30

Perhaps just setting translatesAutoresizingMaskIntoConstraints to YES (and not adding additional constraints affecting that view) will let you set the frame without fighting the auto layout system.

sig
  • 6,024
  • 2
  • 26
  • 31
Jon Hull
  • 679
  • 6
  • 15
17

In iOS 8 you can set an NSLayoutConstraint to be active or not. So if I'm using interface builder, I add all my constraints to an OutletCollection and then activate or deactivate using:

NSLayoutConstraint.deactivateConstraints(self.landscapeConstraintsPad)
NSLayoutConstraint.activateConstraints(self.portraitConstraintsPad)

The particular application I'm using it for here is having different constraints in portrait and landscape mode and I activate/deactivate based on the rotation of the device. It means I can create some complex layout changes all in interface builder for both orientations, and still use auto layout without the verbose auto layout code.

Or you can activate / deactivate using removeConstraints and addConstraints.

bandejapaisa
  • 26,576
  • 13
  • 94
  • 112
  • Sorry for hijacking such an old question, but may I ask how you managed to add layout constraints for different layouts in the first place? I can't seem to find an option inside interface builder to enable/disable a constraints. Or did you simply add constraints for both layouts and just ignore the interface builder complaining about invalid (overriding) constraints? – Malte Mar 13 '15 at 08:19
  • Well you could do it by using size classes: https://developer.apple.com/library/ios/recipes/xcode_help-IB_adaptive_sizes/chapters/SelectingASizeClass.html#//apple_ref/doc/uid/TP40014436-CH7-SW1, but I couldn't do it this way because I was supporting iOS 7 too and size classes don't fully support iOS 7. I did it by lowering the priority of the conflicting constraint. Not ideal, but it prevents the warning. I think this is how I did it anyway.... – bandejapaisa Mar 13 '15 at 11:34
10

I don't know if this will help anyone else, but I wrote a category to make this convenient because I find myself doing this a lot.

UIView+DisableAutolayoutTemporarily.h

#import <UIKit/UIKit.h>

@interface UIView (DisableAutolayoutTemporarily)

// the view as a parameter is a convenience so we don't have to always
// guard against strong-reference cycles
- (void)resizeWithBlock:(void (^)(UIView *view))block;

@end

UIView+DisableAutolayoutTemporarily.m

#import "UIView+DisableAutoResizeTemporarily.h"
@implementation UIView (DisableAutoResizeTemporarily)

- (void)resizeWithBlock:(void (^)(UIView * view))block
{
    UIView *superview = self.superview;
    [self removeFromSuperview];
    [self setTranslatesAutoresizingMaskIntoConstraints:YES];
    __weak UIView *weakSelf = self;
    block(weakSelf);
    [superview addSubview:self];
}

@end

I use it like this:

[cell.argumentLabel resizeWithBlock:^(UIView *view) {
    [view setFrame:frame];
}];

Hope it helps.

michaelsnowden
  • 6,031
  • 2
  • 38
  • 83
  • Brilliant ! I used a modified version of your code to write a simple "hideControl" function for any UIView. It basically sets the height of a UIView to 0, but, crucially, it uses your code to make AutoLayout handle it properly (unlike using "someView.hidden=TRUE"). So controls which appeared below the hidden control, and had vertical spacing linked to it, would now shift up on the screen to fill the gap, where the hidden-view previously appeared. – Mike Gledhill Dec 10 '14 at 14:09
6

You can set the translatesAutoresizingMaskIntoConstraints type Boolean, Value Yes in the User Defined Runtime Attributes of the UIView you want in the xib/storyboard.

Bhavin Bhadani
  • 22,224
  • 10
  • 78
  • 108
renaun
  • 151
  • 2
  • 4
4

In my view I had a Label and a Text. The label had pan gesture. The label moves around fine during drag. But when I use the text box keyboard, the label resets its position to the original location defined in auto layout. The issue got resolved when I added the following in swift for the label. I added this in viewWillAppear but it can be added pretty much anywhere you have access to the target field.

self.captionUILabel.translatesAutoresizingMaskIntoConstraints = true
Bhavin Bhadani
  • 22,224
  • 10
  • 78
  • 108
Big D
  • 377
  • 4
  • 6
2
  • Open project in 4.5
  • Select storyboard
  • Open the file inspector
  • Under Interface Builder Document uncheck 'Use Autolayout'

You can split across multiple storyboards if you want to use autolayout for some views.

Mike Bobbitt
  • 739
  • 9
  • 12
  • 10
    That disables autolayout for the entire storyboard/nib. What I was looking for was a way (ideally programmatic) to have particular subviews within a storyboard/nib not use autolayout while leaving the rest doing whatever the storyboard/nib specified. – terriblememory Jul 31 '12 at 05:01
  • You could use multiple storyboards and just invoke the ones with/without autolayout as needed: //Initialize the storboard & detail view controller for display UIStoryboard* sb = [UIStoryboard storyboardWithName:@"Storyboard" bundle:[NSBundle mainBundle]]; DetailViewController *dvController = [sb instantiateViewControllerWithIdentifier: entry.viewName]; – Mike Bobbitt Aug 02 '12 at 18:41
1

For me it worked to create the subview programmatically, in my case the auto layout was messing with a view that I needed to rotate around its center but once I created this view programmatically it worked.

Andrespch
  • 370
  • 3
  • 16
1

I've encountered a similar scenario, where I joined a project that was initiated with auto-layout, but I needed to make dynamic adjustments to several views. Here is what has worked for me:

  1. Do NOT have views or components laid out in interface builder.

  2. Add your views purely programmatically starting with alloc/init and setting their frames appropriately.

  3. Done.

Bill the Lizard
  • 398,270
  • 210
  • 566
  • 880
Yup.
  • 1,883
  • 21
  • 18
0

This happened to me in a project without storyboards or xib files. All 100% code. I had an ad banner at the bottom and wanted the view bounds to stop at the ad banner. The view would resize itself automatically after loading. I tried every resolution on this page but none of them worked.

I ended up just creating a sub view with the shortened height and placed that in into the main view of the controller. Then all my content went inside the sub view. That solved the problem very easily without doing anything that felt like it was going against the grain.

I am thinking if you want a view that is not the normal size that fills the window then you should use a sub view for that.

RandallTo
  • 395
  • 2
  • 11
0

Instead of disabling autolayout, I would just calculate the new constraint with the frame you are replacing. That appears to me to be the appropriate way. If you are adjusting components that rely on constraints, adjust them accordingly.

For example, if you have a vertical constraint of 0 between two views (myView and otherView), and you have a pan gesture or something that adjusts the height of myView then you can recalculate the constraint with the adjusted values.

self.verticalConstraint.constant = newMyViewYOriginValue - (self.otherView.frame.origin.y + self.otherView.frame.size.height);
[self.myView needsUpdateConstraints];
0

For those of you who are using auto layout, please check out my solution here. You should be making @IBOutlet's of the constraints you want to adjust and then change their constants.

Community
  • 1
  • 1
Mike
  • 1,307
  • 3
  • 17
  • 29
-16

if it's xib file:

  1. select the .xib file
  2. select the "File's Owner"
  3. show the Utilities
  4. click on: "File Inspector"
  5. Under "Interface Builder Document" disable: "Use Autolayout"
Alish
  • 133
  • 3
  • 13
  • 1
    No, that disables autolayout for the entire xib. What I was looking for is a way to take only one particular subview out of the autolayout system. – terriblememory Dec 03 '12 at 15:26