So I'm slightly embarrassed to ask this because it seems so rudimentary but ever since beginning iOS development (about a year of self/internet teaching), when not using storyboards, I have relied heavily on frame sizes and used auto layout very sparingly. In Objective C I find myself in situation where I need to use Auto Layout more often but with Swift I can animate a frame's x and y origins, its center, etc... I have never run into a situation where I could not accomplish what I want with frame sizes and pixel locations alone and I have done some fairly complex view work. My question is, what are the benefits of using Auto Layout rather than frame sizes and pixel locations? I would hate to miss out on some great performance benefits due to ignorance and habit. Thanks so much!
tl;dr Learn how to use Auto Layout – it's a huge time saver for real-world apps!
Long answer:
Finally it is up to you if you want to use features like Auto Layout or Size Classes that Apple provides. The main benefit of those is not really performance in terms of UI rendering in the final product. It is the time you need to invest into thinking about all edge cases if you plan to develop an app that works on different screen sizes and orientations.
Let's say you want to do all the work you need to do to support iPhone 4/4s screen size, iPhone 5/5s screen size, iPhone 6/6s (Plus) screen size and iPad screen size plus Portrait and Landscape modes for all the above screen sizes yourself (here's a good overview): Well, there's nothing wrong with it, just go for it. You would then, of course, need to test things very carefully and also always keep this list up to date – in each app you are using your own solution. If that's for you, do it.
But most people want to get their ideas out without struggling with things like different screen sizes too much. You could of course extract your own logic from one app to use it in all of your apps, and that's exactly what Auto Layout and Screen Sizes are about. Simply put, Apple has done all of the work to make your frame-setting work in different screens for you. You just need to use their new vocabulary (Constraints) to make it work.
In other words: It's an abstraction layer on top of handling with the screen rendering logic directly. If you don't like it, let it go. But if you're planning to do some serious apps that should work on different iPhone/iPad generations and also plan to maintain those however, then, please, learn how to do things with Auto Layout and Size Classes. It will save you and all future maintainers quite some time in development.
A good starting point is the docs. Or a tutorial like this one on raywenderlich.com.
Apple says the following about this difficulty themselves (in the docs linked above):
In many ways, programmatically defining a view’s frame provides the most flexibility and power. When a change occurs, you can literally make any change you want. Yet because you must also manage all the changes yourself, laying out a simple user interface requires a considerable amount of effort to design, debug, and maintain. Creating a truly adaptive user interface increases the difficulty by an order of magnitude.
BTW: There's no difference at all in using Auto Layouts or Frames regarding the programming language: Both Swift and Objective-C support both ways perfectly well. It seems you just didn't find out how to change frames in Obj-C yet. ;)
One more thing: You also don't need to use Storyboards to use Auto Layout. You can use Auto Layout from code. See the docs here. There's even plenty of frameworks trying to make this easier (the original APIs from Apple tend to be not very pretty in many terms), I can recommend SnapKit.
Relying on frame sizes equals calculating the sizes. When using percentage or proportions, it could work on different phone models, but still might look sometimes strange on few resolutions. Then you would add another conditional statement because e.g. on iPhone 4s you would want a little bit different layout. Your code is more and more complicated and could lead to the place where any change is a risk. It is doable, but why reinventing the wheel?
Tools like basic constraints replace sometimes really huge logic blocks for layouts. Logic to compress one label when second one is extending means few ifs. Think about 10 labels. With addition of size classes, enabling/disabling constraints, compression & hugging, auto layout has become irreplaceable for me and I would suggest to take a look at it since most of companies are using it, if not in storyboards, then in xibs or in code, but with auto layout.
Auto Layout was developed to allow programmers to write programs on iOS for screens of and views of arbitrary sizes. Previously, for the first four generations of the iPhone, iOS developers could target their designs to one screen size, but as recent history has shown Apple is open to making hardware of different shapes and sizes. Auto layout provides a convenient way of creating UI that can adapt to different screen and view dimensions. Can one develop UI without auto layout that will take advantage of different view dimensions? Yes. But to make truly resizable views with multiple UI elements, generally programmers have to resort to sizing an element situating relative to other UI element, which have themselves had to have been sized and situated. For example:
NSString *nameString = [NSString stringWithFormat:@"%@", self.name];
CGSize nameSize = [nameString sizeWithAttributes:attribs];
[nameString drawAtPoint:CGPointMake(self.bounds.size.width*0.5-nameSize.width*0.5, IMAGE_INSET+4*CONTENT_BUFFER(self.bounds.size.height)+facePicHeight) withAttributes:attribs];
questionFont = [UIFont systemFontOfSize:self.bounds.size.height*0.025];
attribs = @{NSFontAttributeName: questionFont, NSParagraphStyleAttributeName:paragraphStyle};
NSString *maleString = [NSString stringWithFormat:@"%@", self.male];
CGSize maleSize = [maleString sizeWithAttributes:attribs];
[maleString drawAtPoint:CGPointMake(self.bounds.size.width*0.5-maleSize.width*0.5,
IMAGE_INSET+5*CONTENT_BUFFER(self.bounds.size.height)+facePicHeight+nameSize.height) withAttributes:attribs];
NSString *femaleString = [NSString stringWithFormat:@"%@", self.female];
CGSize femaleSize = [femaleString sizeWithAttributes:attribs];
[femaleString drawAtPoint:CGPointMake(self.bounds.size.width*0.5-femaleSize.width*0.5,
IMAGE_INSET+6*CONTENT_BUFFER(self.bounds.size.height)+facePicHeight+nameSize.height+maleSize.height) withAttributes:attribs];
NSString *babyString = [NSString stringWithFormat:@"%@", self.baby];
CGSize babySize = [babyString sizeWithAttributes:attribs];
[babyString drawInRect:CGRectMake(IMAGE_INSET+CONTENT_BUFFER(self.bounds.size.height), IMAGE_INSET+7*CONTENT_BUFFER(self.bounds.size.height)+facePicHeight+nameSize.height+maleSize.height+femaleSize.height, self.bounds.size.width-2.0*(IMAGE_INSET+CONTENT_BUFFER(self.bounds.size.height)), babySize.height) withAttributes:attribs];
The whole thing can get a bit clunky. Obviously some views are simpler than others, but if you have complicated view that you want to support multitasking and localization and other things that are going to affect the layout of your UI it is generally much easier to use auto layout.
iOS Frame-based vs AutoLayout
translatesAutoresizingMaskIntoConstraints
- bridge between Frame-based and AutoLayout.
- By default true - for programmatically created view
- By default false - for IB created view
- true - under the hood creates a set of implicit constraints based on view's frame and autoresizing mask
- false - allows you to apply your own constraints in AutoLayout and prevent conflict(implicit and explicit constraints)
Frame-based
- positioning via (x, y, with, height). There are a lot of calculations and variants in multi-screen situation
let customView = CustomView(frame: CGRect(x: 0, y: 0, width: 50, height: 50))
Enable in Interface Builder: Size Inspector -> Layout -> Inferred (Autoresizing Mask)
Autoresizing Masks
is a view's resizing behaviour based on parent view. Adds some dynamism which based on:
- springs - is a flexible dimension
- struts - is a constant dimenssion
- IB allows you to use struts for external properties, and springs for internal properties
- A programmatic way allow you to use springs(
.flexibleWidth
,.flexibleHeight
,.flexibleLeftMargin
,.flexibleTopMargin
,.flexibleRightMargin
,.flexibleBottomMargin
). But if mask is skipped - it means use appropriate struts instead of it
view.autoresizingMask = [.flexibleRightMargin]
Simple example of Frame-based autoresizing:
let viewA = UIView(frame: CGRect(origin: CGPoint(x: 0, y: 100), size: CGSize(width: 150, height: 300)))
viewA.backgroundColor = .magenta
viewA.translatesAutoresizingMaskIntoConstraints = false
self.view.addSubview(viewA)
let viewB = UIView(frame: CGRect(origin: CGPoint(x: 72, y: 22), size: CGSize(width: 20, height: 40)))
viewB.backgroundColor = .cyan
viewB.translatesAutoresizingMaskIntoConstraints = false
//set autoresizing mask
viewB.autoresizingMask = [.flexibleWidth] //<- comment it to show first screen
viewA.addSubview(viewB)
viewA.frame.size = CGSize(width: 300, height: viewA.frame.height)
First screen dosn't use autoresizingMask
, and the ssecond one usse it as sshowed in the code
Auto Layout
- uses constraints to set relationships between views. View's frame is calculated dynamically based on constraints. It adds extra step which theoretically has an impact on performance but on the other hand is more friendly
let customView = CustomView()
customView.translatesAutoresizingMaskIntoConstraints = false
customView.widthAnchor.constraint(equalToConstant: 50).isActive = true
...
AutoLayout engine
measurement(subView->superView) -> layout(superView->subView)
By default AutoLayout is disabled in InterfaceBuilder and enabled for programmatically created view.
- Enable AutoLayout in code
view.translatesAutoresizingMaskIntoConstraints = false
- Enable AutoLayout in InterfaceBuilder - add constraint
view.translatesAutoresizingMaskIntoConstraints = false // false for autolayout
view.topAnchor.constraint(equalTo: parentView.topAnchor, constant: 16).isActive = true
AutoLayout is used in UITableView
with UITableView.automaticDimension
in func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat
which allow you to don't care of view height.
Also set constraint when view was already added into print view
parentView.addSubview(view)
view.topAnchor.constraint(equalTo: self.topAnchor, constant: 16).isActive = true
or you get
Terminating app due to uncaught exception 'NSGenericException', reason: 'Unable to activate constraint with anchors <> because they have no common ancestor. Does the constraint or its anchors reference items in different view hierarchies? That's illegal.'
© 2022 - 2024 — McMap. All rights reserved.