開發(fā)語言:SwiftUI 2.0
開發(fā)環(huán)境:Xcode 12.0.1
發(fā)布平臺:IOS 14
SwiftUI使用VStack/HStack/ZStack,來包含多個界面得湘,并且設(shè)置它們在其之中的對齊方式跪帝,通常有3種使用方式市埋。
1 默認(rèn)方式
在使用VStack和HStack時,可以指定其對齊方式。下面的代碼分別展示了VStack和HStack的對齊方式和效果。
struct MainView: View {
var body: some View {
VStack{
VStack(alignment: .leading){
Text("first").background(Color.red)
Text("second").background(Color.blue)
Text("third").background(Color.yellow)
}.background(Color.gray)
Spacer().fixedSize()
VStack(alignment: .center){
Text("first").background(Color.red)
Text("second").background(Color.blue)
Text("third").background(Color.yellow)
}.background(Color.gray)
Spacer().fixedSize()
VStack(alignment: .trailing){
Text("first").background(Color.red)
Text("second").background(Color.blue)
Text("third").background(Color.yellow)
}.background(Color.gray)
}
}
}
struct MainView: View {
var body: some View {
VStack{
HStack(alignment: .top){
Text("first").background(Color.red).frame(width:20)
Text("second").background(Color.blue).frame(width:20)
Text("third").background(Color.yellow).frame(width:15)
}.background(Color.gray)
Spacer().fixedSize()
HStack(alignment: .center){
Text("first").background(Color.red).frame(width:20)
Text("second").background(Color.blue).frame(width:20)
Text("third").background(Color.yellow).frame(width:15)
}.background(Color.gray)
Spacer().fixedSize()
HStack(alignment: .bottom){
Text("first").background(Color.red).frame(width:20)
Text("second").background(Color.blue).frame(width:20)
Text("third").background(Color.yellow).frame(width:15)
}.background(Color.gray)
}
}
}
通常情況下妆偏,默認(rèn)對齊已經(jīng)可以滿足我們的需求熊尉,也是我們在開發(fā)中使用最多的對齊方式催植。
2 使用alignmentGuide設(shè)置對齊
我們可以通過alignmentGuide省核,為Stack中的某一項指定不同的對齊方式,事實上,默認(rèn)對齊也是調(diào)用了alignmentGuide來設(shè)置對齊的,首先我們看一下alignmentGuide的相關(guān)定義铁瞒。
public func alignmentGuide(_ g: HorizontalAlignment,
computeValue: @escaping (ViewDimensions) -> CGFloat) -> some View
public func alignmentGuide(_ g: VerticalAlignment,
computeValue: @escaping (ViewDimensions) -> CGFloat) -> some View
第一個參數(shù)HorizontalAlignment和VerticalAlignment就是我們在默認(rèn)對齊方式中使用的對齊類型栅表,我們更關(guān)心的是第二個參數(shù)洗贰。
struct ViewDimensions {
var height: CGFloat { get }
var width: CGFloat { get }
subscript(guide: HorizontalAlignment) -> CGFloat { get }
subscript(guide: VerticalAlignment) -> CGFloat { get }
subscript(explicit guide: VerticalAlignment) -> CGFloat? { get }
subscript(explicit guide: HorizontalAlignment) -> CGFloat? { get }
}
- height和width記錄的是當(dāng)前View的高和寬
- 四個subscript為下標(biāo)取值的方式,傳遞一個對齊方式,獲取按照該對齊方式對齊的值煤裙,例如一個width為300的View,他的. trailing就是300。
為了解釋清楚alignmentGuide的運作原理括授,我們按照以下方法實現(xiàn)一個自定義的對齊方式梯澜。
extension HorizontalAlignment {
private enum HAlignment: AlignmentID {
static func defaultValue(in dimensions: ViewDimensions) -> CGFloat {
return 800
}
}
static let myHAlignment = HorizontalAlignment(HAlignment.self)
}
我們實現(xiàn)了AlignmentID接口漓帚,其中包含一個defaultValue漓踢,我們使用的leading/center/trailing也是實現(xiàn)了這個接口,他們的默認(rèn)值分別為0,ViewDimensions.width/2悔据,ViewDimensions.width。這個值表示棺禾,從View的原點(左上角位置)偏移defaultValue(右為正拯刁,下為正)后,與Stack的基線對齊郭蕉。
以VStack為例,說明alignmentGuide的使用方法瓦戚。
struct MainView: View {
var body: some View {
VStack(alignment: .myHAlignment){
Text("first")
.background(Color.red)
.alignmentGuide(.myHAlignment, computeValue: { dimension in
return dimension[.leading]
})
Text("second")
.background(Color.blue)
.alignmentGuide(.myHAlignment, computeValue: { dimension in
return dimension[.trailing]
})
Text("third")
.background(Color.yellow)
.alignmentGuide(.myHAlignment, computeValue: { dimension in
return dimension[HorizontalAlignment.center]
})
Text("fourth")
.background(Color.green)
.alignmentGuide(.myHAlignment, computeValue: { dimension in
return 40
})
Text("fifth")
.background(Color.secondary)
.alignmentGuide(.myHAlignment, computeValue: { dimension in
return -20
})
}.background(Color.gray)
}
}
alignmentGuide的作用是了赌,將computeValue的值,設(shè)置到第一個參數(shù)指定的對齊類型中辩恼,替換掉它的defaultValue。
Stack在布局的時候翘贮,首先先確認(rèn)設(shè)置的對齊類型,這里我們使用的是自定義類型myHAlignment熄阻,然后查找每個子控件的ViewDimensions中myHAlignment的值,此時這個值已經(jīng)在alignmentGuide中設(shè)置過畦木,然后與基線對齊,最終呈現(xiàn)整個Stack。
- 如果我們沒有通過alignmentGuide設(shè)置Stack的對齊方式的值,布局時則會使用默認(rèn)值常遂。
通過圖中的標(biāo)出的VStack的基線,解釋了5個不同的alignmentGuide設(shè)置對齊的方式跃赚。
通過上例可以看出Stack只關(guān)心和它對齊方式一致的值着裹,但我們通過alignmentGuide設(shè)置值時粱胜,第一個參數(shù)不一定要和Stack中設(shè)置的對齊方式一致鸿脓,如下例:
struct MainView: View {
var body: some View {
VStack(alignment: .myHAlignment){
Text("first")
.background(Color.red)
.alignmentGuide(.myHAlignment, computeValue: { dimension in
return dimension[.leading]
})
Text("second")
.background(Color.blue)
//設(shè)置與VStack不一樣的對齊方式
.alignmentGuide(.leading, computeValue: { dimension in
return 50
})
//此處拿到的.leading已經(jīng)不是0,而是50
.alignmentGuide(.myHAlignment, computeValue: { dimension in
return dimension[.trailing] + dimension[explicit: .leading]!
})
}.background(Color.gray)
}
}
我們將.leading的值涯曲,設(shè)置為了50野哭,然后在第二個alignmentGuide,我們通過dimension[explicit: .leading]拿到設(shè)置的值幻件,與其他值組合后設(shè)置到.myHAlignment內(nèi)虐拓,供Stack布局時使用。
這里也演示了dimension[explicit: .leading]的作用傲武,它返回的是一個可選型蓉驹,表示如果通過alignmentGuide設(shè)置過.leading的值,則可以獲取揪利,否則返回nil态兴。
- 在使用alignmentGuide設(shè)置值后,不管通過dimension[explicit: ]或者dimension[]疟位,獲取到的值時相同的瞻润。
3 自定義對齊方式
在上一小節(jié)中,我們使用了自定義的對齊方式甜刻,而自定義的對齊方式绍撞,往往可以幫助我們解決一些特殊的對齊需求,先看下面的例子:
extension HorizontalAlignment {
private enum HAlignment: AlignmentID {
static func defaultValue(in dimensions: ViewDimensions) -> CGFloat {
return dimensions[HorizontalAlignment.leading]
}
}
static let myHAlignment = HorizontalAlignment(HAlignment.self)
}
struct MainView: View {
var body: some View {
VStack(alignment: .myHAlignment){
HStack {
Text("first")
.background(Color.red)
Text("second")
.background(Color.blue)
.alignmentGuide(HorizontalAlignment.myHAlignment, computeValue: { dimension in
return dimension[.leading]
})
Text("third")
.background(Color.yellow)
}
Text("fourth")
.background(Color.green)
}.background(Color.gray)
}
}
例子中得院,我們自定義了一個對齊方式傻铣,默認(rèn)的對齊與.leading保持一致,然后我們將HStack中第二個Text的leading設(shè)置為myHAlignment的值祥绞,這樣VStack的基線位置就是HStack中的第二個Text保持一致非洲,VStack的其余部件布局時鸭限,會按照這個基線進行對齊。
如果我們嘗試不使用myHAlignment两踏,而直接使用.leading對齊方式败京。
struct MainView: View {
var body: some View {
VStack(alignment: .leading){
HStack {
Text("first")
.background(Color.red)
Text("second")
.background(Color.blue)
.alignmentGuide(.leading, computeValue: { dimension in
return dimension[.leading]
})
Text("third")
.background(Color.yellow)
}
Text("fourth")
.background(Color.green)
}.background(Color.gray)
}
}
此時對Text("second")的alignmentGuide設(shè)置沒有起到任何效果。
但這里設(shè)置.leading沒有起作用的原因我也不太了解梦染,猜測可能是.leading作為系統(tǒng)自帶對齊方式赡麦,無法跨Stack傳遞或者者會在傳遞時設(shè)置初值。