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

開發(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)
        }
    }
}
VStack的三種對齊方式
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的三種對齊方式

通常情況下妆偏,默認(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è)置初值。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末帕识,一起剝皮案震驚了整個濱河市隧甚,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌渡冻,老刑警劉巖戚扳,帶你破解...
    沈念sama閱讀 216,324評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異族吻,居然都是意外死亡帽借,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,356評論 3 392
  • 文/潘曉璐 我一進店門超歌,熙熙樓的掌柜王于貴愁眉苦臉地迎上來砍艾,“玉大人,你說我怎么就攤上這事巍举〈嗪桑” “怎么了?”我有些...
    開封第一講書人閱讀 162,328評論 0 353
  • 文/不壞的土叔 我叫張陵懊悯,是天一觀的道長蜓谋。 經(jīng)常有香客問我,道長炭分,這世上最難降的妖魔是什么桃焕? 我笑而不...
    開封第一講書人閱讀 58,147評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮捧毛,結(jié)果婚禮上观堂,老公的妹妹穿的比我還像新娘。我一直安慰自己呀忧,他們只是感情好师痕,可當(dāng)我...
    茶點故事閱讀 67,160評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著而账,像睡著了一般胰坟。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上福扬,一...
    開封第一講書人閱讀 51,115評論 1 296
  • 那天腕铸,我揣著相機與錄音,去河邊找鬼铛碑。 笑死狠裹,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的汽烦。 我是一名探鬼主播涛菠,決...
    沈念sama閱讀 40,025評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼撇吞!你這毒婦竟也來了俗冻?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,867評論 0 274
  • 序言:老撾萬榮一對情侶失蹤牍颈,失蹤者是張志新(化名)和其女友劉穎迄薄,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體煮岁,經(jīng)...
    沈念sama閱讀 45,307評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡讥蔽,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,528評論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了画机。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片冶伞。...
    茶點故事閱讀 39,688評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖步氏,靈堂內(nèi)的尸體忽然破棺而出响禽,到底是詐尸還是另有隱情,我是刑警寧澤荚醒,帶...
    沈念sama閱讀 35,409評論 5 343
  • 正文 年R本政府宣布芋类,位于F島的核電站,受9級特大地震影響界阁,放射性物質(zhì)發(fā)生泄漏梗肝。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,001評論 3 325
  • 文/蒙蒙 一铺董、第九天 我趴在偏房一處隱蔽的房頂上張望巫击。 院中可真熱鬧,春花似錦精续、人聲如沸坝锰。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,657評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽顷级。三九已至,卻和暖如春确垫,著一層夾襖步出監(jiān)牢的瞬間弓颈,已是汗流浹背帽芽。 一陣腳步聲響...
    開封第一講書人閱讀 32,811評論 1 268
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留翔冀,地道東北人导街。 一個月前我還...
    沈念sama閱讀 47,685評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像纤子,于是被迫代替她去往敵國和親搬瑰。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,573評論 2 353