前言html
後續這個SwiftUI分類的文章所有都是針對SwiftUI的平常學習和理解寫的,本身利用Swift寫的第二個項目也順利上線後續的需求也不是特着急,最近正好有空就利用這段時間補一下本身對SwiftUI的理解,這個過程中正好把整個學習過程記錄下來,方便本身查閱,也但願能給須要的同窗一點點的幫助。因爲本身還欠着RxSwift的賬,此次也是想着先放棄別的帳務(欠的的確挺多的)先全心全意的把這兩塊的賬給補補,但願補上這筆帳以後本身對Swift的理解也能上一個臺階,對Siwft的理解自認爲仍是感受欠缺的,不算是真的深刻的掌握,我對SwiftUI也是在學習當中,如今能查閱的關於SwiftUI的資料不少是須要收費的,遇到問題只能想辦法努力解決,有寫的不鐘意的地方,但願多加指正!git
這兩張圖相信看過蘋果官方SwiftUI介紹文檔而且跟着寫了一遍代碼的同窗應該不陌生,固然咱們的目的不是說這兩篇的代碼,這個具體的能夠到下面鏈接去查看,我本身跟着寫了一遍以後對SwiftUI也是有了一個基本的認識。咱們在後面遇到的一些問題也會回到這個官方文檔進行一些驗證。github
SwiftUIswift
在進入項目搭建先說說我本身對SwiftUI的一個基本的認知:session
SwiftUI我以爲對iOSer來講最大的是開發UI模式的優化,針對一個需求或者是一個新的項目咱們基本上都是從寫UI開始的,根據設計圖再編造一些假數據來作,只是在寫的過程當中它的及時效果也都是腦補!SwiftUI我以爲能改變的痛點就是這點,能讓咱們實時預覽本身寫的UI效果,保持咱們代碼和界面的同步性!app
聲明式UI:關於它的理解往細了說,的確能專門寫一篇文章出來,下面這篇文章能很好的幫助理解咱們如今使用的命令式和SwiftUI採用的聲明式UI之間的區別。ide
跨平臺: 在最新的swiftUI 5.1中,咱們建立一個MultilPlatform App有了下面這些區別:學習
·Before
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`. // If using a storyboard, the `window` property will automatically be initialized and attached to the scene. // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead). // Create the SwiftUI view that provides the window contents. let contentView = BaseTabbarView() // Use a UIHostingController as window root view controller. if let windowScene = scene as? UIWindowScene { let window = UIWindow(windowScene: windowScene) window.rootViewController = UIHostingController(rootView: contentView) self.window = window window.makeKeyAndVisible() } }
·After
import SwiftUI @main struct MultiPlatformApp: App { var body: some Scene { WindowGroup { ContentView() } } }
SwiftUI 將整個原有的蘋果平臺差別部分抽象爲 App 和 Scene 部分,能夠看到Swift5.1以後在徹底無需引入UIKit 的狀況下咱們就建立了一個多平臺的App工程,代碼也從本來的基於 UI/NS HostViewController 變成了基於 App的聲明式描述。這意味着咱們後續在UI佈局系統上能夠逐漸擺脫對傳統命令式 UI 編程的依賴。達到真正的平臺無關!
下面開始咱們最多見的項目場景的搭建,一點點的學習一下SwiftUI裏面的一些知識。
實時預覽:
這個畫布的顯示控制是在下圖標註的地方,固然當你建立一個SwiftUIView的時候它是默認建立展現的,要是不見了就在下面去找:
畫布的代碼控制是在你每一個View 的最後面的遵循了PreviewProvider協議的結構體裏面的,就像下面咱們要說的基本的Tab的預覽:
struct BaseTabbarView_Previews: PreviewProvider { /// 預覽視圖,你試着在這裏多添加兩個看看效果呀 static var previews: some View { BaseTabbarView() } }
從最多見的場景搭建開始
在咱們的平常開發中,標籤(TabBar)+ 導航(Na)形式的模式是隨處可見的,咱們此次的目的是利用SwiftUI搭建這樣一個場景構建一個基本的應用,包括登陸和數據處理以及iOS常見控件在SwiftUI中的一些具體的使用,這個項目會隨着學習進度慢慢的把全部的內容都基本的補齊,下面是最基本的導航+標籤的git效果。
View
我本身以爲,要想從UIKit轉換到SwiftUI,須要咱們最早轉變的概念就是 Controller -> View 的一個改變,在使用SiwftUI寫UI的過程當中,基本上是不在須要咱們向UIkit那樣去建立Controller來管理View,在SwiftUI中最多見的就是View。
在UIKit中咱們的導航、標籤都是經過控制器來管理,可是在SwiftUI中他們分別是經過NavigationView+TabView管理的,咱們得在認識上有一個基本的轉變,從Controller到View的認識轉變。
認識一下NavigationView,先看看下面的代碼:
NavigationView{ NavigationLink.init( destination: Text("Destination"), label: { Text("Navigate") }) .navigationTitle(title) /// 留意下這個顯示模式 displayMode 分三種,具體的能夠點進去看看 /// inline 就是咱們常見的模式 /// .navigationBarTitle(title,displayMode: .inline) }
大概解析一下上面代碼的 NavigationLink,它是用來控制View之間的跳轉的:
destination:是跳轉的目標View,咱們在作一些數據傳遞的時候通常都是在這裏說明的。
label:對它的理解簡單點就是下個View的內容
再認識一下TabView,下面代碼是SwiftUI對它的基本定義和描述:
/// A view that switches between multiple child views using interactive user /// interface elements. /// /// To create a user interface with tabs, place views in a `TabView` and apply /// the ``View/tabItem(_:)`` modifier to the contents of each tab. The following /// creates a tab view with three tabs: /// /// TabView { /// Text("The First Tab") /// .tabItem { /// Image(systemName: "1.square.fill") /// Text("First") /// } /// Text("Another Tab") /// .tabItem { /// Image(systemName: "2.square.fill") /// Text("Second") /// } /// Text("The Last Tab") /// .tabItem { /// Image(systemName: "3.square.fill") /// Text("Third") /// } /// } /// .font(.headline) /// /// Tab views only support tab items of type ``Text``, ``Image``, or an image /// followed by text. Passing any other type of view results in a visible but /// empty tab item. @available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 7.0, *) public struct TabView<SelectionValue, Content> : View where SelectionValue : Hashable, Content : View { /// Creates an instance that selects from content associated with /// `Selection` values. public init(selection: Binding<SelectionValue>?, @ViewBuilder content: () -> Content) /// The content and behavior of the view. public var body: some View { get } /// The type of view representing the body of this view. /// /// When you create a custom view, Swift infers this type from your /// implementation of the required `body` property. public typealias Body = some View }
關於這個TabView在定義的上面蘋果是給出了一個使用的基本的示例的,要和咱們項目中常用的模式要綁定在一塊兒的的話就是結合他的初始化方法綁定一個@State變量使用的,具體的咱們會在後面的代碼中說的,關於這個@State我在項目Demo中有具體的解釋,包括像@bind類型或者是@EnvironmentObject這些關鍵字咱們確定是得須要學習的,就像咱們從OC轉到Swift同樣。
簡單看看Na+Tb的代碼
從SceneDelegate開始, 根控制器就是 UIHostingController,咱們須要作的第一步就是設置它的根視圖 rootView
// Create the SwiftUI view that provides the window contents. let contentView = BaseTabbarView() // Use a UIHostingController as window root view controller. if let windowScene = scene as? UIWindowScene { let window = UIWindow(windowScene: windowScene) window.rootViewController = UIHostingController(rootView: contentView) self.window = window window.makeKeyAndVisible() }
接下來是TabView的代碼,須要注意的是咱們點擊item的時候視圖切換的綁定狀態,基本上在代碼註釋中我說的比較清楚了,應該能理解的。
import SwiftUI struct BaseTabbarView: View { /// 理解State這個屬性 https://www.cnblogs.com/xiaoniuzai/p/11417123.html /* 經過使用 @State 修飾器咱們能夠關聯出 View 的狀態. SwiftUI 將會把使用過 @State 修飾器的屬性存儲到一個特殊的內存區域,而且這個區域和 View struct 是隔離的. 當 @State 裝飾過的屬性發生了變化,SwiftUI 會根據新的屬性值從新建立視圖 */ @State private var selectedTab = 0 var body: some View { /// public init(selection: Binding<SelectionValue>?, @ViewBuilder content: () -> Content) TabView(selection: $selectedTab){ HomeView(title: "首頁") .tabItem { Image(selectedTab == 0 ? "icon_home_selected" : "icon_home") Text("首頁") } .onTapGesture { selectedTab = 0 } .tag(0) ServiceView(title: "周邊") .tabItem { Image(selectedTab == 1 ? "icon_around_selected" : "icon_around") Text("周邊") } .onTapGesture { selectedTab = 1 } .tag(1) AroundView(title: "服務") .tabItem { Image(selectedTab == 2 ? "icon_service_selected" : "icon_service") Text("服務") } .onTapGesture { selectedTab = 2 } .tag(2) MineView(title: "個人").environmentObject(NavigationAction()) .tabItem { Image(selectedTab == 3 ? "icon_mine_selected" : "icon_mine") Text("個人") } .onTapGesture { selectedTab = 3 } .tag(3) /// 這個着重顏色設置能夠設置tabbaritem字體的顏色 }.accentColor(.blue) } } struct BaseTabbarView_Previews: PreviewProvider { /// 預覽視圖,你試着在這裏多添加兩個看看效果呀 static var previews: some View { BaseTabbarView() } }
在上面的代碼中,點擊的切換咱們是經過View的onTapGesture方法經過改變selectedTab 來進行控制的,而後item具體是要顯示那種風格的圖片也是經過selectedTab通過三目運算符控制,具體得咱們這裏很少解釋了廢話了,看代碼我相信都能理解。
下面的參考文章相信能幫助咱們更好的理解一下,SwiftUI!
參考文章: