Recently I have been interviewing and it is eye opening to be the other side of the interview table/Zoom chat! Having been an iOS developer for 14+ years, I think there are things you learn very early on in your career that you never question or need to revisit it again and it can catch you a little off-guard. One of those areas that I got asked about recently was about the relationship between the frame
, bounds
and transforms
in a `UIView`.
The frame and bounds Relationship
We all know that with a UIView, the frame
corresponds to the size and location of a view relative to the containing view’s co-ordinate system, while the bounds
represent this relationship to the view’s own co-ordinate system.
However, what is less clear and perhaps something I’d forgotten is that the size property is shared between these two properties. If we change the size of the frame, the bounds size changes to match and vice versa. You can’t have a frame size of 100×100 and a bounds size of 50×50. It’s strictly 1:1.
Something to point out is that changing the bounds size will inset the frame e.g. if we reduce the bounds by half, the origin of the frame will change to allow the view to keep its “center” position.
If we change the origin of the frame, the view moves:
If we change the origin of the bounds, the view “scrolls”. It is basically offsets the start point of the “content” or sub views.
It can be handy to set the clipToBounds property to true and set a layer border to see this behaviour better.
The frame and constraints
Let’s be honest, once you understand the fundamentals of UIKit you tend to get into the following working practice and never need to look back:
- Initialise a view
- Set
translatesAutoresizingMaskIntoConstraints
to false - Add it to a superview
- Add constraints to it
- Call
layoutIfNeeded()
to apply the constraints to set the frame (such as for animation purposes) or wait for such a point to be called in the natural flow of an app (e.g. when a containing view controller resizes a view controller just before pushing on screen).
Very rarely do you need to, or should, touch the frame or bounds since any manually changed values will get replaced as soon as any of the views in the superview hierarchy calls layoutIfNeeded()
. (This is because layoutIfNeeded()
effectively walks through all the sub views attached to a view, re-calculating the frame from the constraints if they differ).
Transforms and the frame
You can apply transforms to a view allowing rotations, translations, and scaling, with very little code. However, I think anyone who has played around with transforms for any amount of time soon notices that they don’t respect constraints. In Apple’s documentation it says “the transform property does not affect Auto Layout”. For all intents and purposes, the transform can be considered to happen right at the very end of the layout process, after the constraints have been applied. You can see this easily by applying translation transforms moving everything to the left 20 points – here we have strict constraints set and the transform easily ignores all of it. Since I don’t have access to the UIKit source code I can’t say for sure, but I believe transforms are possibly applied as GPU instructions using Core Animation to speed up rotation transforms etc. CGAffineTransform is a Core Graphics struct after all!
The Big Question
So it caught me off-guard to be asked what the frame of a view is after a rotation transform has been applied. From a practical sense, the frame is irrelevant – Auto layout and transforms do not affect each other, as stated in the Apple documentation. Does the frame change? I didn’t think so, in my head it’s a graphics accelerated operation that happens after Auto Layout and it had been many years since I had last looked at UIKit fundamentals in detail.
The reality? Well, the interviewer informed me that the transform does indeed change the frame. However his colleague shadowing interjected and pointed out that Apple specify a big red warning on the transform property documentation that unless the standard identity transform is set, all values returned by the frame property are undefined and should be ignored – making the frame change somewhat a moot technical point. A short pause endured before we awkwardly shuffled onto the next question…
Summary
- Constraints and transforms don’t affect each other
- Constraints update the frame before any transforms are applied
- Transforms also update the frame (however you can’t trust the frame property again and should not use it)
- Setting the origin property of a frame, moves the view relative to the superview’s co-ordinates (what we’re use to when setting the frame of a view in a view controller for example).
- Setting the origin property of the bounds offsets where content is laid out or drawn. E.g an origin of {x:20, y:0} will shift everything to the left.
- Setting the size property of a frame updates the bounds size to be the same, e.g. 1 point in the frame co-ordinate space is 1 point in the bound co-ordinate space. Remember, if we have a scale transform applied, we aren’t meant to look at the value in the frame property of the view!
So I actually think it’s interesting to see someone’s thinking and thought process when asking these types of questions however unluckily for me, I feel like the interviewer was just looking for me to say that the frame changed because that is what it said on his interview answer sheet! It was an interesting interview question still though, mostly for revisiting how frame and bounds interact with constraints and transforms along with best working practices.
I am curious though as to why Apple say you shouldn’t use the frame property after setting the transform, if anyone has any theories or knows why, I’d really like to know.