UIView的Margins

寫在前面

之前使用Storyboard拖拽約束時佛嬉,可以看到比較的view有margin選項,來支持相對某view的margin進行布局。

storyboard-constraints-margin.png

那么在代碼中如何體現(xiàn),就需要UIView的以下API:

  • layoutMargins
  • directionalLayoutMargins
  • preservesSuperviewLayoutMargins

iOS11引入了Safe Area的概念诬乞,相應對UIView的Margin也增加以下API:

  • insetsLayoutMarginsFromSafeArea

layoutMargins

這個屬性用于指定視圖和它的子視圖之間的邊距。和preservesSuperviewLayoutMargins一起在iOS8開始引入。

AutoLayout中NSLayoutAttribute的枚舉值也有相應的更新:

    NSLayoutAttributeLeftMargin NS_ENUM_AVAILABLE_IOS(8_0),
    NSLayoutAttributeRightMargin NS_ENUM_AVAILABLE_IOS(8_0),
    NSLayoutAttributeTopMargin NS_ENUM_AVAILABLE_IOS(8_0),
    NSLayoutAttributeBottomMargin NS_ENUM_AVAILABLE_IOS(8_0),
    NSLayoutAttributeLeadingMargin NS_ENUM_AVAILABLE_IOS(8_0),
    NSLayoutAttributeTrailingMargin NS_ENUM_AVAILABLE_IOS(8_0),
    NSLayoutAttributeCenterXWithinMargins NS_ENUM_AVAILABLE_IOS(8_0),
    NSLayoutAttributeCenterYWithinMargins NS_ENUM_AVAILABLE_IOS(8_0),

在VFL(Visual Format Language)語法中也有相應的引入震嫉,比如“|-[subview]-|”森瘪,設置Margin約束。

子視圖采用上面的約束與父視圖建立約束時票堵,父視圖的layoutMarigin才會生效扼睬。

場景一:blueView占據(jù)全屏,它的子視圖orangeView相對它的margin布局

    UIView *blueView = [[UIView alloc] init];
    blueView.backgroundColor = [UIColor blueColor];
    blueView.translatesAutoresizingMaskIntoConstraints = NO;
    [self.view addSubview:blueView];
    [blueView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.edges.mas_equalTo(self.view);
    }];
    
    UIView *orangeView = [[UIView alloc] init];
    orangeView.backgroundColor = [UIColor orangeColor];
    orangeView.translatesAutoresizingMaskIntoConstraints = NO;
    [blueView addSubview:orangeView];
    [orangeView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.top.mas_equalTo(blueView.mas_topMargin);
        make.bottom.mas_equalTo(blueView.mas_bottomMargin);
        make.left.mas_equalTo(blueView.mas_leftMargin);
        make.right.mas_equalTo(blueView.mas_rightMargin);
    }];

    blueView.layoutMargins = UIEdgeInsetsMake(50, 50, 50, 50);

效果:

layoutMargin.png

可以看到orangeView相對上下左右有個比較大的margin悴势。(這里肉左右下的margin是50窗宇,但是肉眼看距離上面的margin似乎要比左右下的margin大,這是因為iOS11的Safe Area瞳浦,下面會講到)

除了layoutMargins担映,iOS11還增加了一個新屬性directionalLayoutMargins,這個屬性的類型不是UIEdgeInsets叫潦,而是NSDirectionalEdgeInsets,定義如下:

typedef struct UIEdgeInsets {
    CGFloat top, left, bottom, right;
} UIEdgeInsets;

typedef struct NSDirectionalEdgeInsets {
    CGFloat top, leading, bottom, trailing;  
} NSDirectionalEdgeInsets API_AVAILABLE(ios(11.0),tvos(11.0),watchos(4.0));

從結構上看主要是將UIEdgeInsets結構的left和right調(diào)整為NSDirectionalEdgeInsets結構的leading和trailing官硝。這一調(diào)整主要是為了Right To Left(RTL)語言下可以進行自動適配矗蕊。

preservesSuperviewLayoutMargins

當這個屬性的值為YES的時候,一個視圖布局內(nèi)容時其父視圖的margins也會被考慮在內(nèi)氢架。默認是NO傻咖。

場景二:blueView占據(jù)全屏,它的子視圖orangeView相對blueView的邊距布局岖研,orangeView的子視圖redView相對orangeView的margin布局卿操。

    UIView *blueView = [[UIView alloc] init];
    blueView.backgroundColor = [UIColor blueColor];
    blueView.translatesAutoresizingMaskIntoConstraints = NO;
    [self.view addSubview:blueView];
    [blueView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.edges.mas_equalTo(self.view);
    }];
    
    UIView *orangeView = [[UIView alloc] init];
    orangeView.backgroundColor = [UIColor orangeColor];
    orangeView.translatesAutoresizingMaskIntoConstraints = NO;
    [blueView addSubview:orangeView];
    [orangeView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.left.right.top.mas_equalTo(blueView);
        make.width.mas_equalTo(blueView);
        make.height.mas_equalTo(blueView).multipliedBy(0.5);
    }];
    
    UIView *redView = [[UIView alloc] init];
    redView.backgroundColor = [UIColor redColor];
    redView.translatesAutoresizingMaskIntoConstraints = NO;
    [orangeView addSubview:redView];
    [redView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.top.mas_equalTo(orangeView.mas_topMargin);
        make.bottom.mas_equalTo(orangeView.mas_bottomMargin);
        make.left.mas_equalTo(orangeView.mas_leftMargin);
        make.right.mas_equalTo(orangeView.mas_rightMargin);
    }];

    blueView.layoutMargins = UIEdgeInsetsMake(50, 50, 50, 50);
    orangeView.preservesSuperviewLayoutMargins = YES;
    orangeView.layoutMargins = UIEdgeInsetsZero;

效果:

preservesSuperviewLayoutMargins-1.png

其中orangeView.preservesSuperviewLayoutMargins = YES;就設置了orangeView保持父視圖的layoutMargins,下面沒有保持父視圖的邊距是因為孙援,orangeView不在父視圖的bottomMargin內(nèi)害淤。

如果把orangeView.preservesSuperviewLayoutMargins = YES;這句代碼去掉或者設置為NO,效果如下:

preservesSuperviewLayoutMargins-2.png

這里忽略上邊距(iOS11 Safe Area引起拓售,后面講)窥摄,發(fā)現(xiàn)redView的左右邊距不再相對爺爺視圖,也就是blueView的Margin對齊了础淤,這是因為視圖orangeView沒有保持blueView的layoutMargin崭放。

接下來我們把代碼修改為:

     blueView.layoutMargins = UIEdgeInsetsMake(50, 50, 50, 50);
    orangeView.preservesSuperviewLayoutMargins = YES;
    orangeView.layoutMargins = UIEdgeInsetsMake(0, 50, 0, 0);

也就是我們想要orangeView保持父視圖的Margin的基礎上,增加自己的左Margin鸽凶,效果如下:

preservesSuperviewLayoutMargins-3.png

發(fā)現(xiàn)效果和orangeView沒有自己的Margin時一樣币砂,這是因為視圖本身的Margin和從父視圖保持來的Margin是重合的。也就是說preservesSuperviewLayoutMargins=YES時玻侥,真正layoutMargins的值是决摧,“手動設置layoutMargins值”與”在父視圖Margin范圍內(nèi)區(qū)域“的最大值,偽代碼表示如下:

layoutMargins = Max(self.layoutMargins, Combine(self.superview.layoutMargins, self.frame));

iOS11的insetsLayoutMarginsFromSafeArea

從iOS 7以來,我們在整個操作系統(tǒng)中都有這些半透明的bars蜜徽,蘋果鼓勵我們通過這些bars繪制內(nèi)容祝懂,我們是通過viewController的edgesForExtendedLayout屬性來做這些的。

iOS11的Safe Area的出現(xiàn)拘鞋,很快將iOS7出現(xiàn)的topLayoutGuide砚蓬、bottomLayoutGuide廢棄。Safe Area定義了view中可視區(qū)域的部分盆色,保證不被系統(tǒng)的狀態(tài)欄灰蛙、或父視圖提供的view如導航欄覆蓋。

UIView的safeAreaInsets屬性反映了一個view距離該view的安全區(qū)域的邊距隔躲。對于一個Controller的根視圖而言摩梧,SafeAreaInsets值包括了被statusbar和其他可視的bars覆蓋的區(qū)域和其他通過additionalSafeAreaInsets自定義的insets值。對于view層次中得其他view宣旱,SafeAreaInsets值反映了view被覆蓋的部分仅父。如果一個view全部在它父視圖的安全區(qū)域內(nèi),則SafeAreaInsets值為(0,0,0,0)浑吟。

說了這么多終于到insetsLayoutMarginsFromSafeArea了笙纤,從名字就可以看出來它和layoutMargins和safeAreaInsets有一定聯(lián)系。我們通過下面的場景來證明一下:

我們想要看一個view在真正布局時的safeAreaInsets值和layoutMargins值组力,這樣寫一個UIView的子類TestView省容,重寫父類的layoutSubviews方法,打印出這兩個值燎字,并把上面的三個視圖改成TestView的實例:

- (void)layoutSubviews
{
    [super layoutSubviews];
    NSLog(@"%@", self);
    NSLog(@"safeAreaInsets : %@",  [NSValue valueWithUIEdgeInsets:self.safeAreaInsets]); //safeAreaInsets是iOS11才有的屬性腥椒,注意使用時判斷系統(tǒng)版本
    NSLog(@"layoutMargins : %@",  [NSValue valueWithUIEdgeInsets:self.layoutMargins]);
}

將上面三個視圖的layoutMargin設置為:

    blueView.layoutMargins = UIEdgeInsetsMake(50, 50, 50, 50);
    orangeView.layoutMargins = UIEdgeInsetsMake(0, 50, 0, 0);

控制臺打印:

//blueView
safeAreaInsets : UIEdgeInsets: {20, 0, 0, 0}
layoutMargins : UIEdgeInsets: {70, 50, 50, 50}

//orangeView
safeAreaInsets : UIEdgeInsets: {20, 0, 0, 0}
layoutMargins : UIEdgeInsets: {20, 50, 0, 0}

//redView
safeAreaInsets : UIEdgeInsets: {0, 0, 0, 0}
layoutMargins : UIEdgeInsets: {8, 8, 8, 8}

打印的layoutMargin和設置的layoutMargin值不一樣候衍,視圖真正顯示時的layoutMargin其實是設置的layoutMargin和safeAreaInsets的累加笼蛛。那么跟insetsLayoutMarginsFromSafeArea屬性有什么關系呢,這個值默認是YES脱柱,我們把它設置為NO:

    blueView.layoutMargins = UIEdgeInsetsMake(50, 50, 50, 50);
    orangeView.layoutMargins = UIEdgeInsetsMake(0, 50, 0, 0);
    orangeView.insetsLayoutMarginsFromSafeArea = NO;

控制臺打臃サ:

//orangeView
safeAreaInsets : UIEdgeInsets: {20, 0, 0, 0}
layoutMargins : UIEdgeInsets: {0, 50, 0, 0}

發(fā)現(xiàn)safeAreaInsets不再累加到layoutMargins上了,所以insetsLayoutMarginsFromSafeArea屬性也很簡單榨为,就是控制safeAreaInsets是否加到layoutMargins上惨好。

另外,從打印結果看随闺,safeAreaInsets的值就是status bar的高度日川,也說明了我們之前的效果上面的邊距要多出一點的原因。

總結

本文主要講了UIView關于Margin的以下屬性:

  • layoutMargins: iOS8開始引入矩乐,用于指定視圖和它的子視圖之間的邊距龄句。
  • directionalLayoutMargins:iOS11開始引入回论,可以根據(jù)語言的方向進行前后布局,與layoutMargins相比分歇,能更好的適配RTL語言傀蓉。
  • preservesSuperviewLayoutMargins:iOS8開始引入,當這個屬性的值為YES的時候职抡,一個視圖布局內(nèi)容時其父視圖的margins也會被考慮在內(nèi)葬燎。默認是NO。
  • insetsLayoutMarginsFromSafeArea:iOS11開始引入缚甩,控制safeAreaInsets是否加到layoutMargins上谱净。默認YES。

他們之間的關系:

第一步:一個視圖“真正的layoutMargins”是否受父視圖的layoutMargins影響擅威,取決于preservesSuperviewLayoutMargins值壕探,如果NO,則不考慮父視圖layoutMargins郊丛,如果YES李请,受影響值為視圖在父視圖的margin的區(qū)域,然后取“設置的layoutMargins”與“在父視圖的margin區(qū)域”的最大值宾袜。

第二步:再判斷視圖是否受Safe Area影響捻艳,判斷insetsLayoutMarginsFromSafeArea值,如果NO庆猫,直接使用,如果YES绅络,則將從上面得到的layoutMargins加上safeAreaInsets(注意這里是加上月培,前面與父視圖margin的影響區(qū)域是取最大值),得到最終真正的layoutMargins恩急。

偽代碼表示如下:

- (UIEdgeInsets)getRealLayoutMargins {
    UIEdgeInsets layoutMargins = UIEdgeInsetsMake(8, 8, 8, 8);  //默認是8

    if (self.preservesSuperviewLayoutMargins) {
        layoutMargins = Max(settingsLayoutMargins, Combine(self.superview.layoutMargins, self.frame));
    }

    if (self.insetsLayoutMarginsFromSafeArea) {
        layoutMargins = Add(layoutMargins, self.safeAreaInsets);
    }

    return layoutMargins;
}

參考

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末杉畜,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子衷恭,更是在濱河造成了極大的恐慌此叠,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,311評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件随珠,死亡現(xiàn)場離奇詭異灭袁,居然都是意外死亡,警方通過查閱死者的電腦和手機窗看,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評論 2 382
  • 文/潘曉璐 我一進店門茸歧,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人显沈,你說我怎么就攤上這事软瞎》昊剑” “怎么了?”我有些...
    開封第一講書人閱讀 152,671評論 0 342
  • 文/不壞的土叔 我叫張陵涤浇,是天一觀的道長鳖藕。 經(jīng)常有香客問我,道長只锭,這世上最難降的妖魔是什么著恩? 我笑而不...
    開封第一講書人閱讀 55,252評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮纹烹,結果婚禮上页滚,老公的妹妹穿的比我還像新娘。我一直安慰自己铺呵,他們只是感情好裹驰,可當我...
    茶點故事閱讀 64,253評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著片挂,像睡著了一般幻林。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上音念,一...
    開封第一講書人閱讀 49,031評論 1 285
  • 那天沪饺,我揣著相機與錄音燕侠,去河邊找鬼团搞。 笑死,一個胖子當著我的面吹牛良姆,可吹牛的內(nèi)容都是我干的讥脐。 我是一名探鬼主播遭居,決...
    沈念sama閱讀 38,340評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼旬渠!你這毒婦竟也來了俱萍?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,973評論 0 259
  • 序言:老撾萬榮一對情侶失蹤告丢,失蹤者是張志新(化名)和其女友劉穎枪蘑,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體岖免,經(jīng)...
    沈念sama閱讀 43,466評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡岳颇,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,937評論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了觅捆。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片赦役。...
    茶點故事閱讀 38,039評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖栅炒,靈堂內(nèi)的尸體忽然破棺而出掂摔,到底是詐尸還是另有隱情术羔,我是刑警寧澤,帶...
    沈念sama閱讀 33,701評論 4 323
  • 正文 年R本政府宣布乙漓,位于F島的核電站级历,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏叭披。R本人自食惡果不足惜寥殖,卻給世界環(huán)境...
    茶點故事閱讀 39,254評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望涩蜘。 院中可真熱鬧嚼贡,春花似錦、人聲如沸同诫。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,259評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽误窖。三九已至叮盘,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間霹俺,已是汗流浹背柔吼。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留丙唧,地道東北人愈魏。 一個月前我還...
    沈念sama閱讀 45,497評論 2 354
  • 正文 我出身青樓,卻偏偏與公主長得像想际,于是被迫代替她去往敵國和親蝌戒。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,786評論 2 345

推薦閱讀更多精彩內(nèi)容