iOS UI佈局簡史

高能提醒:本文內容是個大雜燴,摘抄引用請見文章末尾的參考資料。另外,加了一些本身的看法!html

平常開發中,UI搭建、調試會佔用咱們大部分的時間,以致於移動端開發常常會被調侃爲搭界面的。提升UI佈局技術能夠提升開發效率,把更多的時間放在優化、邏輯方面,而不是被界面業務綁死。react

UI佈局技術概覽

  • nib:nib是NeXT interface builder的英文縮寫,以二進制的形式存儲界面信息,是IB3.0之前的文件格式。android

  • xib:xib是xml interface builder的英文縮寫,是IB3.0以後蘋果公司推出的新一代,以xml格式存儲界面信息,在最終執行前,xib文件會被編譯爲nib文件。。。。。。(請注意大神@史前圖 的評論)ios

  • storyboard:故事版文件,是蘋果最新推出的用於在界面開發中替代xib文件的一種新技術。本質上是一個xml文件的集中管理區,不但能夠描述xib單個界面的結構,還能夠描述界面之間的跳轉及依賴關係。主要是靠手拖,感受像積木玩具。git

  • frame:等效於代碼版的storyboard,但更靈活。目前比較經常使用。若是沒有好的適配方案,是多衆多機型尺寸仍是有點棘手。(歡迎補充最優雅的適配方案教程地址)程序員

  • AutoLayout:自動佈局(AutoLayout)是iOS6發佈的界面佈局技術,該算法的主要思想是:將基於約束系統的佈局規則(本質上是表示視圖佈局關係的線性方程組)轉化爲表示規則的視圖幾何參數。實際上AutoLayout算法自己並不是有🍎發明,只是蘋果用Objective-C去實現了該算法,方便iOS開發者使用。AutoLayout有多種使用方式,如①可視化工具:Xcode的Interface Builder,②純代碼:以Masonry爲表明。更多內容見自動佈局 Auto Layout (原理篇)github

  • FlexBox:彈性佈局(Flexible Box)。對,就是目前Web端最流行的佈局方式(之前是盒子模型),如今APP上也能使用。此方案就擴展出不少技術,如Yoga(最牛逼的表明,Facebook出品,衍生出不少上層方案,如跨平臺的ReactNative、Android的Litho、iOS的Yogakit)、FlexboxLayout(Android表明,Google出品)、FlexLibFLEX等。(歡迎補充)golang

  • swiftUI:官網,蘋果官方推薦。更多內容見蘋果發佈全新 SwiftUI 框架:一次編碼,五端通用objective-c

nib

iOS 開發中,搭建界面的一些爭論算法

xib

iOS 開發中,搭建界面的一些爭論

storyboard

iOS 開發中,搭建界面的一些爭論

frame

經過frame編寫界面,主要難點在於機型適配,並且還比較繁瑣。下面是適配的2個要點:

非劉海機型

很明顯能看出這三種屏幕的尺寸寬高比是差很少的,加上如今都屏幕尺寸都是4.7+,所以常以iPhone 6(s)爲基準,進行等比縮放。在實際中可能整個頁面所有、或部分節點、或僅縮放寬(或高,而後另外一側自適應) 。

// 在AppDelegate.h中
@property float autoSizeScaleX;
@property float autoSizeScaleY;

// 在AppDelegate.m中
#define ScreenHeight [[UIScreen mainScreen] bounds].size.height
#define ScreenWidth [[UIScreen mainScreen] bounds].size.width
 
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    AppDelegate *myDelegate = [[UIApplication sharedApplication] delegate];
   
    if(ScreenHeight > 667){ // 這裏以(iPhone 6)爲準
        myDelegate.autoSizeScaleX = ScreenWidth/375;
        myDelegate.autoSizeScaleY = ScreenHeight/667;
    } else {
        myDelegate.autoSizeScaleX = 1.0;
        myDelegate.autoSizeScaleY = 1.0;
    }
}
複製代碼

iPhone 6屏幕的高度是667,所以當屏幕尺寸大於iPhone 6時,autoSizeScaleX和autoSizeScaleY即爲當前屏幕和iPhone 6尺寸的寬高比。若是手機爲Iphone6那麼屏幕比例爲1,若是爲Iphone6s,屏幕比放大,Iphone5就屏幕比縮小。如今咱們獲取了比例關係後,先來看一下如何解決代碼設置界面時的適配。

CGRectMake(CGFloat x, CGFloat y, CGFloat width, CGFloat height)這個方法使咱們經常使用的設置尺寸的方法,在.m文件中

CG_INLINE CGRect TS_CGRectMake(CGFloat x, CGFloat y, CGFloat width, CGFloat height)
{
    AppDelegate *myDelegate = [[UIApplication sharedApplication] delegate];
    CGRect rect;
    rect.origin.x = x * myDelegate.autoSizeScaleX; 
    rect.origin.y = y * myDelegate.autoSizeScaleY;
    rect.size.width = width * myDelegate.autoSizeScaleX;
    rect.size.height = height * myDelegate.autoSizeScaleY;
    return rect;
}
複製代碼

當咱們使用的時候直接這樣作UIImageView *imageview = [[UIImageView alloc] initWithFrame:TS_CGRectMake(100, 100, 50, 50)]; 這樣咱們得出的就是轉換後的座標了。這個imageview在五、六、6 Plus的位置和尺寸比例都是同樣的。

不止是尺寸的適配,還有文字大小的適配。

#define MainScreenWidth [[UIScreen mainScreen] bounds].size.width

#define font(R) (R)*(MainScreenWidth)/375.0
複製代碼

因此就會常常看到下面的代碼:

self.btnForgetPassWord = [UIButton alloc]initWithFrame:TS_CGRectMake(161, 499, 54, 12);
[self.btnForgetPassWord setFont:[UIFont systemFontOfSize:font(12)]];
複製代碼

總之,媽媽不再用擔憂屏幕的適配了。

劉海機型

劉海比非劉海的區別在於多了一個安全區域SafeArea,因此最簡單的辦法是移除非安全區域的尺寸,而後按照非劉海機型進行適配。

經常使用的方法方案就是宏!

#define K_iPhoneXStyle ( (CGSizeEqualToSize(CGSizeMake(414, 896), [[UIScreen mainScreen] bounds].size)) || ([UIScreen instancesRespondToSelector:@selector(currentMode)] ? CGSizeEqualToSize(CGSizeMake(1125, 2436), [[UIScreen mainScreen] currentMode].size) : NO) )
// 或
#define K_iPhoneXStyle ((KScreenWidth == 375.f && KScreenHeight == 812.f ? YES : NO) || (KScreenWidth == 414.f && KScreenHeight == 896.f ? YES : NO))


// 其餘宏
#define KScreenWidth ([UIScreen mainScreen].bounds.size.width)
#define KScreenHeight ([UIScreen mainScreen].bounds.size.height)
#define K_iPhoneXStyle ((KScreenWidth == 375.f && KScreenHeight == 812.f ? YES : NO) || (KScreenWidth == 414.f && KScreenHeight == 896.f ? YES : NO))
#define KStatusBarAndNavigationBarHeight (K_iPhoneXStyle ? 88.f : 64.f)
#define KStatusBarHeight (K_iPhoneXStyle ? 44.f : 20.f)
#define KTabbarHeight (K_iPhoneXStyle ? 83.f : 49.f)
#define KMagrinBottom (K_iPhoneXStyle ? 34.f : 0.f)

#define KScaleWidth(width) ((width)*(KScreenWidth/375.f))
#define IsIphone6P SCREEN_WIDTH==414
#define SizeScale (IsIphone6P ? 1.5 : 1)
#define kFontSize(value) value*SizeScale
#define kFont(value) [UIFont systemFontOfSize:value*SizeScale]
複製代碼

只考慮大部分狀況其餘哈~~例如橫屏、新分辨率等就另說。

AutoLayout

和frame比起來靈活性有必定加強,將尺寸影響從總體降爲局部,可是在更新時使用起來仍是有必定麻煩,並且新引入優先級的概念。關於更新的幾個方法的區別:

  • setNeedsLayout:告知頁面須要更新,可是不會馬上開始更新。執行後會馬上調用layoutSubviews。
  • layoutIfNeeded:告知頁面佈局馬上更新。因此通常都會和setNeedsLayout一塊兒使用。若是但願馬上生成新的frame須要調用此方法,利用這點通常佈局動畫能夠在更新佈局後直接使用這個方法讓動畫生效。
  • layoutSubviews:系統重寫佈局。
  • setNeedsUpdateConstraints:告知須要更新約束,可是不會馬上開始。
  • updateConstraintsIfNeeded:告知馬上更新約束。
  • updateConstraints:系統更新約束。

更多關於Masonry的使用請移步Masonry使用注意篇

AutoLayout最大的問題在於須要精確的知道每個UI元素的約束關係,當頁面約束很複雜的時候,對程序員的細心和耐心是極大的考驗。然而這根本不算是最嚴重的問題,通常頁面都由若干個組件拼裝而成,AutoLayout最可怕的是不支持熱插拔組件,即不能夠銷燬一個有其餘組件依賴該組件約束的組件,程序員若是想要「刪除」一個組件,須要添加一個引用指向該組件的寬或高,當須要「刪除」時,將該變量設爲0,你覺得就這麼簡單的完了?若是再將該組件「插入」回原處呢?

恰好FlexBox完美的解決了這個問題,請繼續日後看。

我從15年開始轉iOS,當時跳過xib、storyboard,直接學習的用Masonry純代碼式佈局,因此我到如今也不會Interface Builder。Masonry給個人感受是比較枯燥的,沒有所見即所得的體驗,並且一個VC或View裏UI代碼佔用了很大部分。可是熬過初級後,和其餘同事協同開發、靈活性、可控性、抽象複用等方便的體驗是很是棒的!

FlexBox

終於輪到今天的主角了!開心~~有一點Web基礎的能夠去Flexbox佈局詳解學習和練習。

Flexbox解決了什麼?

  • 方向性 (傳統佈局方向是從左到右,從上至下)
  • 彈性伸縮 (傳統尺寸定義是經過像素等來精肯定義)
  • 元素對齊(能夠作到插拔)

從上述幾點看來,它彷佛完美的解決了iOS原生布局開發效率低的問題,但它會增長頁面的嵌套層級關係,在硬件性能飽和的狀況下用空間換取開發效率。

我相信你對它的效率有疑慮!?(大神@史前圖 的評論裏指出AutoLayout的性能已經更新,因此請執行搜索相關最新的資料😄)

這裏根據從AutoLayout的佈局算法談性能裏的測試代碼進行修改,對Frame/AutoLayout/FlexBox進行佈局,分段測算10~350個UIView的佈局時間,取100次佈局時間的平均值做爲結果,耗時單位爲秒。結果以下圖:(測試佈局的項目代碼GitHub地址

雖然測試結果不免有誤差,可是根據折線圖能夠明顯發現,FlexBox的佈局性能是比較接近 Frame的。60FPS做爲一個iOS流暢度的黃金標準,要求佈局在0.0166667s內完成,AutoLayout在超過50個視圖的時候,可能保持流暢就會開始有問題了。本次測試相關配置:Xcode9.2,iPad Pro (12.9-inch)(2nd generation) 模擬器。

Yoga

Yoga最初源自Facebook在2014年的一個開源的CSS佈局開源庫,在2016年通過修改,改名爲 Yoga。它是由C語言實現,基於Flexbox的一個編寫視圖的跨平臺代碼,讓佈局變得更簡單,支持多個平臺,包括Java、C#、C、Objective-C和Swift。

Yoga Layout 官網。庫開發者能夠集成 Yoga 到他們的佈局系統,就如Facebook 已經集成進了它的兩個開源項目:React NativeLithoComponentkit。另外iOS開發者能夠直接用YogaKit來佈局視圖的框架。

React Native

RN就很少說,目前市場上不少APP都在使用,應該說是APP跨平臺開發的不二選擇:

Litho

Litho is a declarative framework for building efficient user interfaces (UI) on Android. It allows you to write highly-optimized Android views through a simple functional API based on Java annotations. It was primarily built to implement complex scrollable UIs based on RecyclerView. With Litho, you build your UI in terms of components instead of interacting directly with traditional Android views. A component is essentially a function that takes immutable inputs, called props, and returns a component hierarchy describing your user interface.

Litho是高效構建Android UI的聲明式框架,經過註解API建立高優的Android視圖,很是適用於基於Recyclerview的複雜滾動列表。Litho使用一系列組件構建視圖,代替了Android傳統視圖交互方式。組件本質上是一個函數,它接受名爲Props的不可變輸入,並返回描述用戶界面的組件層次結構。

Litho是一套徹底不一樣於傳統Android的UI框架,它繼承了Facebook一貫大膽創新的風格,突破性地在Android上實現了React風格的UI框架。架構圖以下:

我對Android開發不熟,因此請移步 基本功 | Litho的使用及原理剖析

YogaKit

iOS端Yoga的上層UI庫Componentkit,可是我看文檔以後以爲很是難用,因此略過。

可是YogaKit還不錯,同時支持OC和Swift,Yoga 教程-使用跨平臺佈局引擎 這份基於Swift的YogaKit教程還不錯。

ps:我本想找一個Masonry的替代者,特別但願能支持Flex佈局,因此抱有很大但願去了解和學習YogaKit,可是失望😞了。第一是代碼繁瑣,第二是YogaKit的使用者很少。我仍是好好研究RN吧~

FlexboxLayout

FlexboxLayout是谷歌出的,只支持Android,因此期待各位大神關於此框架的評論。

順便吐槽一句,google是看着什麼語言和技術火,都會插一腳,而後獨自開發一套。如golang、angular、android studio、dart、flutter等,加上國內技術開發者的google情節,因此大家本身體會。。。除了JS引擎——V8

swiftUI

iOS上的UI,我以爲swiftUI纔是之後的王者!我不接受反駁,哈哈哈哈哈哈...iOS的UI開發語言就objective-c和swift,從編譯型的oc變爲解釋型的swift,從語言角度看跨度仍是挺大的,在衆多技術均在快速發展的現代,解釋型語言更能適應將來,不過swift還未在跨平臺上發揮出任何兩點,就連在iPhone和mac上都沒。

若是沒有swift的從入門到再入門事件,可能swift的市場又是另一番景象!真但願swift 5能穩定下來。

後語

UI的基礎佈局也就那麼回事,都是從圖形繪製OpenGL,到UI Library,再到UI Controls,再到APP,電腦發展到如今一直沒有變過,如Windows上的MFC、iOS上的UIKit、剛出來的flutter等。

此處借鑑TCP/IP的七層模型(我以爲這是世界上最牛逼的架構,沒有之一,不接受反駁!),只有分層以後,不一樣的人員才能扮演不一樣的角色,去完成對應的工做。咱們說的UI是軟件層面的,是運行在硬件上的,因此軟件到硬件的銜接要靠圖形繪製,這個工做是由操做系統去完成。

有了操做系統這個大環境,爲了創建生態,必須對外提供響應的開發套件,如UI庫、網絡庫、驅動庫等,這一點在Windows提現特別明顯。而後開發者纔會加入你的開發陣營,而後你的市場纔會逐步擴大,因此爲啥常常據說一門語言和技術背後,也須要強大的公司和資金來支持,好比微軟、谷歌、亞馬遜、Facebook、衆多第三方廠商的SDK等等,請注意像阿里雲SDK、騰訊SDK等各個廠商也算是這一層的,能夠把Windows、Android、iOS等操做系統看着是一級廠商,把Facebook、阿里雲、騰訊雲等服務提供商是二級廠商。

有了各個等級廠商提供的Library和對應的Control,而後才能更好的創造出百花齊放的APP,纔有咱們這種出現不一樣方向的軟件開發者。因此看待一個新的UI庫或技術,須要從其原理和應用分析,找到其合適的定位,若是你沒有深刻分析背後的技術,是沒法發揮其最大價值的。

就像React Native,你特麼叫我去作IM和多媒體,老子一巴掌給你飛過來!RN它只是寫下降了APP UI的門檻,擴大了其受衆者,就如React的口號『learn once write anywhere』。RN的大體原理是經過JavaScript和JavaScript解釋器,去動態控制原生的UI佈局,和之前有大神經過解析XML去自動生成UI,是同樣的道理,又如Lua這個同JavaScript語法相似的腳本在遊戲行業活得風生水起,RN的熱更新原理就是這麼來的——經過更新JavaScript文件達到更新UI佈局和業務邏輯等目的,因此RN的核心是在具備ES 6/7/8/...的JavaScript和對應的JavaScript解釋器,關於RN中的JavaScript解釋器,能夠看看逆襲Futter? Facebook 發佈全新跨平臺引擎 Hermes!JS引擎大PK:JSC vs V8 vs Hermes

不限於RN,你想要作得更好更優秀,你須要多研究。不要想着你會React網頁,就以爲能夠來作APP,呵呵,一個庫依賴問題就能把你搞死。不是你長的美,就能夠想得美!就比如讓你用openGL的API來畫一個控件,你畫很差,而後就說openGL技術不行?!這時我只能送你一個字,滾!

說了這麼多,我不是但願任一一個UI庫有多牛逼,而是但願FlexBox這個UI規範能在APP端能擴大影響,儘可能和在佈局上更優秀的Web端保持一致,最終實現統一,作到最根本的跨平臺,可是能夠有不少庫或者方案來實現。




2019-07-28 更新: 下面評論 @小二Flutter 提到的MyLinearLayout也很優秀很強大,新舊佈局方案均兼容,各位技術官能夠去嘗試一下,說不定你也喜歡。可是我以爲文檔不是完善,特別是使用方面的,會致使上手難度增大。

下面評論 @史前圖騰 提到的iOS佈局歷史,本文中提到的有些不對,請注意甄別!(我想修改,可是不知道怎麼修改😔)。



參考資料:(排名不分前後)

相關文章
相關標籤/搜索