Laying Out iOS UIs in Code

本文探讨了在iOS开发中放弃使用Interface Builder,转而采用纯代码方式布局UI的优点。这种方式不仅提供了更精确的位置控制,还能实现相对布局,使得UI在不同设备上保持一致的比例。此外,代码布局还简化了版本控制,提高了开发效率。

For most iOS developers, Interface Builder is an incredibly easy WYSIWYG, drag-and-drop editor to lay out UIs. For many, however, Auto Layout (or the springs and struts of yore) only provide a cursory amount of control over placement.

For about a year, I've been laying out all of my UIs purely in code. The benefits are tremendous.

Before I go on, I'd like to preface this by saying that I would highlyrecommend using a category to make positioning in code much easier.

I use UIView+Positioning, which exposes xywidthheight,rightbottomcenterX, and centerY as both setters and getters. Seriously, you will never seldom touch CGRects again.

You have ultimate control over your positioning.

With Interface Builder and Auto Layout, you're basically restricted to point-based layouts.Point-based constraints

You are required to layout your views through their points, so no matter what device is rendering your view, every position is absolute.

You can also define your constraints relative to the containing view, but again those numbers are absolute.

When you lay out UIs in code, you can lay them out relative to each other using percentages and whatever other crazy math you want, to maintain a more consistent and proportional UI between devices.

button.x = self.view.width  * 0.2; // Position the inset at 20% of the width. button.y = self.view.height * 0.2; // Position the inset at 20% of the height.

Layouts are more explicitly defined in code.

Many people have had issues with Interface Builder's hundreds of check boxes that seem to have no default pattern.

For instance, UIWebView defaults to detecting Phone Numbers, but not Links or Addresses, whereas UITextView defaults to detecting only Links.

When you define these in code, these defaults are more explicit.

[webView setDataDetectorTypes:UIDataDetectorTypeAll];

IBOutlets are too tightly coupled.

If you create lots of small helper views like I do, you have to connect their IBOutlets individually to all of your classes that need access to their properties.

And lord help you if you try to delete an IBOutlet declaration before clearing out the IBOutlets in Interface Builder. The compiler frowns on that.

Version Control with nibs is nigh-impossible.

Since Apple moved to the new xib format, version control has improved dramatically, but it's still very difficult.

Interface Builder documents are now stored as straight XML data, which includes unnecessary data like window positioning, for some reason. This has the weird side-effect of altering the file upon viewing which is some quantum insanity that I don't fully understand. This makes version control especially difficult, because that means some innocuous action like looking up a view's positioning makes uncommitted changes to your xib file.

When you lay out your UIs in code, UI changes are just regular old code changes, which makes git very happy.

Interface Builder is slow

Everyone who's dealt with Interface Builder understands this one. Just opening a xib takes at least 5 seconds and completely halts Xcode until it's loaded.

I'd estimate I've spent an hour of my life waiting for Interface Builder to load.

Even worse is when you accidentally click on a xib when you mean to click a class. Loading the class takes a quarter of a second, but now you must stop what you're doing and wait for Xcode to load that xib before you can click the class.

It's even worse with storyboards, because that means it has to load the xibs for an entire application's View Controller hierarchy.

Programmatically laying out UIs is not difficult.

It'll take a little bit of practice laying out a few UIViews, watching how their properties interact, before it becomes second-nature. There are a few small quirks, though.

  • The main view property of UIViewController is not final inviewDidLoad.

    • You'll need to adjust frames in the ViewController'sviewDidLayoutSubviews method.

    • EDIT 1: If you're strictly following MVC (which you should be) you should do these layout calls in UIView'slayoutSubviews method instead. Thanks, James Prower.

  • In iOS 7, the navigation bar no longer adjusts the frame of the main view property.

    • You'll need to look for the topLayoutGguide andbottomLayoutGuide properties on UIViewController. They have a property called length that will tell you (inviewDidLayoutSubviews) where the bottom of the navigation bar (or top of the tab bar) is. So lay out your views relative to [top | bottom]LayoutGuide.lengthinstead of 0.0.

  • Make heavy use of siteToFit on UIViews.

    • This will resize your UIViews to just enclose their subviews, no matter what. This is incredibly convenient for positioning views with multiple subviews and making sure they're entirely consistent.

I'm confident that once you make one programmatically-laid-out UI, you won't want to touch Interface Builder again.


转载于:https://my.oschina.net/w11h22j33/blog/205788

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值