iOS界面佈局之三——純代碼的autoLayout及佈局動畫

iOS界面佈局之三——純代碼的autoLayout及佈局動畫

1、引言

        關於界面佈局,apple的策略已經趨於成熟,autolayout的優點在開發中也已經展示的淋漓盡致。除了使用storyBoard進行佈局約束的拖拽,有時咱們也須要在代碼中進行autolayout的佈局設置,Masonry庫能夠方便的建立約束屬性,實際上,咱們也沒有必要再使用系統原生的代碼來建立和設置約束,這篇博客只做爲使用的方法備忘。前幾篇佈局介紹的連接以下:app

使用autoresizing進行界面佈局:http://my.oschina.net/u/2340880/blog/423357框架

初識autolayout佈局模型:http://my.oschina.net/u/2340880/blog/423500工具

2、瞭解一個類

        用代碼來作視圖間的相關約束,那麼就必定要將「約束」也進行對象化,在iOS6以後,引入了autolayout這個概念,相應的也增長了NSLayoutConstraint這個對象,這個對象就是專門用來進行約束佈局的設置對象。經過這個對象,咱們能夠設置相似視圖對象之間的間距,約束的寬高,比例等屬性。建立NSLayoutConstraint對象的方法有兩種,下面咱們分別介紹:佈局

一、使用Objective-C風格的方法建立約束對象

        所謂Objective-C風格的方法,就是經過原生枚舉和一些屬性設置來建立NSLayoutConstraint對象。使用NSLayoutConstraint類的以下方法:動畫

+(instancetype)constraintWithItem:(id)view1 attribute:(NSLayoutAttribute)attr1 relatedBy:(NSLayoutRelation)relation toItem:(nullable id)view2 attribute:(NSLayoutAttribute)attr2 multiplier:(CGFloat)multiplier constant:(CGFloat)c;

先來介紹下方法中的參數都是什麼意義,咱們應該怎麼用他們:spa

view1:要添加約束的視圖對象。.net

attr1:要約束的對象屬性,這個就是一些枚舉,以下:設計

typedef NS_ENUM(NSInteger, NSLayoutAttribute) {
    NSLayoutAttributeLeft = 1,//左
    NSLayoutAttributeRight,//右
    NSLayoutAttributeTop,//上
    NSLayoutAttributeBottom,//下
    NSLayoutAttributeLeading,//起始邊,相似左,只在某些從右向左排列的語言中和NSLayoutAttributeLeft有大區別
    NSLayoutAttributeTrailing,//結束邊
    NSLayoutAttributeWidth,//寬度
    NSLayoutAttributeHeight,//高度
    NSLayoutAttributeCenterX,//x中心
    NSLayoutAttributeCenterY,//y中心
    NSLayoutAttributeBaseline,//基線
    NSLayoutAttributeLastBaseline = NSLayoutAttributeBaseline,
    NSLayoutAttributeFirstBaseline NS_ENUM_AVAILABLE_IOS(8_0),
    
    //下面的屬性是設置的邊距 意義和上面相似 對應左,右等邊距
    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),
    //無,後面會說應用場景
    NSLayoutAttributeNotAnAttribute = 0
};

relation:約束的選項,對應<=,==,>=這些,枚舉以下:code

typedef NS_ENUM(NSInteger, NSLayoutRelation) {
    NSLayoutRelationLessThanOrEqual = -1,//<=
    NSLayoutRelationEqual = 0,//==
    NSLayoutRelationGreaterThanOrEqual = 1,//>=
};

view2:與之對應添加約束的視圖對象,例如,如過我要設置view1的上邊距離父視圖的上邊必定間距,這個view2就是view1的父視圖,若是我要設置view1與另外一個視圖必定距離,這個view2就是另外一個視圖。orm

attr2:view2的要約束的屬性,和attr1含義同樣。

multiplie:約束的比例,好比view1的寬是view2的寬的兩倍,這個multiplie就是2.

C:這是具體的約束值

對於這些屬性,文檔上有這樣的解釋:view1.attr1 = view2.attr2 * multiplier + constant

例如,咱們建立一個label,將它的寬高固定爲100*100,位置放在屏幕的中央,咱們可使用以下的約束代碼:

UILabel * label = [[UILabel alloc]init];
    label.numberOfLines = 0;
    //使用代碼佈局 須要將這個屬性設置爲NO
    label.translatesAutoresizingMaskIntoConstraints = NO;
    label.backgroundColor = [UIColor redColor];
    //建立x居中的約束
    NSLayoutConstraint * constraintx = [NSLayoutConstraint constraintWithItem:label attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeCenterX multiplier:1 constant:0];
    //建立y居中的約束
    NSLayoutConstraint * constrainty = [NSLayoutConstraint constraintWithItem:label attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeCenterY multiplier:1 constant:0];
    //建立寬度約束
    NSLayoutConstraint * constraintw = [NSLayoutConstraint constraintWithItem:label attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1 constant:100];
    //建立高度約束
    NSLayoutConstraint * constrainth = [NSLayoutConstraint constraintWithItem:label attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1 constant:100];
    //添加約束以前,必須將視圖加在父視圖上
    [self.view addSubview:label];
    [self.view addConstraints:@[constraintx,constrainty,constrainth,constraintw]];

效果以下:

能夠發現,一個如此簡單的約束方式,咱們用這樣的代碼要寫這麼一大坨,麻煩並且不直觀。因而,apple又提供給咱們下面一種方式。

2.使用ASCLL字符創造藝術般的格式化字符串約束

        看到這個小標題是否是眼前一亮,這個標題不是我憑空想象出來的,apple的文檔上就是這麼寫的。十分可愛,對吧。相對於NSLayoutConstraint中的建立方法以下:

+ (NSArray<__kindof NSLayoutConstraint *> *)constraintsWithVisualFormat:(NSString *)format options:(NSLayoutFormatOptions)opts metrics:(nullable NSDictionary<NSString *,id> *)metrics views:(NSDictionary<NSString *, id> *)views;

        那麼咱們先來看,什麼是格式化的字符串約束。

        說到格式化的字符串約束,要先提一個東西VFL:visual format language——格式化約束語言,這又是什麼鬼?確實,這個東西寫出來第一眼看上去真的不知道是什麼鬼,就好比要設置一個label,距離上邊100,左邊20,寬高都爲100,代碼以下:

 label.numberOfLines = 0;
    label.translatesAutoresizingMaskIntoConstraints = NO;
    label.backgroundColor = [UIColor redColor];
    
    //label.text=@"12332322132131233213213";
      [self.view addSubview:label];
    NSArray * constraintArray = [NSLayoutConstraint constraintsWithVisualFormat:@"H:|-20-[label(100@1000)]" options:0 metrics:nil views:NSDictionaryOfVariableBindings(label)];
    NSArray * constraintArray2 = [NSLayoutConstraint constraintsWithVisualFormat:@"V:|-100-[label(100)]" options:0 metrics:nil views:NSDictionaryOfVariableBindings(label)];
    [self.view addConstraints:constraintArray];
    [self.view addConstraints:constraintArray2];

效果以下:

代碼少了不少,對吧,可是中間那部分字符串什麼玩意?下面咱們來解釋一下。

        VFL語言我我的而言,他很相似於古代的象形文字(不知道是否apple的工程師從其中獲得靈感),對佈局的約束設置是直觀的用符號表達出來的,例如:

H:|-20-[label(100@1000)]

前面的H表明是水平的佈局仍是垂直的佈局,H表明水平,V表示垂直,|表示父視圖的邊沿,-20-表示距離20px,[]內是要佈局擺放的視圖對象名,()中是約束的尺寸,H下則爲寬度,V下則爲高度,@後面的數字表明優先級。

建立方法中的options參數,用來設置對齊模式,不須要能夠寫0:

typedef NS_OPTIONS(NSUInteger, NSLayoutFormatOptions) {
    NSLayoutFormatAlignAllLeft = (1 << NSLayoutAttributeLeft),
    NSLayoutFormatAlignAllRight = (1 << NSLayoutAttributeRight),
    NSLayoutFormatAlignAllTop = (1 << NSLayoutAttributeTop),
    NSLayoutFormatAlignAllBottom = (1 << NSLayoutAttributeBottom),
    NSLayoutFormatAlignAllLeading = (1 << NSLayoutAttributeLeading),
    NSLayoutFormatAlignAllTrailing = (1 << NSLayoutAttributeTrailing),
    NSLayoutFormatAlignAllCenterX = (1 << NSLayoutAttributeCenterX),
    NSLayoutFormatAlignAllCenterY = (1 << NSLayoutAttributeCenterY),
    NSLayoutFormatAlignAllBaseline = (1 << NSLayoutAttributeBaseline),
    NSLayoutFormatAlignAllLastBaseline = NSLayoutFormatAlignAllBaseline,
    NSLayoutFormatAlignAllFirstBaseline NS_ENUM_AVAILABLE_IOS(8_0) = (1 << NSLayoutAttributeFirstBaseline),
    
    NSLayoutFormatAlignmentMask = 0xFFFF,
    
    /* choose only one of these three
     */
    NSLayoutFormatDirectionLeadingToTrailing = 0 << 16, // default
    NSLayoutFormatDirectionLeftToRight = 1 << 16,
    NSLayoutFormatDirectionRightToLeft = 2 << 16,  
    
    NSLayoutFormatDirectionMask = 0x3 << 16,  
};

metrics參數是屬性替換字典,例如咱們上邊用到的距離左邊界20,若是這個20是一個變量width,咱們能夠將20的地方換成width,而後配置這個字典:@{@"width":@20},這樣,在佈局時,系統會把width換成20。

views是對象的映射字典,原理也是將字符串中的對象名label映射成真實的對象,NSDictionaryOfVariableBindings會幫咱們生成這樣的字典,咱們只須要想對象傳進去便可,若是要手動建立這字典,格式以下:@{@"label":label}.

3、使用aotulayout設計一個聊天界面的輸入框

        仔細觀察QQ或者其餘聊天工具的app上的輸入框,會發現他很是智能,寬度會隨着咱們輸入文字的行數進行自適應,而且這個寬度不是無限增大的,當咱們文字多到必定行數,寬度會保持不變,文本框能夠進行內容滑動,若是不用autolayout,這個功能會比較棘手一些,可是使用它,會發現這是如此的容易:

@interface ViewController ()<UITextViewDelegate>
{
    UITextView * textView ;
    NSArray * array1;
    NSArray * array2;
}
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    
    
    textView = [[UITextView alloc]init];
    textView.layer.borderColor = [[UIColor grayColor]CGColor];
    textView.layer.borderWidth = 1;
    textView.translatesAutoresizingMaskIntoConstraints = NO;
    textView.delegate=self;
    [self.view addSubview:textView];
    array1 = [NSLayoutConstraint constraintsWithVisualFormat:@"H:|-100-[textView]-100-|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(textView)];
    array2 = [NSLayoutConstraint constraintsWithVisualFormat:@"V:|-150-[textView(30)]" options:0 metrics:nil views:NSDictionaryOfVariableBindings(textView)];
    [self.view addConstraints:array1];
    [self.view addConstraints:array2];
  
}

-(BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text{
    //當文本高度大於textView的高度而且小於100時,更新約束
    if (textView.contentSize.height>textView.frame.size.height&&textView.contentSize.height<100) {
        float hight =textView.contentSize.height;
        //將之前的移除掉
        [self.view removeConstraints:array1];
        [self.view removeConstraints:array2];
        array1 = [NSLayoutConstraint constraintsWithVisualFormat:@"H:|-100-[textView]-100-|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(textView)];
        array2 = [NSLayoutConstraint constraintsWithVisualFormat:@"V:|-150-[textView(hight)]" options:0 metrics:@{@"hight":[NSNumber numberWithFloat:hight]} views:NSDictionaryOfVariableBindings(textView)];
        [self.view addConstraints:array1];
        [self.view addConstraints:array2];
    }
    //更新約束
    [self.view updateConstraintsIfNeeded];
    return YES;
}

如今,當咱們進行輸入的時候,textView的高度能夠自適應文字行數了。

4、經過動畫改善更新約束時的效果

        這一點很是coll,上面咱們已經實現了textView隨文本的行數高度進行自適應,可是變化的效果十分生硬,還要apple的動畫框架支持autolayout,把剛纔調用更新約束的地方進行以下修改:

[UIView animateWithDuration:1 animations:^{
        [self.view layoutIfNeeded];
    }];

試試看,變換的效果已經很是平滑了。

專一技術,熱愛生活,交流技術,也作朋友。

——琿少 QQ羣:203317592

相關文章
相關標籤/搜索