SwiftUI之View Tree(AnchorPreferences)

在之前的SwiftUI之View Tree(PreferenceKey)這篇文章中,介紹了父view如何通過(guò)PreferenceKey獲取子view的信息绵咱,我們演示了這樣一個(gè)效果:

Kapture 2020-07-09 at 14.41.20.gif

由于能夠獲取所有子view的位置信息砌梆,因此綠色圓圈可以移動(dòng)到準(zhǔn)確的位置上濒憋,核心代碼如下:

struct NumberPreferenceViewSetter: View {
    let idx: Int
    var body: some View {
        GeometryReader { proxy in
            Circle()
                .stroke(Color.clear, lineWidth: 5)
                .preference(key: NumberPreferenceKey.self, value: [NumberPreferenceValue(viewIdx: idx, rect: proxy.frame(in: .named("ZStackSpace")))])
        }

    }
}
var body: some View {
    ZStack(alignment: .topLeading) {
        ...

        VStack {
            ...
        }
    }
    .onPreferenceChange(NumberPreferenceKey.self) { preferences in
        for pre in preferences {
            self.rects[pre.viewIdx] = pre.rect
        }
    }
    .coordinateSpace(name: "ZStackSpace")

想了解更多的同學(xué),可以去看那篇文章但壮,在上邊的代碼中冀泻,我們必須把Circle()放到GeometryReader中才能獲取到view的frame信息常侣。

那么在本篇文章中,我們通過(guò)anchorPreference腔长,可以輕松獲取view的位置信息袭祟,我們通過(guò)演示下邊這樣一個(gè)效果,來(lái)說(shuō)明它的用法:

Kapture 2020-07-13 at 19.09.22.gif

首先捞附,我們?nèi)匀恍枰x子view需要傳遞的數(shù)據(jù)信息是什么巾乳?

struct MySegementPreferenceData {
    let viewIdx: Int
    let bounds: Anchor<CGRect>
    var topLeading: Anchor<CGPoint>? = nil
}

然后自定義一個(gè)唯一的PreferenceKey類(lèi)別,用于把同一個(gè)key下的數(shù)據(jù)匯總起來(lái):

struct MySegementPreferenceKey: PreferenceKey {
    typealias Value = [MySegementPreferenceData]
    static var defaultValue: Value = []
    static func reduce(value: inout [MySegementPreferenceData], nextValue: () -> [MySegementPreferenceData]) {
        value.append(contentsOf: nextValue())
    }
}

最后在通過(guò).anchorPreferencemodifier把數(shù)據(jù)傳遞進(jìn)來(lái):

    struct SegementItem: View {
        let title: String
        let index: Int
        let selectedIndex: Int
        
        var body: some View {
            Text(title)
                ...
                .anchorPreference(key: MySegementPreferenceKey.self, value: .bounds, transform:                             {
                    [MySegementPreferenceData(viewIdx: index, bounds: $0)]
                })
        }
    }

通過(guò)上邊代碼鸟召,可以看出胆绊,value: .bounds中的bounds表明了我們要傳遞的位置信息,這里也可以是.topLeading,.bottomTrailing等等欧募。

像上邊的MySegementPreferenceData压状,包含了兩個(gè)信息,分別是bounds和topLeading跟继,如果連續(xù)寫(xiě)2個(gè).anchorPreference是不行的种冬,最后邊的數(shù)據(jù)會(huì)覆蓋前邊的數(shù)據(jù),遇到這種情況舔糖,需要使用.transformAnchorPreference:

.anchorPreference(key: MySegementPreferenceKey.self, value: .bounds, transform: {
    [MySegementPreferenceData(viewIdx: index, bounds: $0)]
})
.transformAnchorPreference(key: MySegementPreferenceKey.self, value: .topLeading, transform: { (value: inout [MySegementPreferenceData], anchor: Anchor<CGPoint>) in
    value[0].topLeading = anchor
})

.transformAnchorPreference把獲取的.topLeading賦值給了MySegementPreferenceData娱两。

本篇文章的演示代碼可以在這里下載:AnchorPreferenceDemo.swift

有意思的一點(diǎn)是,如果我打開(kāi)代碼中注釋的那一行代碼.animation(.easeInOut)

func createBottomLine(_ proxy: GeometryProxy, preferences: [MySegementPreferenceData]) -> some View {
    let p = preferences.first(where: { $0.viewIdx == self.selectedIndex })

    let bounds = proxy[p!.bounds]

    return RoundedRectangle(cornerRadius: 2.5)
        .foregroundColor(.green)
        .frame(width: bounds.width, height: 5)
        .offset(x: bounds.minX, y: bounds.height - 5)
            /// 開(kāi)啟動(dòng)畫(huà)
        .animation(.easeInOut)
}

運(yùn)行后的效果非常有意思:

Kapture 2020-07-13 at 19.26.43.gif

總結(jié)

可能大家會(huì)有疑問(wèn)金吗,在哪種情況下需要用到AnchorPreferences呢十兢?其實(shí)很簡(jiǎn)單,當(dāng)你需要子view的位置信息的時(shí)候摇庙,考慮這個(gè)技術(shù)就可以了旱物。我們會(huì)在接下來(lái)的兩篇文章中講到幾個(gè)實(shí)際應(yīng)用。其中二叉樹(shù)的例子還算有趣卫袒。

企業(yè)微信截圖_d7318bbd-82ed-40c4-acf6-534a33e83b38.png

SwiftUI集合:[FuckingSwiftUI

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末宵呛,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子夕凝,更是在濱河造成了極大的恐慌烤蜕,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,372評(píng)論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件迹冤,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡虎忌,警方通過(guò)查閱死者的電腦和手機(jī)泡徙,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)膜蠢,“玉大人堪藐,你說(shuō)我怎么就攤上這事莉兰。” “怎么了礁竞?”我有些...
    開(kāi)封第一講書(shū)人閱讀 162,415評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵糖荒,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我模捂,道長(zhǎng)捶朵,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,157評(píng)論 1 292
  • 正文 為了忘掉前任狂男,我火速辦了婚禮综看,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘岖食。我一直安慰自己红碑,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,171評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布泡垃。 她就那樣靜靜地躺著析珊,像睡著了一般。 火紅的嫁衣襯著肌膚如雪蔑穴。 梳的紋絲不亂的頭發(fā)上忠寻,一...
    開(kāi)封第一講書(shū)人閱讀 51,125評(píng)論 1 297
  • 那天,我揣著相機(jī)與錄音澎剥,去河邊找鬼锡溯。 笑死,一個(gè)胖子當(dāng)著我的面吹牛哑姚,可吹牛的內(nèi)容都是我干的祭饭。 我是一名探鬼主播,決...
    沈念sama閱讀 40,028評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼叙量,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼倡蝙!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起绞佩,我...
    開(kāi)封第一講書(shū)人閱讀 38,887評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤寺鸥,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后品山,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體胆建,經(jīng)...
    沈念sama閱讀 45,310評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,533評(píng)論 2 332
  • 正文 我和宋清朗相戀三年肘交,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了笆载。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,690評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖凉驻,靈堂內(nèi)的尸體忽然破棺而出腻要,到底是詐尸還是另有隱情,我是刑警寧澤涝登,帶...
    沈念sama閱讀 35,411評(píng)論 5 343
  • 正文 年R本政府宣布雄家,位于F島的核電站,受9級(jí)特大地震影響胀滚,放射性物質(zhì)發(fā)生泄漏趟济。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,004評(píng)論 3 325
  • 文/蒙蒙 一蛛淋、第九天 我趴在偏房一處隱蔽的房頂上張望咙好。 院中可真熱鬧,春花似錦褐荷、人聲如沸勾效。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,659評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)层宫。三九已至,卻和暖如春其监,著一層夾襖步出監(jiān)牢的瞬間萌腿,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,812評(píng)論 1 268
  • 我被黑心中介騙來(lái)泰國(guó)打工抖苦, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留毁菱,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,693評(píng)論 2 368
  • 正文 我出身青樓锌历,卻偏偏與公主長(zhǎng)得像贮庞,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子究西,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,577評(píng)論 2 353