swiftui Text控制多行顯示fixedSize和PreferenceKey

需求:
body要求設置一個最小高度minHeight,body內的content進行垂直排列VStack。且當content內容較少時魔慷,置頂展示。
問題1:
VStack中無法設置alignment: .top
問題2:
使用Spacer()會導致垂直距離著恩,直接被撐開院尔。無法滿足minHeight的要求蜻展。


處理方法:
所以為了使用Spacer(),就要直接計算出content的高度邀摆,將body的height設置為:max(content高度纵顾,minHeight)

一:.preference(key:defaultValue:transform:)
用途:
  • 用于定義一種偏好(preference),可以在視圖層次結構中傳遞特定類型的值栋盹。
  • 通過在視圖樹中的不同位置設置和讀取偏好值施逾,可以實現跨視圖的信息傳遞和響應。

其實就是在你想在視圖樹中哪個位置獲取height or size 等信息例获,用這個方法把信息發(fā)送出去汉额,類似與對應UIKit的postNotification。

二:.onPreferenceChange(key:perform:)
用途:
  • 用于響應由.preference或.anchorPreference定義的偏好變化榨汤。
  • 當偏好值發(fā)生變化時蠕搜,.onPreferenceChange中的代碼塊將被執(zhí)行,允許你根據偏好的變化進行相應的操作收壕。

其實就是用來監(jiān)聽當value發(fā)生變化妓灌,這個方法會被響應,類似與UIKit中的addNotification蜜宪。

所以我們有了下面的代碼

struct HeightPreferenceKey: PreferenceKey {
    static var defaultValue: CGFloat = 0
    static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) {
        value = nextValue()
    }
}

struct ContentView: View {
    @State private var vstackHeight: CGFloat = 0
        VStack {
            VStack(spacing: .zero) {
                VStack(spacing: .zero) {
                    ForEach(0..<5) { tag in
                        Text("------TextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextText=========")
                            .foregroundStyle(Color.gray)
                            .font(.system(size: 14))
                            .background{Color.yellow}
                            .padding(Constants.spacing10)
                            .frame(minHeight: Constants.spacing20)
                            .frame(maxWidth:.infinity, alignment:.leading)
                            .background(GeometryReader { geo in
                                Color.red.preference(key: HeightPreferenceKey.self, value: geo.size.height)
                            })
                    }
                }
                Spacer()
            }
            .background {
                Color.purple
            }
            .frame(height: max(300, vstackHeight))
            .onPreferenceChange(HeightPreferenceKey.self) { height in
                vstackHeight = height
            }
            Text("---jlifedkljdfsjkldskjldfjkdfjksdjjlifedkljdfsjkldskjldfjkdfjksdjjlifedkljdfsjkldskjldfjkdfjksdjjlife---")
                .foregroundStyle(Color.gray)
                .font(.system(size: 18))
                .padding(Constants.spacing10)
                .background(Color.blue)
            Spacer()
        }
        .background {
            Color.black
        }
    }
}
Simulator Screenshot - iPhone 15 Pro - 2024-09-15 at 12.28.48.png

當內容過多時虫埂,出現文字顯示不全的問題

于是給Text()添加fixedSize屬性

.fixedSize(horizontal: false, vertical: true)

即:

ForEach(0..<5) { tag in
    Text("------TextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextText=========")
        .foregroundStyle(Color.gray)
        .font(.system(size: 14))
        .background{Color.yellow}
        .padding(Constants.spacing10)
        .frame(minHeight: Constants.spacing20)
        .frame(maxWidth:.infinity, alignment:.leading)
        .fixedSize(horizontal: false, vertical: true) // 添加這個屬性
        .background(GeometryReader { geo in
            Color.red.preference(key: HeightPreferenceKey.self, value: geo.size.height)
        })
}
Simulator Screenshot - iPhone 15 Pro - 2024-09-15 at 12.29.26.png

添加.fixedSize()屬性后,內容雖然顯示完整了圃验,但又能看到掉伏,外層的VStack()并沒有撐開,隨意底部藍色區(qū)域覆蓋了紅色的區(qū)域澳窑。

這是因為.preference()取值出現問題斧散,繼續(xù)修改:

struct HeightPreferenceKey: PreferenceKey {
    static var defaultValue: CGFloat = 0
    static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) {
//        value = nextValue()
        value += nextValue()
    }
}
Simulator Screenshot - iPhone 15 Pro - 2024-09-15 at 12.29.42.png
完整代碼為:
import SwiftUI
struct HeightPreferenceKey: PreferenceKey {
    static var defaultValue: CGFloat = 0
    static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) {
//        value = nextValue()
        value += nextValue()
    }
}

struct ContentView: View {
    @State private var vstackHeight: CGFloat = 0
    var body: some View {
        VStack(spacing: .zero) {
            VStack(spacing: .zero) {
                VStack(spacing: .zero) {
                    ForEach(0..<3) { tag in
                        Text("------TextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextText=========")
                            .foregroundStyle(Color.gray)
                            .font(.system(size: 14))
                            .background{Color.yellow}
                            .padding(Constants.spacing10)
                            .frame(minHeight: Constants.spacing20)
                            .frame(maxWidth:.infinity, alignment:.leading)
                            .fixedSize(horizontal: false, vertical: true) // 添加這個屬性
                            .background(GeometryReader { geo in
                                Color.clear.preference(key: HeightPreferenceKey.self, value: geo.size.height)
                            })
                    }
                }
                if vstackHeight < 300  { Spacer() }
            }
            .background {
                Color.purple
            }
            .frame(height: max(300, vstackHeight))
            .onPreferenceChange(HeightPreferenceKey.self) { height in
                vstackHeight = height
            }
            Text("---jlifedkljdfsjkldskjldfjkdfjksdjjlifedkljdfsjkldskjldfjkdfjksdjjlifedkljdfsjkldskjldfjkdfjksdjjlife---")
                .foregroundStyle(Color.gray)
                .font(.system(size: 18))
                .padding(Constants.spacing10)
                .background(Color.blue)
            Spacer(minLength: 0)
        }
        .background {
            Color.black
        }
    }
}

思考如下代碼中將:
Color.clear.preference(key: HeightPreferenceKey.self, value: geo.size.height)
直接放置在外層VStack中,
發(fā)現并不好使照捡,具體原因還不知道,難道.preference()不能放置在外層VStack()中话侧?
原諒樓主也是一個小白而已栗精。。瞻鹏。有知道的請下面評論悲立,非常感謝~

struct HeightPreferenceKey: PreferenceKey {
    static var defaultValue: CGFloat = 0
    static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) {
        value = nextValue()
//        value += nextValue()
    }
}
VStack(spacing: .zero) {
    ForEach(0..<3) { tag in
        Text("------TextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextText=========")
            .foregroundStyle(Color.gray)
            .font(.system(size: 14))
            .background{Color.yellow}
            .padding(Constants.spacing10)
            .frame(minHeight: Constants.spacing20)
            .frame(maxWidth:.infinity, alignment:.leading)
            .fixedSize(horizontal: false, vertical: true) // 添加這個屬性
//                            .background(GeometryReader { geo in
//                                Color.clear.preference(key: HeightPreferenceKey.self, value: geo.size.height)
//                            })
    }
}
.background(GeometryReader { geo in
    Color.clear.preference(key: HeightPreferenceKey.self, value: geo.size.height)
})

New: 上面之所以會有問題,是因為對frame的height獲取后新博,又對superview的height設置薪夕,導致重新約束布局,所以會引起問題赫悄。

法一: 通過獲取到的height原献,當height < minHeight時:對內部整體的view做一個y軸的offset馏慨。
.offset(x: 0, y: vstackHeight < 300 ? -(300 - vstackHeight) / 2.0 : 0)
法二:就是通過ZStack的alignment,進行約束控制

其中VStack{}.frame(minHeight: 300)是為了撐開ZStack姑隅,這樣就能使content安裝.top對齊

ZStack(alignment: .top) {
    VStack{}.frame(minHeight: 300)
    VStack(spacing: .zero) {
        ForEach(0..<1) { tag in
            Text("------TextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextText=========")
                .foregroundStyle(Color.gray)
                .font(.system(size: 14))
                .background{Color.yellow}
                .padding(Constants.spacing10)
                .frame(minHeight: Constants.spacing20)
        }
    }
}
注意不能直接設置在下面這里写隶,因為這樣VStack又被撐開了,內部還是會按VStack居中布局讲仰。慕趴。。
ZStack(alignment: .top) {
    VStack(spacing: .zero) {
        ForEach(0..<1) { tag in
            Text("------TextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextText=========")
                .foregroundStyle(Color.gray)
                .font(.system(size: 14))
                .background{Color.yellow}
                .padding(Constants.spacing10)
                .frame(minHeight: Constants.spacing20)
        }
    }
    .frame(minHeight: 300)//不能設置在這里
}
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末鄙陡,一起剝皮案震驚了整個濱河市冕房,隨后出現的幾起案子,更是在濱河造成了極大的恐慌趁矾,老刑警劉巖耙册,帶你破解...
    沈念sama閱讀 219,188評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現場離奇詭異愈魏,居然都是意外死亡觅玻,警方通過查閱死者的電腦和手機,發(fā)現死者居然都...
    沈念sama閱讀 93,464評論 3 395
  • 文/潘曉璐 我一進店門培漏,熙熙樓的掌柜王于貴愁眉苦臉地迎上來溪厘,“玉大人,你說我怎么就攤上這事牌柄』” “怎么了?”我有些...
    開封第一講書人閱讀 165,562評論 0 356
  • 文/不壞的土叔 我叫張陵珊佣,是天一觀的道長蹋宦。 經常有香客問我,道長咒锻,這世上最難降的妖魔是什么冷冗? 我笑而不...
    開封第一講書人閱讀 58,893評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮惑艇,結果婚禮上蒿辙,老公的妹妹穿的比我還像新娘。我一直安慰自己滨巴,他們只是感情好思灌,可當我...
    茶點故事閱讀 67,917評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著恭取,像睡著了一般泰偿。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上蜈垮,一...
    開封第一講書人閱讀 51,708評論 1 305
  • 那天耗跛,我揣著相機與錄音裕照,去河邊找鬼。 笑死课兄,一個胖子當著我的面吹牛牍氛,可吹牛的內容都是我干的。 我是一名探鬼主播烟阐,決...
    沈念sama閱讀 40,430評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼搬俊,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了蜒茄?” 一聲冷哼從身側響起唉擂,我...
    開封第一講書人閱讀 39,342評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎檀葛,沒想到半個月后玩祟,有當地人在樹林里發(fā)現了一具尸體,經...
    沈念sama閱讀 45,801評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡屿聋,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,976評論 3 337
  • 正文 我和宋清朗相戀三年空扎,在試婚紗的時候發(fā)現自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片润讥。...
    茶點故事閱讀 40,115評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡转锈,死狀恐怖,靈堂內的尸體忽然破棺而出楚殿,到底是詐尸還是另有隱情撮慨,我是刑警寧澤,帶...
    沈念sama閱讀 35,804評論 5 346
  • 正文 年R本政府宣布脆粥,位于F島的核電站砌溺,受9級特大地震影響,放射性物質發(fā)生泄漏变隔。R本人自食惡果不足惜规伐,卻給世界環(huán)境...
    茶點故事閱讀 41,458評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望匣缘。 院中可真熱鬧猖闪,春花似錦、人聲如沸孵户。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,008評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽夏哭。三九已至,卻和暖如春献联,著一層夾襖步出監(jiān)牢的瞬間竖配,已是汗流浹背何址。 一陣腳步聲響...
    開封第一講書人閱讀 33,135評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留进胯,地道東北人用爪。 一個月前我還...
    沈念sama閱讀 48,365評論 3 373
  • 正文 我出身青樓,卻偏偏與公主長得像胁镐,于是被迫代替她去往敵國和親偎血。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,055評論 2 355

推薦閱讀更多精彩內容