Can I use setFrame and autolayout on the same view?

52,446

Solution 1

Yes, this can be done.

If you set a view's translatesAutoresizingMaskIntoConstraints = YES, then calls to setFrame: are automatically translated at runtime into layout constraints based on the view's current autoresizingMask. This lets you mix frame-based layout with constraint-based layout.

For instance you could use Auto Layout to define the layout of all of the subviews of a view, but still call setFrame: to set the size and position of the view itself. From your perspective, you're doing layout with a mix of Auto Layout and direct frame manipulation. But the system is actually using constraints to handle everything.

However, there is one big caveat about using translatesAutoresizingMaskIntoConstraints.

When you do this, you still need to make sure that these automatic constraints can be satisfied with the rest of your constraints.

So, for instance, suppose there are already constraints that determine the size and position of your view, and then you also set translatesAutoresizingMaskIntoConstraints = YES and called setFrame:. The call to setFrame: will generate new constraints on the view, which will probably conflict with the already existing constraints.

(In fact, this error happens often. If you ever see a log message complaining about conflicting constraints, and one of those constraints is a NSAutoresizingMaskLayoutConstraint, then what you're seeing is a conflict with an automatic constraint. This is an easy mistake, because translatesAutoresizingMaskIntoConstraints = YES is the default value, so if you're configuring constraints in code you need to remember to turn it off if you don't want these automatic constraints.)

In contrast, suppose again that there are already existing constraints that determine the size and position of your view, but then you set translatesAutoresizingMaskIntoConstraints = NO before you call setFrame:. In this case, your setFrame: calls would not produce new constraints, so there would be no conflict between separate constraints. However, in this case, there is still a "conflict" between the constraints and the frame value you set. At the next time Auto Layout is invoked, it would see the already existing constraints on the view, calculate the frame value which they require, and set the frame to the required value itself, clobbering the value you set manually.

For more details, check out the section "Adopting Auto Layout" in Apple's Cocoa Auto Layout Guide.

Solution 2

What turned out to be the easiest thing for me was to remove the view I wanted to move/resize from its superview, set its frame, and then add it back. For example, take a custom UILabel in a UITableViewCell subclass:

[cell.myLabel removeFromSuperview];
cell.myLabel.frame = someFrameIGenerated;
[cell.contentView addSubview:cell.myLabel];

Solution 3

Something that worked for me was simply adding an IBOutlet for my constraints to the view controller, then changing the constant property on the outlet.

For example, to change the x origin of a view, set an outlet from that view's leading constraint to your controller. In other words, create an outlet @IBOutlet var labelXOrigin: NSLayoutConstraint!. Then, when you want to adjust the x position, simply do something like

self.labelXOrigin.constant = 20.0 // Or whatever origin you want to set.

Solution 4

best way to set frame of sub view is viewWillLayoutSubviews method in autolayout projects.

set property translatedAutoresizingMaskIntoConstraints = true this will work after view did appear or tab on button to set, just after view did appear.

Share:
52,446
SirRupertIII
Author by

SirRupertIII

Updated on July 05, 2022

Comments

  • SirRupertIII
    SirRupertIII almost 2 years

    I want to add padding to all of my buttons, so I subclassed UIButton, and among other changes, I wanted to add a fixed padding by using setFrame method. Everything was working, except for setFrame. I checked around, and I found out that if I uncheck "using AutoLayout" on that view, then I can use setFrame, and it works. Is there a way around this? I really want to use autolayout, because it helps in making the app look nice on both iphone 5 and earlier devices. But I also would like to use setFrame in my subclass, to make my life a litle easier.

    Summing up, my question is: Can I use autolayout and also adjust the frame of a UIView programmatically?

  • Andrew
    Andrew over 10 years
    No problem. Although I feel obligated to tell you I ended up getting rid of this and actually learning how to use constraints in IB. I had no idea they existed, and making complex layouts now seems much more reasonable.
  • Marky
    Marky over 10 years
    Yeah I tried that, but couldn't get constraints working on a table header view. Perhaps I should try again.
  • Hussain Mansoor
    Hussain Mansoor over 9 years
    This works but I see lot of warnings in logs. Can anybody suggest what is the proper way to use this approach? Or should I just ignore the warnings? Thanks
  • algal
    algal over 9 years
    If you see warnings of unsatisfiable constraints, those are probably meaningful. If you're seeing them while operating with translatedAutoresizingMaskIntoConstraints=true on a view, then probably what is happening is you already have some constraints (perhaps set in IB) that try to control the frame of that view. Those old constraints on the frame are interacting with the ones auto-generated from the autoresizing mask. In that case, the answer is to remove those old constraints. If you added them in IB just to get IB to shut up at design time, you can make them "placeholders" to lose them.
  • donkey
    donkey about 8 years
    By doing this your removing the constraints which could screw up other things. Be weary.
  • Peter
    Peter almost 8 years
    @algal how can you get rid of the "old" constraints? And make the subview just listen to the setFrame: values, so the two of them won't conflict.
  • bartzy
    bartzy over 7 years
    Is this still working on iOS 10? I'm trying to do it for a child VC in a custom Container VC but it doesn't respect the frame that I've set.
  • Benjohn
    Benjohn over 7 years
    I've noticed that if you're doing this, in many cases, you will want to derive the frames' CGRects from the frames of other views that are using auto layout. In this situation you need to perform the frame calculations after the other views have completed their auto layout pass. In a view controller viewDidLayoutSubviews is the place for this. In a custom view layoutSubviews should be used. In both cases you should put your frame computations after the super call. Possibly it'd be worth adding this to the answer, but I hope this is a useful comment anyway!