[SwiftUI-Lab] 讓GeometryReader來解決吧

文章源地址:https://swiftui-lab.com/geometryreader-to-the-rescue/

作者: Javier

翻譯: Liaoworking

大多數(shù)情況下纠吴,SwiftUI都會發(fā)揮其神奇的布局的特性。但是有時候,我們需要對自定義視圖的布局進(jìn)行更多操作。目前我們有幾種工具。第一個需要我們?nèi)ヌ剿鞯木褪?strong>GeometryReader。

父級視圖想要什么?

當(dāng)我們創(chuàng)建自定義視圖時靶病,一般不用擔(dān)心旁邊視圖的布局或size。如果你想要創(chuàng)建一個正方形口予。只要用一個Rectangle娄周,就會以父級想要的size和position去畫出一個正方形。

在下面的示例中沪停,我們有一個frame為150×100的VStack煤辨。上面部分是Text,剩余空間都給了MyRectangle()木张。如圖所示都被藍(lán)色顏色填充:

struct ContentView : View {
    var body: some View {
        
        VStack {
            
            Text("Hello There!")
            MyRectangle()
            
        }.frame(width: 150, height: 100).border(Color.black)

    }
}

struct MyRectangle: View {
    var body: some View {
        Rectangle().fill(Color.blue)
    }
}
image

正如你所看到的众辨,MyRectangle(),不用去設(shè)置size舷礼,它只有一個任務(wù)鹃彻,就是畫矩形。讓SwiftUI自己去管理好父級期望的子視圖的大小和位置妻献。 這個例子里Vstack就是父級視圖蛛株。

如果你想要知道更多關(guān)于父級視圖如何確定子視圖的位置和大小。我強烈推薦你看看2019WWDC session 237Building Custom Views With SwiftUI

父級視圖會自動為子視圖找到合適的尺寸和位置育拨。但是如果你想要自定義繪制一個矩形谨履,大小是父級視圖的一半。位置位于父級視圖右邊距里5像素的視圖熬丧。
其實也并不復(fù)雜笋粟,這個時候可以用GeometryReader作為解決方案。

子視圖做了什么析蝴?

先看看Apple官方文檔如何介紹的GeometryReader:

A container view that defines its content as a function of its own size and coordinate space.
一個容器視圖害捕,根據(jù)其自身大小和坐標(biāo)空間定義其內(nèi)容。

這個解釋已經(jīng)算很詳細(xì)了闷畸。

那這句話是什么意思呢尝盼? 簡單來講GeometryReader就是另外一種view。驚不驚喜腾啥? 在SwiftUI中幾乎所有東西都是View东涡。 在下面的例子中,GeometryReader讓你定義了它的content倘待。 但是與其他View 不同疮跑。你可以拿到一些你在其他View中拿不到的信息。

上面說到想要繪制一個大小是父級視圖的一半凸舵。位置位于父級視圖右邊距里5像素的視圖∽婺铮現(xiàn)在我們有了GeometryReader, 這就很簡單了

image
struct ContentView : View {
    var body: some View {
        
        VStack {
            
            Text("Hello There!")
            MyRectangle()
            
        }.frame(width: 150, height: 100).border(Color.black)

    }
}

struct MyRectangle: View {
    var body: some View {
        GeometryReader { geometry in
            Rectangle()
                .path(in: CGRect(x: geometry.size.width + 5,
                                 y: 0,
                                 width: geometry.size.width / 2.0,
                                 height: geometry.size.height / 2.0))
                .fill(Color.blue)
            
        }
    }
}

GeometryProxy

上面的例子中啊奄,閉包中的geometry是一個GeometryProxy類的變量渐苏。我們可以通過Inspecting the View Tree(檢查視圖樹結(jié)構(gòu))這篇文章去了解更多相關(guān)內(nèi)容。

在GeometryProxy類中有兩個計算型屬性菇夸,一個方法琼富,和一個下標(biāo)取值。

public var size: CGSize { get }
public var safeAreaInsets: EdgeInsets { get }
public func frame(in coordinateSpace: CoordinateSpace) -> CGRect
public subscript<T>(anchor: Anchor<T>) -> T where T : Equatable { get }

size屬性是父級視圖建議的大小

GeometryProxy 把 safeAreaInsets也暴露給了我們庄新。

frame方法暴露給我們了父級視圖建議區(qū)域的大小位置鞠眉,可以通過.local,.global,.named()來獲取不同的坐標(biāo)空間。 .named() 用來獲取一個被命名的坐標(biāo)空間择诈。我們可以通過這個命名來獲取其他view坐標(biāo)空間械蹋。 Inspecting the View Tree 這篇文章中有具體的使用方法

最后,我們可以通過下標(biāo)取值來獲取一個錨點<T>羞芍。這個是GeometryReader的一個炫酷的功能哗戈。但也比較繁瑣,我將在second part of Inspecting the View Tree有講解荷科∥ㄒВ看完后就會有一個了解〔阶觯可以獲取視圖樹中任何子級視圖的size和x,y. 是不是很強大副渴,那你得先學(xué)啊。

吸收另外一個View的Geometry

GeometryReader 功能已經(jīng)相當(dāng)強大全度,但它如果與 .background().overlay()的modifier相結(jié)合使用煮剧,功能就會更強大。

在我見過的教程中 background 都是以下面這種形式使用的:Text("hello").background(Color.red)
第一眼看将鸵,都會以為Color.red是一個顏色參數(shù),它設(shè)置了背景色是紅色勉盅,其實并不是,Color.red是一個View顶掉!它的功能就是把父級視圖所建議的區(qū)域填充為紅色草娜。它的父級就是背景。而且背景修改了Text痒筒。所以建議Color.red所填充的區(qū)域就是Text("Hello")所在的區(qū)域宰闰。是不是很優(yōu)美茬贵?

.overlay 修改器也是同樣的道理,只是它并不是繪制背景移袍,而是繪制前景而已解藻。

我們已經(jīng)知道了,我們可以給任意一個view使用.Color()方法還有 .background()方法葡盗。下面我們將結(jié)合GeometryReader螟左,畫一個每個角指定不同的半徑的矩形的例子來演示如何利用它們。

image

具體實現(xiàn)如下:

struct ContentView : View {
    var body: some View {
        HStack {
            Text("SwiftUI")
                .foregroundColor(.black).font(.title).padding(15)
                .background(RoundedCorners(color: .green, tr: 30, bl: 30))
            
            Text("Lab")
                .foregroundColor(.black).font(.title).padding(15)
                .background(RoundedCorners(color: .blue, tl: 30, br: 30))
            
        }.padding(20).border(Color.gray).shadow(radius: 3)
    }
}

struct RoundedCorners: View {
    var color: Color = .black
    var tl: CGFloat = 0.0
    var tr: CGFloat = 0.0
    var bl: CGFloat = 0.0
    var br: CGFloat = 0.0
    
    var body: some View {
        GeometryReader { geometry in
            Path { path in
                
                let w = geometry.size.width
                let h = geometry.size.height
                
                // We make sure the redius does not exceed the bounds dimensions
                let tr = min(min(self.tr, h/2), w/2)
                let tl = min(min(self.tl, h/2), w/2)
                let bl = min(min(self.bl, h/2), w/2)
                let br = min(min(self.br, h/2), w/2)
                
                path.move(to: CGPoint(x: w / 2.0, y: 0))
                path.addLine(to: CGPoint(x: w - tr, y: 0))
                path.addArc(center: CGPoint(x: w - tr, y: tr), radius: tr, startAngle: Angle(degrees: -90), endAngle: Angle(degrees: 0), clockwise: false)
                path.addLine(to: CGPoint(x: w, y: h - br))
                path.addArc(center: CGPoint(x: w - br, y: h - br), radius: br, startAngle: Angle(degrees: 0), endAngle: Angle(degrees: 90), clockwise: false)
                path.addLine(to: CGPoint(x: bl, y: h))
                path.addArc(center: CGPoint(x: bl, y: h - bl), radius: bl, startAngle: Angle(degrees: 90), endAngle: Angle(degrees: 180), clockwise: false)
                path.addLine(to: CGPoint(x: 0, y: tl))
                path.addArc(center: CGPoint(x: tl, y: tl), radius: tl, startAngle: Angle(degrees: 180), endAngle: Angle(degrees: 270), clockwise: false)
                }
                .fill(self.color)
        }
    }
}

另外觅够,我們對自定義的Overlay設(shè)置透明度為0.5胶背,設(shè)置在Text上。
代碼如下:

Text("SwiftUI")
    .foregroundColor(.black).font(.title).padding(15)
    .overlay(RoundedCorners(color: .green, tr: 30, bl: 30).opacity(0.5))
Text("Lab")
    .foregroundColor(.black).font(.title).padding(15)
    .overlay(RoundedCorners(color: .blue, tl: 30, br: 30).opacity(0.5))

關(guān)于雞和雞蛋的問題

當(dāng)你開始使用GeometryReader喘先, 你就會發(fā)現(xiàn)所謂的雞和雞蛋的問題钳吟。
因為GeometryReader需要給子級試圖提供可用空間,它首先需要盡可能多的占用空間窘拯。 但是子類可能會設(shè)置一個小的空間砸抛,這時候GeometryReader還是盡可能的保持大。
一旦子級試圖確定了其所需空間树枫, 你可能會被迫縮小GeometryReader直焙。這時候子級試圖就會GeometryReader計算出的新的大小做出反應(yīng)。 一個循環(huán)就產(chǎn)生了砂轻。

所以 當(dāng)遇到是子級試圖依賴父級試圖的大小奔誓,還是父級試圖依賴于子級試圖的大小。 可能GeometryReader并不適合解決你的布局問題搔涝。由此厨喂,你可以看看我的下一篇文章Preferences and Anchor Preferences.

總結(jié)

今天所學(xué)的GeometryReader讓我們的自定義view知道了它們所需的大小和位置。 我們還學(xué)習(xí)了獲取其他view的geometry庄呈。
這只是很第一篇官方并沒有提及的講SwiftUI中的布局工具的文章蜕煌,接下來我們將會深度研究view的數(shù)層次,和子級試圖如何把數(shù)據(jù)向上傳遞诬留。點我查看

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末斜纪,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子文兑,更是在濱河造成了極大的恐慌盒刚,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,544評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件绿贞,死亡現(xiàn)場離奇詭異因块,居然都是意外死亡,警方通過查閱死者的電腦和手機籍铁,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,430評論 3 392
  • 文/潘曉璐 我一進(jìn)店門涡上,熙熙樓的掌柜王于貴愁眉苦臉地迎上來趾断,“玉大人,你說我怎么就攤上這事吩愧〖弑” “怎么了?”我有些...
    開封第一講書人閱讀 162,764評論 0 353
  • 文/不壞的土叔 我叫張陵耻警,是天一觀的道長。 經(jīng)常有香客問我甸怕,道長甘穿,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,193評論 1 292
  • 正文 為了忘掉前任梢杭,我火速辦了婚禮温兼,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘武契。我一直安慰自己募判,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,216評論 6 388
  • 文/花漫 我一把揭開白布咒唆。 她就那樣靜靜地躺著届垫,像睡著了一般。 火紅的嫁衣襯著肌膚如雪全释。 梳的紋絲不亂的頭發(fā)上装处,一...
    開封第一講書人閱讀 51,182評論 1 299
  • 那天,我揣著相機與錄音浸船,去河邊找鬼妄迁。 笑死,一個胖子當(dāng)著我的面吹牛李命,可吹牛的內(nèi)容都是我干的登淘。 我是一名探鬼主播,決...
    沈念sama閱讀 40,063評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼封字,長吁一口氣:“原來是場噩夢啊……” “哼黔州!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起阔籽,我...
    開封第一講書人閱讀 38,917評論 0 274
  • 序言:老撾萬榮一對情侶失蹤辩撑,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后仿耽,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體合冀,經(jīng)...
    沈念sama閱讀 45,329評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,543評論 2 332
  • 正文 我和宋清朗相戀三年项贺,在試婚紗的時候發(fā)現(xiàn)自己被綠了君躺。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片峭判。...
    茶點故事閱讀 39,722評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖棕叫,靈堂內(nèi)的尸體忽然破棺而出林螃,到底是詐尸還是另有隱情,我是刑警寧澤俺泣,帶...
    沈念sama閱讀 35,425評論 5 343
  • 正文 年R本政府宣布疗认,位于F島的核電站,受9級特大地震影響伏钠,放射性物質(zhì)發(fā)生泄漏横漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,019評論 3 326
  • 文/蒙蒙 一熟掂、第九天 我趴在偏房一處隱蔽的房頂上張望缎浇。 院中可真熱鬧,春花似錦赴肚、人聲如沸素跺。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,671評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽指厌。三九已至,卻和暖如春踊跟,著一層夾襖步出監(jiān)牢的瞬間仑乌,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,825評論 1 269
  • 我被黑心中介騙來泰國打工琴锭, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留晰甚,地道東北人。 一個月前我還...
    沈念sama閱讀 47,729評論 2 368
  • 正文 我出身青樓决帖,卻偏偏與公主長得像厕九,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子地回,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,614評論 2 353

推薦閱讀更多精彩內(nèi)容