SwiftUI 讓視圖自適應(yīng)高度的 6 種方法(一)

概覽

在 SwiftUI 的世界里,我們無(wú)數(shù)次都?jí)粝胫晥D可以自動(dòng)根據(jù)布局上下文“因勢(shì)而變”。大多數(shù)情況下,SwiftUI 會(huì)將每個(gè)視圖尺寸處理的井井有條,不過(guò)在某些時(shí)候我們還是得親力親為。

如上圖所示,無(wú)論頂部 TabView 容器里子視圖高度如何變化,TabView 本身的高度都能“隨遇而安”。如何用最簡(jiǎn)單、最現(xiàn)代化、最有趣且最切中要害的方法讓容器尺寸與子視圖的高度“如影隨形”呢?

在本篇博文中,您將學(xué)到如下內(nèi)容:

  1. 為什么要讓視圖高度自適應(yīng)?
  2. 初始工作
  3. 最古老的方法:GeometryReader

相信學(xué)完本課后,小伙伴們必能腦洞大開(kāi)、格局打開(kāi),用“千姿百態(tài)”的方法讓問(wèn)題的解決一發(fā)入魂、九轉(zhuǎn)功成!

那還等什么呢?Let‘s go!??!;)


1. 為什么要讓視圖高度自適應(yīng)?

對(duì)于一個(gè)具體的示例來(lái)說(shuō),設(shè)想這樣一種場(chǎng)景:每個(gè) SwiftUI 子視圖都包含了長(zhǎng)度不定的文本,如何穩(wěn)妥而優(yōu)雅的據(jù)此設(shè)定它們父容器的高度呢?

在下面的代碼中,我們的父容器 TabView 包含了若干個(gè) likeIdiomCard 視圖,每個(gè) likeIdiomCard 視圖對(duì)應(yīng)一條自己喜愛(ài)的成語(yǔ),其中每條成語(yǔ)又含有不固定長(zhǎng)度的文本來(lái)表示自身的釋義:

TabView {    
    ForEach(likeIdioms.chunked(into: 2), id: \.self) { idiomChunk in
        HStack {
            ForEach(idiomChunk) { idiom in
                likeIdiomCard(idiom)
            }
            
            if idiomChunk.count == 1 {
                Rectangle()
                    .foregroundStyle(.clear)
            }
        }
        
    }
}
.tabViewStyle(.page)
.frame(height: 200)

注意,為了簡(jiǎn)便起見(jiàn)我們將整個(gè) TabView 的高度設(shè)置為了 200。不過(guò),這樣做的缺點(diǎn)也顯而易見(jiàn):

  • 當(dāng)成語(yǔ)釋義太短時(shí),固定高度造成空間浪費(fèi);
  • 當(dāng)成語(yǔ)釋義太長(zhǎng)時(shí),固定高度又會(huì)造成空間不足;

如下圖所示,左側(cè)的成語(yǔ)釋義很短,會(huì)導(dǎo)致底部殘留過(guò)多空間,而右側(cè)釋義很長(zhǎng)的成語(yǔ)會(huì)迫使 likeIdiomCard 在有限的空間里居中顯示,造成頂部成語(yǔ)名稱顯示不全。

在這個(gè)接地氣的例子中,如果 TabView 的高度可以根據(jù)每個(gè) likeIdiomCard 視圖的高度自動(dòng)調(diào)整那就天衣無(wú)縫了。

當(dāng)然,我們完全可以用自定義布局(Layout)來(lái)讓挑戰(zhàn)大功告成,可是這頗有些“導(dǎo)彈打蚊子”的感覺(jué)。不過(guò)為了確保整個(gè)討論的完整性,我們?nèi)匀粫?huì)在本系列最后一篇文章介紹如何用自定義布局完成 SwiftUI 視圖高度自適應(yīng)的第 6 種實(shí)現(xiàn)。

下面,就讓我們先選出 5 種稍顯“輕量級(jí)”的方法來(lái)讓問(wèn)題迎刃而解吧。

2. 初始工作

首先,為了保存所有 likeIdiomCard 子視圖的最大高度,我們需要在主視圖中創(chuàng)建一個(gè) maxHeight 狀態(tài):

@State private var maxHeight = 0.0

我們的基本思路是,依次獲取每個(gè) likeIdiomCard 視圖的高度,并始終將最大的那個(gè)保存到 maxHeight 里。

最后,我們只需設(shè)置 TabView 的高度為 maxHeight 即可:

TabView {    
    //...
}
.tabViewStyle(.page)
.frame(height: maxHeight)

3. 最古老的方法:GeometryReader

早在 SwiftUI(iOS 13)誕生那天,GeometryReader 視圖就作為一個(gè)重要成員與之?dāng)y手并肩了。

如果我沒(méi)記錯(cuò),GeometryReader 至今已經(jīng)快 6 年了。雖然有許多不足之處,但它們并不影響我們使用 GeometryReader 來(lái)得償所愿:

likeIdiomCard(idiom)
    .background {
        GeometryReader { proxy in
            if proxy.size.height > maxHeight {
                maxHeight = proxy.size.height
            }
            
            return Color.clear
        }
    }

在上面的代碼中,我們機(jī)智的將 GeometryReader 作為 likeIdiomCard 的背景,這樣做避免了它們的尺寸“各持己見(jiàn)”。利用 @ViewBuilder 語(yǔ)法的強(qiáng)大威力,我們將 maxHeight 狀態(tài)的設(shè)置邏輯與返回“占位”視圖完美的“融為一體”。

在下篇博文中,我們將繼續(xù) SwiftUI 視圖尺寸適配之旅,介紹更多有趣的方法,不見(jiàn)不散!

總結(jié)

在本篇博文中,我們介紹了為何要讓 SwiftUI 容器與子視圖的尺寸“唇齒相依”,并討論了一種“最古老”的解決之道。

感謝觀賞,我們下篇見(jiàn)!8-)

?著作權(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)容