本文主要了解SwiftUI中如何進(jìn)行布局,在SwiftUI沒(méi)有坐標(biāo)系這種說(shuō)法,而是使用彈性布局,我們使用Stacks進(jìn)行垂直、水平、深度布局,還可以對(duì)視圖進(jìn)行偏移或設(shè)置到父視圖的某個(gè)特定位置。
主要內(nèi)容:
- SwiftUI的布局
- VStack
- HStack
- ZStack
- LazyStack
- 絕對(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ō)明:
- VStack的Text和button就會(huì)垂直分布,Text視圖堆疊在Button視圖的上方
- HStack的Text和button會(huì)水平分布,Text視圖堆疊在Button視圖的左側(cè)
- ZStack的Text和button會(huì)水平分布,Text視圖堆疊在Button視圖的底部
- 默認(rèn)情況下一個(gè)Stack的兩個(gè)View之間的間隔很少或沒(méi)有間隔,但是我們可以通過(guò)提供一個(gè)參數(shù)(spacing)來(lái)控制間隔
- VStack默認(rèn)也會(huì)將視圖居中對(duì)齊。但我們可以使用' alignment '屬性進(jìn)行更改,這里進(jìn)行左對(duì)齊
- 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ō)明:
- 我們有一個(gè)ZStack和一個(gè)VStack嵌套在HStack中
- VStack本身包含兩個(gè)Text視圖
- ZStack包含一個(gè)圓和一個(gè)按鈕,這個(gè)按鈕包含在覆蓋在圓的前面
- 我們調(diào)用ZStack的frame modeifier來(lái)設(shè)置它的寬度和高度為100。
- 包裝在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ō)明:
- scrollView正常情況下會(huì)一次性加載VStack里的數(shù)據(jù),比如這里的100個(gè)item
- 但是使用lazyVStack后,就只會(huì)加載當(dāng)前頁(yè)面顯示的item
- 需要注意: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ō)明:
- Stack 是通過(guò)ViewBuilder來(lái)實(shí)現(xiàn)底層包裝View的
- 有兩個(gè)參數(shù),對(duì)齊方式和子視圖間距
- 通過(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è)父視圖