SwiftUI2.0 使用Stack和alignmentGuide設(shè)置對(duì)齊

開發(fā)語言:SwiftUI 2.0
開發(fā)環(huán)境:Xcode 12.0.1
發(fā)布平臺(tái):IOS 14

SwiftUI使用VStack/HStack/ZStack,來包含多個(gè)界面,并且設(shè)置它們?cè)谄渲械膶?duì)齊方式,通常有3種使用方式。

1 默認(rèn)方式

在使用VStack和HStack時(shí),可以指定其對(duì)齊方式。下面的代碼分別展示了VStack和HStack的對(duì)齊方式和效果。

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)
        }
    }
}
VStack的三種對(duì)齊方式
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)
        }
    }
}
HStack的三種對(duì)齊方式

通常情況下,默認(rèn)對(duì)齊已經(jīng)可以滿足我們的需求,也是我們?cè)陂_發(fā)中使用最多的對(duì)齊方式。

2 使用alignmentGuide設(shè)置對(duì)齊

我們可以通過alignmentGuide,為Stack中的某一項(xiàng)指定不同的對(duì)齊方式,事實(shí)上,默認(rèn)對(duì)齊也是調(diào)用了alignmentGuide來設(shè)置對(duì)齊的,首先我們看一下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

第一個(gè)參數(shù)HorizontalAlignment和VerticalAlignment就是我們?cè)谀J(rèn)對(duì)齊方式中使用的對(duì)齊類型,我們更關(guān)心的是第二個(gè)參數(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的高和寬
  • 四個(gè)subscript為下標(biāo)取值的方式,傳遞一個(gè)對(duì)齊方式,獲取按照該對(duì)齊方式對(duì)齊的值,例如一個(gè)width為300的View,他的. trailing就是300。

為了解釋清楚alignmentGuide的運(yùn)作原理,我們按照以下方法實(shí)現(xiàn)一個(gè)自定義的對(duì)齊方式。

extension HorizontalAlignment {
    private enum HAlignment: AlignmentID {
        static func defaultValue(in dimensions: ViewDimensions) -> CGFloat {
            return 800
        }
    }
    static let myHAlignment = HorizontalAlignment(HAlignment.self)
}

我們實(shí)現(xiàn)了AlignmentID接口,其中包含一個(gè)defaultValue,我們使用的leading/center/trailing也是實(shí)現(xiàn)了這個(gè)接口,他們的默認(rèn)值分別為0,ViewDimensions.width/2,ViewDimensions.width。這個(gè)值表示,從View的原點(diǎn)(左上角位置)偏移defaultValue(右為正,下為正)后,與Stack的基線對(duì)齊。

以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è)置到第一個(gè)參數(shù)指定的對(duì)齊類型中,替換掉它的defaultValue。
Stack在布局的時(shí)候,首先先確認(rèn)設(shè)置的對(duì)齊類型,這里我們使用的是自定義類型myHAlignment,然后查找每個(gè)子控件的ViewDimensions中myHAlignment的值,此時(shí)這個(gè)值已經(jīng)在alignmentGuide中設(shè)置過,然后與基線對(duì)齊,最終呈現(xiàn)整個(gè)Stack。

  • 如果我們沒有通過alignmentGuide設(shè)置Stack的對(duì)齊方式的值,布局時(shí)則會(huì)使用默認(rèn)值。

通過圖中的標(biāo)出的VStack的基線,解釋了5個(gè)不同的alignmentGuide設(shè)置對(duì)齊的方式。

基線與對(duì)齊

通過上例可以看出Stack只關(guān)心和它對(duì)齊方式一致的值,但我們通過alignmentGuide設(shè)置值時(shí),第一個(gè)參數(shù)不一定要和Stack中設(shè)置的對(duì)齊方式一致,如下例:

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不一樣的對(duì)齊方式
                .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,然后在第二個(gè)alignmentGuide,我們通過dimension[explicit: .leading]拿到設(shè)置的值,與其他值組合后設(shè)置到.myHAlignment內(nèi),供Stack布局時(shí)使用。
這里也演示了dimension[explicit: .leading]的作用,它返回的是一個(gè)可選型,表示如果通過alignmentGuide設(shè)置過.leading的值,則可以獲取,否則返回nil。

  • 在使用alignmentGuide設(shè)置值后,不管通過dimension[explicit: ]或者dimension[],獲取到的值時(shí)相同的。

3 自定義對(duì)齊方式

在上一小節(jié)中,我們使用了自定義的對(duì)齊方式,而自定義的對(duì)齊方式,往往可以幫助我們解決一些特殊的對(duì)齊需求,先看下面的例子:

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)
    }
}

例子中,我們自定義了一個(gè)對(duì)齊方式,默認(rèn)的對(duì)齊與.leading保持一致,然后我們將HStack中第二個(gè)Text的leading設(shè)置為myHAlignment的值,這樣VStack的基線位置就是HStack中的第二個(gè)Text保持一致,VStack的其余部件布局時(shí),會(huì)按照這個(gè)基線進(jìn)行對(duì)齊。

如果我們嘗試不使用myHAlignment,而直接使用.leading對(duì)齊方式。

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)
    }
}

此時(shí)對(duì)Text("second")的alignmentGuide設(shè)置沒有起到任何效果。

但這里設(shè)置.leading沒有起作用的原因我也不太了解,猜測(cè)可能是.leading作為系統(tǒng)自帶對(duì)齊方式,無法跨Stack傳遞或者者會(huì)在傳遞時(shí)設(shè)置初值。

?著作權(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)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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