SwiftUI學(xué)習(xí)(4)-Hello World簇宽!

新建一個SwiftUI的項目

圖1
圖2

項目結(jié)構(gòu)

我們發(fā)現(xiàn)圖2中阴绢,項目結(jié)構(gòu)變得非常的簡單喘垂,只有兩個文件#AppName#App.swiftContentView.swift

我們先看一下相對簡單的ContentView.swift這個文件。
代碼并不多侄泽,創(chuàng)建一個結(jié)構(gòu)體遵循了View協(xié)議礁芦,重寫了body變量的get方法這時候我們發(fā)現(xiàn)一個比較讓人疑惑的事情,這個View類型用一個some來修飾悼尾。

some關(guān)鍵字是什么柿扣?
被some關(guān)鍵字修飾的類型被成為不透明類型(opaque return types)。
它的語義可以簡單的理解為“返回符合這個協(xié)議的具體類型闺魏,但不指明具體類型”未状。
它相當(dāng)于一個反向范型。通常的范型析桥,我們是需要使用協(xié)議本身司草,并不關(guān)心子類是什么艰垂,需要被調(diào)用者指定。而反向范型正好相反埋虹,反范型函數(shù)需要使用協(xié)議的子類來處理猜憎,但是返回一個遵循協(xié)議的不透明類型。

范型:具體類型由調(diào)用者指定
some:具體類型由被調(diào)用者指定

//Protocol 'View' can only be used as a generic constraint because it has Self or associated type requirements
//代碼提示以上錯誤搔课,無法編譯通過
//(1)
func someView() -> View {
    Text("Hello World")
}
//(2)
func someView2() -> Text {
    Text("Hello World")
}
//(3)
func someView3() -> some View {
    Text("Hello World")
}

函數(shù)(1)無法編譯通過胰柑,因為View協(xié)議內(nèi)部定義了Self范型,我們可以看下View協(xié)議的定義。

public protocol View {
    associatedtype Body : View
    @ViewBuilder var body: Self.Body { get }
}

這里使用了Self范型爬泥,以及Body范型柬讨。Swift語法中,這不符合語法規(guī)范急灭。因為編譯器只能知道View的接口情況姐浮,而并不知道associatedtype的具體情況谷遂。

函數(shù)(2)雖然可以編譯通過葬馋,但是我們可能用任意視圖去渲染,如果每次視圖不一樣肾扰,都要改方法的返回值畴嘶,那就太麻煩了。some關(guān)鍵字就解決了這個問題集晚。它可以有被調(diào)用者指定具體類型窗悯,而不用透明出來。

接下來我們看一下有一個struct叫ContentView_Previews它遵循了PreviewProvider協(xié)議偷拔。以下是PreviewProvider的定義

public protocol PreviewProvider : _PreviewProvider {
    associatedtype Previews : View
    @ViewBuilder static var previews: Self.Previews { get }
    static var platform: PreviewPlatform? { get }
}

跟View的定義類似蒋院,只不過preivews是statics變量。這個類對應(yīng)右手邊Canvas窗口莲绰∑劬桑可以進行實時預(yù)覽。previews可以返回多個View蛤签,每個View都會生成一個預(yù)覽窗口辞友。我們可以結(jié)合不同的參數(shù),測試各種case震肮。

不同參數(shù)的測試

struct ContentView: View {
    private var content:String = "test"
    
    init(aContent: String) {
        content = aContent
    }
    
    var body: some View {
        Text(content)
            .lineLimit(2)
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView(aContent: "Hello")
        ContentView(aContent: "Hello\nHello")
        ContentView(aContent: "Hello\nHello\nHello")

    }
}

以上代碼在Content中繪制了一個Text称龙,通過init傳入的參數(shù)展示內(nèi)容。Text被設(shè)定為最多展示兩行戳晌。
預(yù)覽窗口分別顯示了一行文本鲫尊,兩行文本,三行文本展示的樣式沦偎。

4.jpg
5.jpg
6.jpg

不同機型的測試

View協(xié)議定義了previewDevice的方法疫向,可以傳入一個PreviewDevice對象竞帽,測試不同機型的展示

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView(aContent: "Hello")
            .previewDevice(PreviewDevice(rawValue: "iPhone 8"))
    
        ContentView(aContent: "Hello")
            .previewDevice(PreviewDevice(rawValue: "iPhone 8Plus"))
        
        ContentView(aContent: "Hello")
            .previewDevice(PreviewDevice(rawValue: "iPhone X"))
        
        ContentView(aContent: "Hello")
            .previewDevice(PreviewDevice(rawValue: "iPhone 11"))

    }
}

以上代碼就可以根據(jù)不同機型預(yù)覽視圖


8.jpg
9.jpg
10.jpg
11.jpg

如果是支持多平臺的項目,還可以根據(jù)不同平臺預(yù)覽 鸿捧,重寫PreviewProvider的platform方法

static var platform: PreviewPlatform? { get } 

PreviewPlatfrom是一個枚舉屹篓,定義如下

public enum PreviewPlatform {
    case iOS
    case macOS
    case tvOS
    case watchOS
}

預(yù)覽代碼對原有的業(yè)務(wù)邏輯無入侵,而且可以事實預(yù)覽非常方便匙奴,調(diào)試階段可以把各種UI的case寫入預(yù)覽類里面堆巧,每個View創(chuàng)建時,默認(rèn)都會創(chuàng)建預(yù)覽類泼菌。

是源代碼也是樣式表

var body: some View {
        VStack {
            HStack {
                Text(content)
                    .lineLimit(2)
                    .background(Color.blue)
                    .foregroundColor(.green)
                    .lineSpacing(5)

                Text("right")
                    .background(Color.red)
            }

            Text("bottom")
                .font(.none)
        }.colorScheme(.light)
    }  

這是一個簡單的視圖布局谍肤,我們可以看到它特別像Android里面的布局文件xml。但由于它確實也是源代碼哗伯,所以在代碼風(fēng)格上尤為特殊荒揣,要避免過于混亂導(dǎo)致的維護困難。xml是通過節(jié)點對齊的方式書寫焊刹,SwiftUI也有自己的書寫樣式系任。上面的代碼我給Text設(shè)定了很多屬性。我們可以看到它是以鏈?zhǔn)椒绞秸{(diào)用的虐块。也有一些容器組件俩滥。自動生成的代碼樣式就是如上面所以,不同屬性之間通過【換行】+【Tab縮進】方式排列贺奠,容易組件于基本組件之間也用【Tab縮進】方式排列霜旧。同一層級多個組件對齊。這可能是蘋果官方推薦的一種代碼樣式儡率,建議所有人都按照這種風(fēng)格書寫挂据,由于屬性太多導(dǎo)致很多行,同一層級的多個組件之間建議增加空行儿普。

@ViewBuilder
在上述代碼我們看到SwiftUI的布局代碼跟Xml或者json有點類似崎逃。VStack是個節(jié)點里面有HStack和Text節(jié)點,HStack又有兩個Text箕肃。這其中每個節(jié)點都可以作為一個完整獨立的視圖婚脱。包括單純的Text里面的一串屬性,多一個勺像,少一個也并不影響其完整性障贸,其最終結(jié)果都返回一個View。而@ViewBuilder吟宦,可以理解為一種語法糖或者宏篮洁,其有點類似于閉包,把一段布局代碼封裝成一個特殊的對象殃姓,這個對象作為參數(shù)和返回值傳遞到各種地方進行復(fù)用袁波。

struct RedBackGroundCornerRadius<Content: View>: View {
    let content: Content
    
    init(@ViewBuilder content: () -> Content) {
        self.content = content()
    }

    var body: some View {
        content
            .background(Color.red)
            .cornerRadius(5)
    }
}

struct FontColorGreen<Content: View>: View {
    let content: Content
    init(@ViewBuilder content: () -> Content) {
        self.content = content()
    }
    
    var body: some View {
        content.foregroundColor(.green)
    }
}

struct MyView: View {
    var body: some View {
        RedBackGroundCornerRadius {
            FontColorGreen {
                Text("Hello World!")
            }
        }
    }
}

我們可以看到@ViewBuilder可以把我們既定的樣式進行封裝瓦阐,把我們的樣式封裝成一種容器類的視圖。這樣我們就可以復(fù)用我們定義的一些通用的樣式篷牌,并且以一種裝飾器的方式構(gòu)建頁面睡蟋。代碼變得簡潔易懂,復(fù)用性強枷颊。

接下來我們看另外一個文件##AppName##App.swift文件

import SwiftUI

@main
struct SwiftUIFirstAppApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView(aContent: "Hello World")
        }
    }
}

struct SwiftUIFirstAppApp_Previews: PreviewProvider {
    static var previews: some View {
           Text("Hello, World!")
    }
}

全部的代碼也非常少戳杀。首先說一下@main這個寫法。

這個寫法有點像Java的注解夭苗。實際上有點類似信卡,它定義了程序的入口點。它后面必須跟一個struct题造,后面的類必須提供一個static void main方法傍菇,否則會報錯,說實話者跟J2SE的main函數(shù)有點像界赔。App啟動后會調(diào)用這個main方法丢习。

假如你嘗試再搞一個遵循App的struct,前面加上@main仔蝌,程序完全可以正常運行泛领,而你原來的App struct就沒有用了荒吏。我也嘗試自己隨便寫一個struct敛惊,不遵循App定義,也實現(xiàn)了static void main方法绰更,程序編譯通過瞧挤,并且也可以啟動,只不過后面直接崩潰了儡湾。

再說一下App特恬,App是一個協(xié)議(protocol),其定義如下。

public protocol App {
    associatedtype Body : Scene
    @SceneBuilder var body: Self.Body { get }
    init()
}

顯而易見徐钠,Scene也是一個協(xié)議癌刽,App有一個body屬性遵循Scene協(xié)議。每個Scene有一個包含一個視圖層級樹的根視圖尝丐,并由系統(tǒng)管理其生命周期显拜。SwiftUI提供了一些具體的場景類型來處理常見的一些場景。例如文檔和設(shè)置爹袁,你也可以自定義一些Scene远荠。

同樣顯而易見,從代碼我們也知道失息,WindowGroup是實現(xiàn)了Scene的一個具體struct譬淳。WindowGroup是作為視圖層級的一個容器档址,我們可以簡單看下WindowGroup的定義。

public struct WindowGroup<Content> : Scene where Content : View {
      public init(@ViewBuilder content: () -> Content)
}

容器通常有一個Content范型邻梆,其實現(xiàn)View協(xié)議守伸,并且再初始化的時候傳入一個閉包,閉包返回一個View浦妄。WindowGroup是一個既有的含友,SwiftUI提供的Scene。Content就是WindowGroup的根視圖校辩。
如果我們想要自定義Scene窘问,可以進行如下代碼。

struct MyScene: Scene {
    var body: some Scene {
        WindowGroup {
            MyRootView()
        }
    }
}

系統(tǒng)根據(jù)程序狀態(tài)宜咒,以適合平臺特性的方式顯示W(wǎng)indowGroup的視圖惠赫。例如,系統(tǒng)允許用戶在macOS和iPadOS等平臺上創(chuàng)建或刪除包含MyRootView的窗口故黑。在其他平臺上儿咱,就會全屏展示。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末场晶,一起剝皮案震驚了整個濱河市混埠,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌诗轻,老刑警劉巖钳宪,帶你破解...
    沈念sama閱讀 206,311評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異扳炬,居然都是意外死亡吏颖,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評論 2 382
  • 文/潘曉璐 我一進店門恨樟,熙熙樓的掌柜王于貴愁眉苦臉地迎上來半醉,“玉大人,你說我怎么就攤上這事劝术∷醵啵” “怎么了?”我有些...
    開封第一講書人閱讀 152,671評論 0 342
  • 文/不壞的土叔 我叫張陵养晋,是天一觀的道長衬吆。 經(jīng)常有香客問我,道長匙握,這世上最難降的妖魔是什么咆槽? 我笑而不...
    開封第一講書人閱讀 55,252評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮圈纺,結(jié)果婚禮上秦忿,老公的妹妹穿的比我還像新娘麦射。我一直安慰自己,他們只是感情好灯谣,可當(dāng)我...
    茶點故事閱讀 64,253評論 5 371
  • 文/花漫 我一把揭開白布潜秋。 她就那樣靜靜地躺著,像睡著了一般胎许。 火紅的嫁衣襯著肌膚如雪峻呛。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,031評論 1 285
  • 那天辜窑,我揣著相機與錄音钩述,去河邊找鬼。 笑死穆碎,一個胖子當(dāng)著我的面吹牛牙勘,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播所禀,決...
    沈念sama閱讀 38,340評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼方面,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了色徘?” 一聲冷哼從身側(cè)響起恭金,我...
    開封第一講書人閱讀 36,973評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎褂策,沒想到半個月后横腿,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,466評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡辙培,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,937評論 2 323
  • 正文 我和宋清朗相戀三年蔑水,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片扬蕊。...
    茶點故事閱讀 38,039評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖丹擎,靈堂內(nèi)的尸體忽然破棺而出尾抑,到底是詐尸還是另有隱情,我是刑警寧澤蒂培,帶...
    沈念sama閱讀 33,701評論 4 323
  • 正文 年R本政府宣布再愈,位于F島的核電站,受9級特大地震影響护戳,放射性物質(zhì)發(fā)生泄漏翎冲。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,254評論 3 307
  • 文/蒙蒙 一媳荒、第九天 我趴在偏房一處隱蔽的房頂上張望抗悍。 院中可真熱鬧驹饺,春花似錦、人聲如沸缴渊。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,259評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽衔沼。三九已至蝌借,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間指蚁,已是汗流浹背菩佑。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留凝化,地道東北人擎鸠。 一個月前我還...
    沈念sama閱讀 45,497評論 2 354
  • 正文 我出身青樓,卻偏偏與公主長得像缘圈,于是被迫代替她去往敵國和親劣光。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,786評論 2 345