AsyncDisplayKit學(xué)習(xí)系列1 - 布局

一年多以前就接觸了AsyncDisplayKit,但是那時菜的摳腳疟羹,不會用。現(xiàn)在打算學(xué)一下禀倔。

ASDK的2.0版本更名為Texture榄融,主要做的事情就是將渲染和布局從主線程移到異步線程,充分利用多核心的優(yōu)勢救湖,努力保證UI不卡頓愧杯。

這篇主要講ASDK的布局,這個布局學(xué)習(xí)起來還是有點麻煩的鞋既,但是掌握了之后感覺用起來比AutoLayout要方便(反正我目前是沒有完全掌握)力九。

一、ASDisplayNode

首先我們需要講一下ASDisplayNode這個類邑闺,這個類相當(dāng)于UIView跌前,是ASDK中的視圖的基類。

下面是ASDisplayNode的介紹:

/**
 * An `ASDisplayNode` is an abstraction over `UIView` and `CALayer` that allows you to perform calculations about a view
 * hierarchy off the main thread, and could do rendering off the main thread as well.
 *
 * The node API is designed to be as similar as possible to `UIView`. See the README for examples.
 *
 * ## Subclassing
 *
 * `ASDisplayNode` can be subclassed to create a new UI element. The subclass header `ASDisplayNode+Subclasses` provides
 * necessary declarations and conveniences.
 *
 * Commons reasons to subclass includes making a `UIView` property available and receiving a callback after async
 * display.
 *
 */

二陡舅、- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize;

這個方法是ASDisplayNode提供的抵乓,我們實現(xiàn)這個方法來進(jìn)行布局。

三、ASLayoutSpec(布局規(guī)則)

ASLayoutSpec這個類是所有布局類的基類灾炭,它主要遵循了<ASLayoutElement>這個代理,這個代理聲明了@property (nonatomic, readonly) ASLayoutElementStyle *style;這個屬性茎芋,用來設(shè)置寬、高蜈出、size等屬性田弥。下面是一些ASLayoutSpec的子類。

3.1 ASWrapperLayoutSpec

顧名思義铡原,這個布局規(guī)則的作用就是將視圖完整填充到父視圖皱蹦。

示例代碼如下:

/**
 ASWrapperLayoutSpec:填充整個視圖
 */
- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize {
    
    return [ASWrapperLayoutSpec wrapperWithLayoutElement:self.subnode1];
}

#pragma mark - lazy load
- (ASDisplayNode *)subnode1 {
    if (!_subnode1) {
        _subnode1 = [[ASDisplayNode alloc] init];
        _subnode1.backgroundColor = [UIColor redColor];
    }
    return _subnode1;
}

效果圖如下:

ASWrapperLayoutSpec

3.2 ASStackLayoutSpec

ASStackLayoutSpec是最常用的布局規(guī)則,和flexbox很像(然而我并沒有研究過flexbox)眷蜈。下面是常用屬性的介紹:

/**
 ASStackLayoutSpec:最常用的類沪哺,盒子布局
 
 1. direction:主軸的方向,有兩個可選值:
    縱向:ASStackLayoutDirectionVertical
    橫向:ASStackLayoutDirectionHorizontal
 
 2. spacing: 主軸上視圖排列的間距酌儒,比如有四個視圖辜妓,那么它們之間的存在三個間距值都應(yīng)該是spacing
 
 3. justifyContent: 主軸上的排列方式,有五個可選值:
    ASStackLayoutJustifyContentStart 從前往后排列
    ASStackLayoutJustifyContentCenter 居中排列
    ASStackLayoutJustifyContentEnd 從后往前排列
    ASStackLayoutJustifyContentSpaceBetween 間隔排列忌怎,兩端無間隔
    ASStackLayoutJustifyContentSpaceAround 間隔排列籍滴,兩端有間隔
 
 4. alignItems: 交叉軸上的排列方式,有五個可選值:
    ASStackLayoutAlignItemsStart 從前往后排列
    ASStackLayoutAlignItemsEnd 從后往前排列
    ASStackLayoutAlignItemsCenter 居中排列
    ASStackLayoutAlignItemsStretch 拉伸排列
    ASStackLayoutAlignItemsBaselineFirst 以第一個文字元素基線排列(主軸是橫向才可用)
    ASStackLayoutAlignItemsBaselineLast 以最后一個文字元素基線排列(主軸是橫向才可用)
 
 5. children: 包含的視圖榴啸。數(shù)組內(nèi)元素順序同樣代表著布局時排列的順序孽惰,所以需要注意
 */

直接上代碼:

- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize {
    
    self.subnode1.style.preferredSize = CGSizeMake(constrainedSize.max.width * 0.2, constrainedSize.max.height * 0.2);
    self.subnode2.style.preferredSize = CGSizeMake(constrainedSize.max.width * 0.2, constrainedSize.max.height * 0.2);
    self.subnode3.style.preferredSize = CGSizeMake(constrainedSize.max.width * 0.2, constrainedSize.max.height * 0.2);
    
    ASStackLayoutSpec *horizontalStack = [[ASStackLayoutSpec alloc] init];
    horizontalStack.direction = ASStackLayoutDirectionHorizontal;
    horizontalStack.justifyContent = ASStackLayoutJustifyContentCenter;
    horizontalStack.alignItems = ASStackLayoutAlignItemsStretch;
    horizontalStack.alignContent = ASStackLayoutAlignContentEnd;
    horizontalStack.flexWrap = ASStackLayoutFlexWrapWrap;
    horizontalStack.spacing = 6;
    horizontalStack.children = @[self.subnode1, self.subnode2];
    
    ASStackLayoutSpec *verticalStack = [[ASStackLayoutSpec alloc] init];
    verticalStack.direction = ASStackLayoutDirectionVertical;
    verticalStack.justifyContent = ASStackLayoutJustifyContentCenter;
    verticalStack.alignItems = ASStackLayoutAlignItemsCenter;
    verticalStack.children = @[horizontalStack, self.subnode3];
    verticalStack.spacing = 6;
    
    return verticalStack;
}

#pragma mark - lazy load

- (ASDisplayNode *)subnode1 {
    if (!_subnode1) {
        _subnode1 = [[ASDisplayNode alloc] init];
        _subnode1.backgroundColor = [UIColor redColor];
    }
    return _subnode1;
}

- (ASDisplayNode *)subnode2 {
    if (!_subnode2) {
        _subnode2 = [[ASDisplayNode alloc] init];
        _subnode2.backgroundColor = [UIColor blueColor];
    }
    return _subnode2;
}

- (ASDisplayNode *)subnode3 {
    if (!_subnode3) {
        _subnode3 = [[ASDisplayNode alloc] init];
        _subnode3.backgroundColor = [UIColor cyanColor];
    }
    return _subnode3;
}

效果圖如下:

ASStackLayoutSpec

3.3 ASInsetLayoutSpec

這個布局規(guī)則的作用就是相對父視圖設(shè)置內(nèi)邊距。

代碼如下:

/**
 ASInsetLayoutSpec:相對于父視圖邊距
 */
- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize {
    UIEdgeInsets insets = UIEdgeInsetsMake(6, 6, 6, 6);
    
    ASInsetLayoutSpec *inset = [[ASInsetLayoutSpec alloc] init];
    inset.insets = insets;
    inset.child = self.subnode1;
    
    return inset;
}

#pragma mark - lazy load

- (ASDisplayNode *)subnode1 {
    if (!_subnode1) {
        _subnode1 = [[ASDisplayNode alloc] init];
        _subnode1.backgroundColor = [UIColor redColor];
    }
    return _subnode1;
}

效果圖如下:

ASInsetLayoutSpec

3.4 ASOverlayLayoutSpec 和 ASBackgroundLayoutSpec

這兩個布局規(guī)則沒有特別大的作用鸥印,即使使用也不會更改視圖的層級關(guān)系勋功。代碼和效果圖就不提供了。


3.5 ASCenterLayoutSpec

這個類的作用是將視圖按照X軸或Y軸或XY軸的中心進(jìn)行布局库说。

代碼如下:

/**
 ASCenterLayoutSpec:居中布局
 */
- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize {
    
    self.subnode1.style.preferredSize = CGSizeMake(constrainedSize.max.width * 0.1, constrainedSize.max.height * 0.1);
    
    ASCenterLayoutSpec *center = [[ASCenterLayoutSpec alloc] init];
    center.child = self.subnode1;
    center.centeringOptions = ASCenterLayoutSpecCenteringXY;
    return center;
}

#pragma mark - lazy load

- (ASDisplayNode *)subnode1 {
    if (!_subnode1) {
        _subnode1 = [[ASDisplayNode alloc] init];
        _subnode1.backgroundColor = [UIColor redColor];
    }
    return _subnode1;
}

效果圖如下:

ASCenterLayoutSpec

3.6 ASRatioLayoutSpec

這個布局規(guī)則的作用是設(shè)置自身的寬高比狂鞋。

代碼如下:

/**
 ASRatioLayoutSpec:設(shè)置自身寬高比
 */
- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize {
    
    ASRatioLayoutSpec *ratio = [[ASRatioLayoutSpec alloc] init];
    ratio.child = self.subnode1;
    ratio.ratio = 0.5;
    
    return [ASCenterLayoutSpec centerLayoutSpecWithCenteringOptions:ASCenterLayoutSpecCenteringXY sizingOptions:ASCenterLayoutSpecSizingOptionDefault child:ratio];
}

#pragma mark - lazy load

- (ASDisplayNode *)subnode1 {
    if (!_subnode1) {
        _subnode1 = [[ASDisplayNode alloc] init];
        _subnode1.backgroundColor = [UIColor redColor];
    }
    return _subnode1;
}

效果圖如下:

ASRatioLayoutSpec

3.7 ASRelativeLayoutSpec

相對布局有horizontalPositionverticalPosition兩個屬性,這兩個屬性都提供了start潜的、center骚揍、end這三個位置,所以可以將視圖布局在9個位置啰挪。

代碼如下:

/**
 ASRelativeLayoutSpec:相對布局
 
 */
- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize {
    
    self.subnode1.style.preferredSize = CGSizeMake(constrainedSize.max.width * 0.1, constrainedSize.max.height * 0.1);
    
    ASRelativeLayoutSpec *relative = [[ASRelativeLayoutSpec alloc] init];
    relative.child = self.subnode1;
    relative.horizontalPosition = ASRelativeLayoutSpecPositionEnd;
    relative.verticalPosition = ASRelativeLayoutSpecPositionCenter;
    relative.sizingOption = ASRelativeLayoutSpecSizingOptionDefault;
    
    return relative;
}

#pragma mark - lazy load

- (ASDisplayNode *)subnode1 {
    if (!_subnode1) {
        _subnode1 = [[ASDisplayNode alloc] init];
        _subnode1.backgroundColor = [UIColor redColor];
    }
    return _subnode1;
}

效果圖如下:

ASRelativeLayoutSpec

3.8 ASAbsoluteLayoutSpec

絕對布局和設(shè)置frame很像信不,視圖根據(jù)設(shè)置坐標(biāo)和大小進(jìn)行布局。

代碼如下:

/**
 ASAbsoluteLayoutSpec:絕對布局
 和設(shè)置frame一樣
 */
- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize {
    
    self.subnode1.style.layoutPosition = CGPointMake(0, 0);
    self.subnode1.style.preferredSize = CGSizeMake(50, 50);
    
    self.subnode2.style.layoutPosition = CGPointMake(100, 100);
    self.subnode2.style.preferredSize = CGSizeMake(50, 50);
    
    ASAbsoluteLayoutSpec *absolute = [[ASAbsoluteLayoutSpec alloc] init];
    absolute.children = @[self.subnode1, self.subnode2];
    
    return absolute;
}

#pragma mark - lazy load

- (ASDisplayNode *)subnode1 {
    if (!_subnode1) {
        _subnode1 = [[ASDisplayNode alloc] init];
        _subnode1.backgroundColor = [UIColor redColor];
    }
    return _subnode1;
}

- (ASDisplayNode *)subnode2 {
    if (!_subnode2) {
        _subnode2 = [[ASDisplayNode alloc] init];
        _subnode2.backgroundColor = [UIColor blueColor];
    }
    return _subnode2;
}

效果圖如下:

ASAbsoluteLayoutSpec

3.9 ASCornerLayoutSpec

這個布局有點像是為視圖設(shè)置角標(biāo)亡呵。

代碼如下:

/**
 ASCornerLayoutSpec:角標(biāo)布局
 */
- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize {
    
    self.subnode1.style.preferredSize = CGSizeMake(50, 50);
    self.subnode2.style.preferredSize = CGSizeMake(20, 20);
    
    ASCornerLayoutSpec *corner = [[ASCornerLayoutSpec alloc] init];
    corner.child = self.subnode1;
    corner.corner = self.subnode2;
    corner.cornerLocation = ASCornerLayoutLocationTopRight;
    corner.offset = CGPointMake(-5, 5);
    
    ASCenterLayoutSpec *center = [ASCenterLayoutSpec centerLayoutSpecWithCenteringOptions:ASCenterLayoutSpecCenteringXY sizingOptions:ASCenterLayoutSpecSizingOptionDefault child:corner];
    
    return center;
}

#pragma mark - lazy load

- (ASDisplayNode *)subnode1 {
    if (!_subnode1) {
        _subnode1 = [[ASDisplayNode alloc] init];
        _subnode1.backgroundColor = [UIColor redColor];
    }
    return _subnode1;
}

- (ASDisplayNode *)subnode2 {
    if (!_subnode2) {
        _subnode2 = [[ASDisplayNode alloc] init];
        
        _subnode2.backgroundColor = [UIColor blueColor];
        _subnode2.borderColor = [UIColor whiteColor].CGColor;
        _subnode2.borderWidth = 3;
        _subnode2.cornerRadius = 10;
    }
    return _subnode2;
}

效果圖如下:

ASCornerLayoutSpec

四抽活、demo練習(xí)

介紹完上面基礎(chǔ)的布局,讓我們來練習(xí)練習(xí)政己,實現(xiàn)兩個小demo酌壕。

4.1 demo1

首先我們來看一下效果圖:

demo1

簡單分析一下掏愁,這個demo首先需要一張背景圖在最下面填充父視圖,然后圖片上方的底部有兩個label卵牍。

背景圖我們用wrapperLayout來進(jìn)行布局果港,兩個label用stackLayout來進(jìn)行布局,label的左邊和底部間距使用insetLayout包一下stackLayout來實現(xiàn)糊昙,最后我們使用overLayout來將wrapper和insetLayout包起來辛掠。

下面是我的代碼實現(xiàn):

- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize {
    
    ASWrapperLayoutSpec *wrapper = [ASWrapperLayoutSpec wrapperWithLayoutElement:self.backgroundNode];

    ASStackLayoutSpec *stack = [ASStackLayoutSpec stackLayoutSpecWithDirection:ASStackLayoutDirectionVertical spacing:10 justifyContent:ASStackLayoutJustifyContentEnd alignItems:ASStackLayoutAlignItemsNotSet children:@[self.titleNode, self.subtitleNode]];
    
    ASInsetLayoutSpec *inset = [ASInsetLayoutSpec insetLayoutSpecWithInsets:UIEdgeInsetsMake(0, 18, 12, 0) child:stack];
    
    ASOverlayLayoutSpec *overlay = [ASOverlayLayoutSpec overlayLayoutSpecWithChild:wrapper overlay:inset];
    
    return overlay;
}

4.2 demo2

我們來看一下效果圖:

demo2

整體看上去應(yīng)該是一個方向為vertical的stackLayout,底部的效果是用stackLayout嵌套來實現(xiàn)的释牺。我們直接看代碼:

- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize {
    
    // 頂部圖片寬高比
    ASRatioLayoutSpec *ratio = [ASRatioLayoutSpec ratioLayoutSpecWithRatio:1 child:self.topImageNode];
    
    // 左下角價格和銷量
    ASStackLayoutSpec *bottomLeftStack = [[ASStackLayoutSpec alloc] init];
    bottomLeftStack.direction = ASStackLayoutDirectionHorizontal;
    bottomLeftStack.justifyContent = ASStackLayoutJustifyContentStart;
    bottomLeftStack.spacing = 6;
    bottomLeftStack.children = @[
                                 // 文字居中
                                 [ASCenterLayoutSpec centerLayoutSpecWithCenteringOptions:ASCenterLayoutSpecCenteringY
                                                                             sizingOptions:ASCenterLayoutSpecSizingOptionDefault
                                                                                     child:self.priceNode],
                                 [ASCenterLayoutSpec centerLayoutSpecWithCenteringOptions:ASCenterLayoutSpecCenteringY
                                                                             sizingOptions:ASCenterLayoutSpecSizingOptionDefault
                                                                                     child:self.salesNode]
                                 ];
    
    // 底部價格萝衩、銷量和更多按鈕
    ASStackLayoutSpec *horizontalStack = [[ASStackLayoutSpec alloc] init];
    horizontalStack.direction = ASStackLayoutDirectionHorizontal;
    horizontalStack.justifyContent = ASStackLayoutJustifyContentSpaceBetween;
    horizontalStack.children = @[
                                 bottomLeftStack,
                                 self.moreButton
                                 ];
    
    // 整體的縱向布局
    ASStackLayoutSpec *verticalStack = [[ASStackLayoutSpec alloc] init];
    verticalStack.direction = ASStackLayoutDirectionVertical;
    verticalStack.justifyContent = ASStackLayoutJustifyContentSpaceBetween;
    verticalStack.children = @[
                               ratio,
                               // 縮進(jìn)
                               [ASInsetLayoutSpec insetLayoutSpecWithInsets:UIEdgeInsetsMake(0, 12, 0, 12) child:self.titleNode],
                               [ASInsetLayoutSpec insetLayoutSpecWithInsets:UIEdgeInsetsMake(0, 12, 0, 12) child:self.descNode],
                               [ASInsetLayoutSpec insetLayoutSpecWithInsets:UIEdgeInsetsMake(0, 12, 0, 12) child:horizontalStack]
                               ];
    
    // 下面留空
    ASInsetLayoutSpec *inset = [ASInsetLayoutSpec insetLayoutSpecWithInsets:UIEdgeInsetsMake(0, 0, 12, 0) child:verticalStack];
    
    return inset;
}

總結(jié)一下,ASDK的布局規(guī)則没咙,可能上手不是那么簡單猩谊,但是掌握之后,布局起來還是比較方便的祭刚。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末牌捷,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子涡驮,更是在濱河造成了極大的恐慌暗甥,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,682評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件捉捅,死亡現(xiàn)場離奇詭異撤防,居然都是意外死亡,警方通過查閱死者的電腦和手機棒口,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,277評論 3 395
  • 文/潘曉璐 我一進(jìn)店門寄月,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人陌凳,你說我怎么就攤上這事剥懒。” “怎么了合敦?”我有些...
    開封第一講書人閱讀 165,083評論 0 355
  • 文/不壞的土叔 我叫張陵,是天一觀的道長验游。 經(jīng)常有香客問我充岛,道長,這世上最難降的妖魔是什么耕蝉? 我笑而不...
    開封第一講書人閱讀 58,763評論 1 295
  • 正文 為了忘掉前任崔梗,我火速辦了婚禮,結(jié)果婚禮上垒在,老公的妹妹穿的比我還像新娘蒜魄。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,785評論 6 392
  • 文/花漫 我一把揭開白布谈为。 她就那樣靜靜地躺著旅挤,像睡著了一般。 火紅的嫁衣襯著肌膚如雪伞鲫。 梳的紋絲不亂的頭發(fā)上粘茄,一...
    開封第一講書人閱讀 51,624評論 1 305
  • 那天,我揣著相機與錄音秕脓,去河邊找鬼柒瓣。 笑死,一個胖子當(dāng)著我的面吹牛吠架,可吹牛的內(nèi)容都是我干的芙贫。 我是一名探鬼主播,決...
    沈念sama閱讀 40,358評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼傍药,長吁一口氣:“原來是場噩夢啊……” “哼屹培!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起怔檩,我...
    開封第一講書人閱讀 39,261評論 0 276
  • 序言:老撾萬榮一對情侶失蹤褪秀,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后薛训,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體媒吗,經(jīng)...
    沈念sama閱讀 45,722評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年乙埃,在試婚紗的時候發(fā)現(xiàn)自己被綠了闸英。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,030評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖炫贤,靈堂內(nèi)的尸體忽然破棺而出益缎,到底是詐尸還是另有隱情,我是刑警寧澤辙喂,帶...
    沈念sama閱讀 35,737評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站鸠珠,受9級特大地震影響巍耗,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜渐排,卻給世界環(huán)境...
    茶點故事閱讀 41,360評論 3 330
  • 文/蒙蒙 一炬太、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧驯耻,春花似錦亲族、人聲如沸炒考。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,941評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽斋枢。三九已至,卻和暖如春女气,著一層夾襖步出監(jiān)牢的瞬間杏慰,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,057評論 1 270
  • 我被黑心中介騙來泰國打工炼鞠, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留缘滥,地道東北人。 一個月前我還...
    沈念sama閱讀 48,237評論 3 371
  • 正文 我出身青樓谒主,卻偏偏與公主長得像朝扼,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子霎肯,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,976評論 2 355