之前如果做過Web前端頁面的小伙伴們,看到絕對定位和相對定位並不陌生,並且使用起來也挺方便。在IOS的UI設計中也有絕對定位和相對定位,和我們的web前端的絕對定位和相對定位有所不同但又有相似之處。下面會結合兩個小demo來學習一下我們IOS開發中UI的絕對定位和相對定位。在前面的博客中所用到的UI事例用的全是絕對定位,用我們Storyboard拖拽出來的控件全是絕對定位的,就是我們可以同改變組件的frame來改變組件的位置和大小。而相對定位則不同,相對定位是參考組件周圍的元素來確定組件的大小或位置,相對定位即約束和周圍組件的距離來布局的,即layoutConstraint. 在布局中LayoutConstraint和Fram布局方式是不能並存的。
上面說了這么多了,可能說的不太明白,還是那句話,怎么能少的了代碼和實例的支持呢,下面會通過屏幕適配的事例來用絕對布局和相對布局同時實現下面的描述效果。
我們要實現的效果:當上面的view的大小及位置改變時,為了不覆蓋掉下面的view,我們同時要改變下view的位置。 或者說在我們4.0寸正常顯示的內容,在3.5寸屏上也能正常顯示,即通常我們所說的屏幕的適配。為了便於觀察效果,我們可以用Slider控件來動態的改變上面view的大小,觀察下面view的位置變化,下面是我們要實現的效果圖:
用絕對布局來實現上述效果,為了節省我們代碼編寫的時間,上面的控件是通過storyborad來實現的,然后在對應的ViewController里添加組件和控件回調的方法,主要是在slider滑動的時候來獲取slider的值,然后動態的設置上面View的frame坐標(當然,如果讓view往四周擴展得計算一下新的fram的值,然后動態的修改),上面的view位置和大小改變了,那么下面的view不能被上面的覆蓋掉,所以也得修改blackView的fram的值。這種通過修改frame的值的方式來確定組件位置即為絕對布局
下面是由storyboard拖拽過來的屬性:
1
2
3
4
5
6
|
//把最上邊的view拖拽到我們的代碼中
@property (strong, nonatomic) IBOutlet UIView *myView;
//添加slider
@property (strong, nonatomic) IBOutlet UISlider *mySlider;
//添加下面黑色的view
@property (strong, nonatomic) IBOutlet UIView *blackView;
|
下面是當slider的值改變時要回調的方法:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
//當slider的值改變的時候回調的方法
- (IBAction)sliderFunction:(id)sender
{
//獲取slider的當前值(在storyboard設置的范圍為0-120)
double
value = self.mySlider.value;
//獲取myView的位置
CGRect frame = self.myView.frame;
//根據slider的值動態的設置myView的坐標和寬高,設置的時候view中心不變
frame.origin.x = 120-value;
frame.origin.y = 66 * (1-value/120);
frame.size.height = 320-frame.origin.x*2;
frame.size.width = 320-frame.origin.x*2;
//更新myView的位置
self.myView.frame = frame;
//同時改變下面黑色view的坐標
CGRect bf = self.blackView.frame;
bf.origin.y = frame.size.height + frame.origin.y + 30;
self.blackView.frame = bf;
}
|
上面是我們的絕對布局的方式,接下來要學習一下相對布局的方式。相對布局使用起來會比絕對布局要復雜一些,下面先做屏幕適配的例子,圖一是在iPhone的4.0寸的效果圖, 當我們不做任何處理的時候在3.5寸屏上是顯示不出來的如第二張圖:
我們如何讓在3.5寸屏上也顯示正常呢,接下啦就是相對布局出出場的時候了,我們用相對布局的方式把最下面的view的位置改為相對於主視圖的底部和左邊的像素值固定,同時設置slider的位置相對於下面的view的位置相對固定。也就是下面的veiw的位置改變,則上面的slider的位置也會改變,用storyboard修改如下:(第一張圖是修改最下面view的相對位置,第二張圖是設置我們slider為相對布局) ,不需要在ViewController中添加任何動態嗎我們就可以實現屏幕的適配。
那么我如何用相對布局實現上面那種view放大的效果呢,接下來我們需要新建一個工程,因為相對布局和絕對布局在同一個組件中無法並存。在新建工程中用storyboard把我們用到的控件進行拖拽 ,界面和上面的是一樣的。
- (1)首先給我們最上面的View設置相對布局的屬性,如下面的圖一
- (2) 再給黑色的View設置相對布局的屬性,入下面的圖二所示:
- (3) 設置上面兩個View相對中心對齊,選中上面的View,按着Ctrl往下面的View中拖拽,在彈出的框中選中Center X入圖三
給我們相應的組件在storyboard中添加上約束以后,怎樣來動態的改變最上面view的寬和高的約束范圍呢?(即改變水平約束和垂直約束的值)第一部就得把最上面的view的水平約束和垂直約束從我們的storyboard中把最上面View中我們要用的約束拖入到我們的Viewcontroller, 第一張圖是storyboard中約束所在的位置,第二張圖把約束添加到ViewController中。
至此我們用storyboard的工作已經做完,程序員是少不了敲代碼的,也只有正兒八經的敲代碼,程序員才會成長。所以嘍下面就是我們在ViewController中添加的代碼部分。絕對布局直接改frame的坐標值就可以啦,那么在程序中我們如何去動態的改變我們約束的值呢?下面的代碼將會用到。 我們要做的事情就是在ViewController中通過改變slider的值來改變最上面View的水平約束和垂直約束,水平約束和垂直約束的相關變量我們已經拖拽過來了,下面就需要在Slider回調的方法中來改變水平和垂直約束的值。先段代碼,之后在說兩句。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
//slider的值改變調用的方法
- (IBAction)sliderChange:(id)sender
{
//為了避免沖突移除myView的水平和垂直約束,注意是從主視圖上移除,因為約束是加載我們的主視圖上,即相對於我們的主視圖
[self.view removeConstraint:self.widthC];
[self.view removeConstraint:self.heightC];
//獲取slider的值
double
sliderValue = self.mySlider.value;
//由slider的值重設我們的約束值,H代表水平約束, V代表垂直約束
NSString *widthValue = [NSString stringWithFormat:@
"H:[_myView(%lf)]"
, sliderValue];
NSString *heightValue = [NSString stringWithFormat:@
"V:[_myView(%lf)]"
, sliderValue];
//新建約束
NSArray *widthConstraint = [NSLayoutConstraint constraintsWithVisualFormat:widthValue options:0 metrics:nil views:NSDictionaryOfVariableBindings(_myView)];
//給水平約束重新賦值
self.widthC = widthConstraint[0];
//給垂直約束重新賦值
NSArray * heightConstraint = [NSLayoutConstraint constraintsWithVisualFormat:heightValue options:0 metrics:nil views:NSDictionaryOfVariableBindings(_myView)];
self.heightC = heightConstraint[0];
//往主視圖上添加新的約束
[self.view addConstraint:self.widthC];
[self.view addConstraint:self.heightC];
}
|
代碼說明:
- 1.一個組件中只能有一中約束,如在myView中我們已經有一個垂直約束,我們如果再給他添加一個垂直約束的話,那么程序在運行時就會報錯,錯誤內容:“Unable to simultaneously satisfy constraints.……”;
- 2.所以在添加新的約束之前,我們得把之前加在我們組件中相應的約束給去掉;約束是加在我們對應組件的父視圖上,移除也得從組件的父視圖上移除;
- 3.在設置約束的值的時候我們是以字符串的形式把參數傳遞給約束的,如:H:[_myView(200)] H代表水平約束,V代表垂直約束。中括號里是我們要為那個組件添加約束以及約束的值是多少;
- 4.給我們的約束更新我們新建的約束;
- 5.在把更新的約束添加到我們的父視圖上,到此我們就可以實現上面我們上面用絕對布局實現的功能
補充說明:
在絕對布局時我們還可以獲取屏幕的尺寸,通過屏幕的尺寸來計算我們組件所在的位置,主要代碼如下:
1
2
3
4
5
6
|
//獲取屏幕大小
UIScreen *s = [UIScreen mainScreen];
//獲取屏幕邊界
CGRect bounds = s.bounds;
//獲取屏幕的高度
float
height = bounds.size.height;
|
上面的總結暫且這么說吧,是根據筆者自己的理解所總結的內容,不免有偏頗之處,歡迎批評指正,轉載請注明出處。