SwiftUI教程(四)布局詳解

SwiftUI教程系列文章匯總

本文主要了解SwiftUI中如何進(jìn)行布局,在SwiftUI沒(méi)有坐標(biāo)系這種說(shuō)法,而是使用彈性布局,我們使用Stacks進(jìn)行垂直、水平、深度布局,還可以對(duì)視圖進(jìn)行偏移或設(shè)置到父視圖的某個(gè)特定位置。

主要內(nèi)容:

  1. SwiftUI的布局
  2. VStack
  3. HStack
  4. ZStack
  5. LazyStack
  6. 絕對(duì)位置position和相對(duì)位置offset

1. SwiftUI的布局

UI通常由多種不同類型的視圖組合而成。我們?nèi)绾螌?duì)他們進(jìn)行分組以及布局定位?此時(shí)就需要使用stacks。我們可以使用三種堆棧來(lái)對(duì)UI進(jìn)行分組:

  • HStack - 水平排列其子視圖
  • VStack - 垂直排列其子視圖
  • ZStack -根據(jù)深度排列子視圖(例如從后到前)

在這三種Stack的基礎(chǔ)上還有一種懶加載的Stack,叫l(wèi)azyStack.

除此之外還需要了解絕地位置和相對(duì)位置的使用

注意: SwiftUI沒(méi)有坐標(biāo)系這種說(shuō)法,使用彈性布局。類似于HTML的布局方式

2. Stacks的使用

垂直布局方式

var body: some View {
    //包括leading、trailing、center
    VStack(alignment:.leading, spacing: 20){
        Text("orange").background(.orange).font(.caption)
        Text("red").background(.red).font(.title)
        Text("blue").background(.blue).font(.largeTitle)
        Text("yellow").background(.yellow).font(.callout)
    }
    .border(.gray)
    //包括bottom、top、firstTextBaseline、lastTextBaseline、center、
    HStack(alignment:.bottom, spacing: 20){
        Text("orange").background(.orange).font(.caption)
        Text("red").background(.red).font(.title)
        Text("blue").background(.blue).font(.largeTitle)
        Text("yellow").background(.yellow).font(.callout)
    }
    .border(.gray)
    //包括leading、trailing、bottom、top、bottomLeading、topLeading、bottomtrailing、toptrailing、center
    ZStack(alignment: .bottomTrailing){
        Text("orange").background(.orange).font(.caption)
        Text("red").background(.red).font(.title)
        Text("blue").background(.blue).font(.largeTitle)
        Text("yellow").background(.yellow).font(.callout)
    }
    .border(.gray)
}

結(jié)果:

結(jié)果

說(shuō)明:

  1. VStack的Text和button就會(huì)垂直分布,Text視圖堆疊在Button視圖的上方
  2. HStack的Text和button會(huì)水平分布,Text視圖堆疊在Button視圖的左側(cè)
  3. ZStack的Text和button會(huì)水平分布,Text視圖堆疊在Button視圖的底部
  4. 默認(rèn)情況下一個(gè)Stack的兩個(gè)View之間的間隔很少或沒(méi)有間隔,但是我們可以通過(guò)提供一個(gè)參數(shù)(spacing)來(lái)控制間隔
  5. VStack默認(rèn)也會(huì)將視圖居中對(duì)齊。但我們可以使用' alignment '屬性進(jìn)行更改,這里進(jìn)行左對(duì)齊
  6. HStack的語(yǔ)法與VStack相同,包括指定空格和對(duì)齊。ZStack也可以指定對(duì)齊方式,但是不能指定空格

3. Stacks的混合使用

我們可以將VStack和HStack視圖組合在一起進(jìn)行復(fù)雜的安排創(chuàng)建更多視圖。

HStack {
    ZStack {
        Circle()
            .fill(Color.yellow)
        Button(action:{
            print("button tapped")
        }){
            Text("Press Me")
        }
    }
    .frame(width: 100.0, height: 100.0)
    VStack(alignment: .leading, spacing: 4) { Text("Beginning SwiftUI")
        Text("Greg Lim, 2021")
    }
}

說(shuō)明:

  1. 我們有一個(gè)ZStack和一個(gè)VStack嵌套在HStack中
  2. VStack本身包含兩個(gè)Text視圖
  3. ZStack包含一個(gè)圓和一個(gè)按鈕,這個(gè)按鈕包含在覆蓋在圓的前面
  4. 我們調(diào)用ZStack的frame modeifier來(lái)設(shè)置它的寬度和高度為100。
  5. 包裝在VStack中的視圖默認(rèn)情況下是在中心對(duì)齊。我們可以用對(duì)齊參數(shù)來(lái)改變這一點(diǎn)。

4. LazyStack

懶加載的Stack

ScrollView {
    LazyVStack {
        ForEach(1...100, id: \.self) {
            Text("Cell \($0)").padding()
        }
    }
}

說(shuō)明:

  1. scrollView正常情況下會(huì)一次性加載VStack里的數(shù)據(jù),比如這里的100個(gè)item
  2. 但是使用lazyVStack后,就只會(huì)加載當(dāng)前頁(yè)面顯示的item
  3. 需要注意:lazyVStack必須和scrollView搭配使用,還必須有循環(huán)創(chuàng)建,否則無(wú)法出現(xiàn)懶加載效果

5.ViewBuilder

ViewBuilder是VStack包裝的底層實(shí)現(xiàn)
以VStack為例進(jìn)行說(shuō)明:

@frozen public struct VStack<Content> : View where Content : View {
    public static func buildBlock<Content>(_ content: Content) -> Content where Content : View
}

說(shuō)明:

  1. Stack 是通過(guò)ViewBuilder來(lái)實(shí)現(xiàn)底層包裝View的
  2. 有兩個(gè)參數(shù),對(duì)齊方式和子視圖間距
  3. 通過(guò)buildBlock方法將子視圖內(nèi)容包裝成一個(gè)視圖進(jìn)行返回

示例:

示例

6. 絕對(duì)位置和相對(duì)位置

6.1 相對(duì)位置offset

查看定義

    @inlinable public func offset(x: CGFloat = 0, y: CGFloat = 0) -> some View
}

說(shuō)明:

  • offset可以傳入一個(gè)CGSize值,也可以傳入x和y坐標(biāo)
  • 偏移量不會(huì)改變視圖的尺寸,只會(huì)改變位置

使用:

VStack (spacing:50){
    Text("Offset by passing CGSize()")
        .border(Color.green)
        .offset(CGSize(width: 20, height: 25))
        .border(Color.gray)

    Text("Offset by passing horizontal & vertical distance")
        .border(Color.green)
        .offset(x: 20, y: 50)
        .border(Color.gray)
}

結(jié)果:

結(jié)果

說(shuō)明:

  • 灰色邊框是原始位置
  • offset是將視圖以及已經(jīng)添加的修飾符都進(jìn)行整體偏移,所以把green也偏移了過(guò)去
  • 而border是在offset后添加的修飾器,所以仍然在文本的原始位置
  • 兩種方式僅僅在于方式不一樣,效果是完全一樣的

6.2 絕對(duì)位置position

定義

extension View {
    @inlinable public func position(x: CGFloat = 0, y: CGFloat = 0) -> some View

}

使用:

Text("Position by passing a CGPoint()")
    .background(Color.blue)
    .position(CGPoint(x: 175, y: 100))
    .background(Color.green)
    
Text("Position by passing the x and y coordinates")
    .background(Color.blue)
    .position(CGPoint(x: 175, y: 100))
    .background(Color.green)

結(jié)果:

絕對(duì)位置

說(shuō)明:

  • position是將視圖的中心放置在父視圖的特定坐標(biāo)上。
  • 第一個(gè)注意的是這里設(shè)置的位置是視圖的中心位置
  • 第二個(gè)需要注意的是放置到了父視圖的特定坐標(biāo)上,所以position后設(shè)置的顏色充滿整個(gè)父視圖
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容