===
1. autoResizing
autoresizing是蘋果早期的ui布局适配的解決辦法,iOS6之前完全可以勝任了,因為蘋果手機隻有3.5寸的螢幕,在加上手機app很少支援橫屏,是以iOS開發者基本不用怎麼适配布局,所有的ui控件隻要相對父控件布局就可以了,沒錯autoResizing就是一個相對于父控件的布局解決方法;注意:它隻能相對父控件布局;
在xcode中可以通過可視化的界面調整也可以通過代碼去控制
在用autoResizing的時候需要關閉autoLayout和sizeclass(如果是用xcode6)
他們之間是互相沖突的
可以通過圖檔看到autoResizing通過可視化能調整的隻有6根線剛好和它的6個枚舉值對應
typedef NS_OPTIONS(NSUInteger, UIViewAutoresizing) {
UIViewAutoresizingNone = 0,
UIViewAutoresizingFlexibleLeftMargin = 1 << 0,
UIViewAutoresizingFlexibleWidth = 1 << 1,
UIViewAutoresizingFlexibleRightMargin = 1 << 2,
UIViewAutoresizingFlexibleTopMargin = 1 << 3,
UIViewAutoresizingFlexibleHeight = 1 << 4,
UIViewAutoresizingFlexibleBottomMargin = 1 << 5
};
外邊的4根線用來設定目前view距離父控件的上、下、左、右的距離是否固定;
内部的兩根線來設定view是否跟随父控件來自适應width和height;
代碼則可以通過view.autoresizingMask = ...來設定autoResizing值;
autoResizing的功能僅此而已;顯然不夠用;
舉例:
1:讓兩個等寬等高的view之間的間距永遠固定,如圖的紅色view和藍色view,想讓它們之間的距離固定通過autoResizing是不行的;因為autoResizing是相對父控件進行布局的,不可以在兩個兄弟view之間建立布局關系;
可以看到一個橫屏和豎屏就已經不能滿足需求了,更别說螢幕尺寸還要變大
當然你有可能通過其他複雜的輔助手段實作,但是很麻煩;因為不僅僅是橫豎螢幕;螢幕尺寸也要變了;
autoResizing就到此為止,顯然它已近過時,了解一下就可以了;
2. autoLayout iOS6之後
做蘋果開發的一個好處是有一個很好的東家,蘋果公司,他不僅很注重使用者體驗,而且還不忘為開發者去除一些不必要的麻煩(例如:ARC的出現...)
autoLayout:可以在任意兩個控件之間建立布局關系,可以是父子view也可以是兄弟view;功能強大了許多,當然學習成本也高了不少;
如圖:
為了友善了解先勾選autoLayout就可以了,sizeclass先别勾了(如果是xcode6)
autoLayout的設定功能就上圖中下方的紅色方框中;
左起第一個:
通過圖中紅色框圈住的地方可以看出來,該處功能是設定多個view之間的對齊方式,是以設定的時候要同時選中多個View進行設定
**第二個: **
這一部分相當于是一個autoResizing,強大之處在于可以是任意兩個view的相對布局,可以設定距離父控件的上下左右位置(紅色框),還有自身的寬高,還可以相對其他控件設定寬高(藍色框)
第三個:
這部分是用來添加、删除、和更新限制的,上半部分是對于選中view的限制更新,下邊是容器中得所有view的限制
除了上邊,還可以通過control按鍵配合拖線來做autoLayout,如圖:
例子:用autolayout完成剛才autoResizing不能完成的任務,這裡繼續加大難度,除了讓紅色view和藍色view等寬等高,而且距離始終保持不變以外,還要讓兩個view整體垂直居中于螢幕;先看最終的效果圖
橫屏效果
可以看到不管是橫屏豎屏還是大屏小屏,都是沒問題的,到中分線的距離固定且相等;
下邊開始一步一步通過autoLayout完成
第一步:
先讓兩個view等高等寬, 在xcode中同時選中紅色view和藍色view,勾選等寬,等高;
這裡也可以用另外一種方式:按住control進行拖線,為了可讀性和看着直覺就不用了;
這時請注意看xcode得左上角,會有紅色的箭頭出現,表示添加的限制不完整,先說明:限制的完整性,一個view在視圖中的位置相對固定(寬高和相對位置),那麼限制就完整,注意是
相對固定
; 而這裡我們隻是讓兩個view等寬和等高,位置在哪裡并沒有說明,限制當然不完整;是以有紅色的錯誤;
知道紅色的報錯箭頭正常之後,我們繼續,添加的每一條限制都是一個實體,可以在view中看到,點選每條限制可以在xcode的右側編輯欄中看到限制的線性公式,這個線性公式正是蘋果工程師的智慧結晶,巧妙的運用數學的智慧值得我們去學習和體會;
順着這幾個文本框從上往下讀可以得到如下的線性公式(Priority優先級不算入公式):
view.width Equal view.width * 1 + 0
化解得:
view.width = view.width
就這樣實作了兩個view之間的width關系的描述,将這個公式轉化成代碼應該很easy吧.. 哈哈
說明:這裡我們檢視的是width限制,是以是view.width,兩個view可以分别認為是紅色view和藍色view,可以互相對調位置
也許上邊不倫不類的公式很難了解,那麼假設紅色view的寬度為x, 藍色view的寬度為y,對于任意兩個實數x,y,都可以用下面的線性公式(或者說是一次線性函數)表示他們之間的關系:
y = k·x + b
其中k是系數(就是上圖中的Multipliter), b是常數(是上圖中的Constant);
就是這個國小已經學習過的一次函數,可以滿足任何兩個view的任何邊距關系;
第二步:
了解這些之後,回到正題,剛才隻是添加了兩個view等寬等高,接下來,為了保證不會錯亂,我們可以一個一個的來完成限制,這裡先固定紅色的view:
這裡采用control拖線的方式:
這裡我們讓紅色view和藍色的centerY到父view的centerY都是70,這樣操作後相當于兩個view在豎直方向上的位置固定;水準方向和每個view的快高具體是多少都還沒固定;是以xcode左上角繼續報紅色錯誤
第三步:
固定水準位置和寬高,這裡固定寬高,隻需要固定其中的一個即可,因為兩個view的寬高相同;
可以看到添加完後紅色的箭頭變成了黃色,這表示,限制完整隻差一步update了,這是你可以通過點選黃色箭頭update或者如圖:
好了,到此就搞定了;這個看似隻有兩個view,但是這個需求已經是相對複雜了;
案例二:
通過上邊的案例一可以看出autoLayout可以設定任意兩個view之間的限制,可以說能實作任何想要布局;
下邊的案例:讓四個view始終等分螢幕:
效果圖(為了說明效果,中間留了一個像素的間距):
思路:先固定好其中的一個,然後其他view和這個view等寬等高,然後設定相對父控件的限制;
代碼實作autolayout
每一條限制都是一個:NSLayoutConstraint對象
NSLayoutConstraint *layout = [NSLayoutConstraint constraintWithItem:(id)
attribute:(NSLayoutAttribute) relatedBy:NSLayoutRelationEqual toItem:(id)
attribute:(NSLayoutAttribute) multiplier:(CGFloat) constant:(CGFloat)];
[self.view addConstraint:layoutConstraint]
該方法正是剛才線性公式 y = k·x + b 的代碼表示;是以雖然很長,但是很容易讀懂;最後要記得把限制添加到view上,添加的時候必須要注意如果這個限制是父子view關系,限制必須加在父view上邊;
但是用代碼去實作autolayout實在是麻煩,剛才的兩個例子,每個例子中得限制有15個以上,如果用代碼實作,要建立15個以上的NSLayoutConstraint對象,還有弄清楚關系,不能寫錯;蘋果又為開發者考慮到了這一點;于是推出了為了寫autolayout友善的VFL語言;
VFL
嚴格來講不算語言,一中文法類似正規表達式的專門用來寫autolayout的;但是vfl并沒有帶來簡化,其繁瑣程度基本上是寫一行就不想寫第二行的程度:
NSString *vfl1 = @"|-hPadding-[_headerL]-hPadding-|";
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:vfl1
options:0 metrics:metrics views:dict1]];
和正則用法差不多,先用字元串寫一些比對規則,然後就調用後邊的方法去解析這個規則到view上;我個人一直認為程式設計語言應該從簡,将複雜的東西屏蔽掉,使我們開發起來更高效,也有更多時間去處理好業務; 畢竟我們每天做的情就是讓使用者簡單起來,把複雜留給自己;是以vfl不用也罷;
一個label用autoLayout限制,會自動計算好寬高,并且上邊和下邊不會留白白;
3. 蘋果界面設計的統一:sizeClass
與其說科技的發展,拉近了空間中任意兩點的距離,讓交流、資訊傳遞更加便捷;倒不如說由于交流和資訊傳遞的需求更加迫切而推動了科技的進步;大屏顯然是一典型的例子,螢幕尺寸的相對增大,一定程度上友善了交流和資訊傳遞,反之,相對小的螢幕對資訊傳遞會有一定的局限;是以蘋果推出大螢幕的手機也是人類進步的需要,并不是什麼跟風,扯淡結束;
螢幕大了,尺寸多了,帶給開發者的自然是适配方面的工作量和思考;正如大家知道的那樣;蘋果是一家最具追求的公司,他當然會推出可行的解決方案就是sizeClass;
sizeClass: 對螢幕尺寸進行了抽象:不在拘泥于具體尺寸;因為尺寸一直都在變化,我們如果按照尺寸去做适配,一定會很累的;
sizeClass針對iOS裝置的螢幕進行了抽象分類:
- compact (緊湊-小)
- Any (任意)
- Regular (寬松-大)
總結幾點:
- sizeClass隻是對螢幕進行了抽象分類;具體做螢幕的适配還得用autoLayout;
- 沒有了橫豎屏的概念,也沒有了具體尺寸,不用在去談具體的iphone5還是ipad air;
- 把高度和寬度都抽象為上邊的3種,3*3也就是總共9種類型;是9種類型,不是9種螢幕尺寸;
這樣做的結果就是你可以做好一個interface builder适配,然後不管在iphone還是ipad中都可以用了;
這就是蘋果的意願;打開xcode如果建立一個universal項目,在xcode6之前會預設有兩個storyboard,一個是iphone的,一個是ipad版本的;xcode6之後隻有一個,并且是正方形的,也就是說不管你做那種螢幕尺寸的app(無論是ipad還是iphone),都隻用這一個storyboard就可以了;
了解3種抽象:
xcode中得樣子:
具體來看:
圖中9個格子代表 3*3的9中抽象;具體每種代表了那些含義可以選中看看;
比如iphone的豎屏它是這樣抽象的:compact width * regular height
通過sizeclass對螢幕進行分類,然後用autolayout去适配布局,可以說能實作任何想要的效果,并且不用區分裝置而做不同的IB了,一個IB全部搞定,不管是iphone還是ipad;
案例:
有個view 100 * 100
,在iphone豎屏時居于左上角,橫屏時在右下角,ipad中在正中間;
像這種過去看來變态的要求,在現在來說小菜一碟;
-
步驟一:處理豎屏情況
先用sizeclass固定螢幕為iphone豎屏:compact Width | Regular Height
然後添加view到ib上,用autolayout固定尺寸100 * 100;并且在左上角;這裡讓它居左20,居上20;然後update frame即可
後邊的同了解決,隻不過要選對正确的sizeclass
iOS8加了sizeclass後,控件也多了個屬性,在storyboard上托個label出來(以label為例),選中,在右邊的菜單區域可以看到:installed,這個是用來控制改控件什麼情況下顯示,目前什麼都沒限制,表示Any * Any,就是不管是iphone什麼尺寸還是ipad什麼尺寸都可以顯示,點選左邊的小加号
+
可以用sizeclass控制什麼情況顯示;同樣的還有字型、圖檔顯示;
在不同的螢幕下顯示不同的字型:
... 類似的功能還有很多
這個功能的思想和UIButton的三種狀态(normal, highlighted, selected)類似,隻不過這裡有9種狀态;
xcode6預覽
當螢幕種類變的越來越豐富的時候,如果要檢視不同螢幕之間的适配情況,在過去是要不停的切換模拟器,然後運作看效果的,而切換模拟器是很耗時的一件事;xcode6之後,我們可以不用運作直接檢視适配的情況;
對于剛才的事例進行如圖操作即可不運作檢視适配效果;
代碼預覽 @IBDesignable和@IBInspectable
這兩個屬性是xcode6新出的特性
如果不是通過IB建立view,而是通過代碼建立的View,如何在不運作程式的情況下實時的渲染在IB上呢,就是通過@IBDesignable和@IBInspectable;@IBDesignable告訴Interface Builder這個類可以實時渲染到界面中,但是這個類必須是UIView或者NSView的子類。通過@IBInspectable可以定義動态屬性,即可在attribute inspector面闆中可視化修改屬性值。
示例(swift編寫): 這個例子自定義一個UIView的子類,該子類擁有一個UIButton
@IBDesignable
class MyCustomView: UIView {
@IBInspectable var buttonTitleColor: UIColor! // button title color
@IBInspectable var buttonTitle: String! // button title
@IBInspectable var buttonFrame: CGRect! // button frame
var myButton: UIButton!
override init(frame: CGRect) {
// init stored properties
buttonTitleColor = UIColor.redColor()
buttonTitle = "我是按鈕"
buttonFrame = CGRectMake(0, 0, 100, 50)
myButton = UIButton(frame: buttonFrame)
myButton.setTitleColor(buttonTitleColor, forState: .Normal)
myButton.setTitle(buttonTitle, forState: .Normal)
// call super initializer
super.init(frame: frame)
// add button to self
addSubview(myButton)
}
required init(coder aDecoder: NSCoder) {
// init stored properties
buttonTitleColor = UIColor.redColor()
buttonTitle = "button title"
buttonFrame = CGRectMake(0, 0, 100, 50)
myButton = UIButton(frame: buttonFrame)
myButton.setTitleColor(buttonTitleColor, forState: .Normal)
myButton.setTitle(buttonTitle, forState: .Normal)
// call super initializer
super.init(coder: aDecoder)
// add button to self
addSubview(myButton)
}
override func layoutSubviews() {
// refresh button state through attribute inspector
myButton.setTitleColor(buttonTitleColor, forState: .Normal)
myButton.setTitle(buttonTitle, forState: .Normal)
}
}
該類在界面上加了一個Button,并且添加了三個屬性,顔色、title、frame;這三個屬性都是用來描述button的,并且在前邊都加上@IBInspectable表示能夠在IB中實時預覽,不用運作程式啟動模拟器即可;
同時還重寫drawRect方法畫了一個矩形;
這個時候打開storyboard,添加一個View(為了控制gif圖的大小,圖中的View事先加好了限制,并且背景色改為藍色,友善識别);修改class為MyCustomView;預覽就會出現;同時我們将開始畫得矩形改為橢圓,然後再次檢視storyBoard,預覽已經變為橢圓,真是太爽了,不用在浪費時間等待模拟器啟動去觀察UI的布局了;
各種蘋果裝置的尺寸和分别率
一圖勝千言