• <noscript id="e0iig"><kbd id="e0iig"></kbd></noscript>
  • <td id="e0iig"></td>
  • <option id="e0iig"></option>
  • <noscript id="e0iig"><source id="e0iig"></source></noscript>
  • iOS 深入理解SwiftUI

    標簽: SwiftUI


    前言: iOS開發者的UI開發體驗一直在大前端中體驗是比較差的, 原始的Frame布局系統, API比較難用的Autolayout, 性能相對較差的Xib, SB, 而對于基礎的業務開發, UI的開發又占用了大量的時間, 但是在2019 WWDC上Apple給我們帶來了新的布局方式 “SwiftUI”; SwiftUI對大量的UI控件進行了重新"定義" Text, Button, List等等

    相較于前兩篇, 這篇進行了深入的探討了SwiftUI, 在新版本編譯器上的表現, 數據流是什么, 以及SwiftUI2.0的重大變革等

    1. SwiftUI的優缺點

    1.1 SwiftUI的優勢

    • 聲明式UI語法
    • 亞秒級別的實時刷新
    • 官方原生的大力支持
    • 實時預覽功能, 可視化修改增刪代碼

    1.2 SwiftUI的劣勢

    • iOS13以后支持
    • API的不穩定性

    截屏2020-07-20 上午10.46.47截屏2020-07-20 上午10.50.54

    2. 語法細節-聲明式語法

    struct TextTest: View {
        var body: some View {
            VStack(spacing: 15) {
                Text("SwiftUI")
                Text("SwiftUI")
                    .foregroundColor(.orange)
                    .bold()
                    .font(.system(.largeTitle))
                    .fontWeight(.medium)
                    .italic()
                    .shadow(color: .black, radius: 1, x: 0, y: 2)
                
                Text(summerxx)
                    .underline(true, color: Color.gray)
                    .font(.system(size: 16, design: .serif)).onTapGesture {
                        print(summerxx)
                }
                
                HStack {
                    Text("Text")
                    Text("Text.bold").bold()
                    Text("SecureField").foregroundColor(.orange)
                }
                
                Text("Views and controls are the visual building blocks of your app’s user interface." +
                    " Use them to present your app’s content onscreen.")
                    .lineLimit(nil)
            }
        }
    }
    
    • 同是聲明式布局, Flutter給人眼花繚亂的感覺, 而Swift 的 View 組合并不是由, 分割,而是由換行分割,在 Swift 中 函數調用是可以換行分割的。這樣的方式對開發者的體驗更為友好

    3. 實時預覽

    曾經我是很羨慕前端的同學實時預覽的, 有時候一個項目編譯鏈接要幾分鐘, 我寫一個UI效果想看看很令人頭大, 但是現在Apple平臺上也擁有了同樣的方式, 這次 蘋果官方 給開發者帶來了此項功能。

    • MacOS catalina Xcode11以上, SwiftUI就可以嘗鮮此項功能可以實時預覽

    • 自動填充代碼, 大大的方便了廣大開發者. 淚目.GIF

    • 可以通過 Group 功能同時預覽多個設備,多個不同的環境

    截屏2020-07-21 22.49.25

    備注: SwiftUI 可以在 Xcode 里面直接切換 LiveMode 可以不運行設備直接進入交互模式,再具有多個預覽設備時可以很方便的動態調試 UI 布局。如何進入多設備預覽模式? 代碼如下:

    struct TextTest_Previews: PreviewProvider {
        static var previews: some View {
            Group {
                TextTest()
                    .previewDevice("iPhone 8")
                TextTest()
                    .previewDevice("iPhone 11 Pro Max")
            }
        }
    }
    
    截屏2020-07-22 22.26.19

    4. Xcode Library

    在編寫真實項目中,一個公司的 APP UI 包含成百上千種風格的 View 組件,對于 UI 組件豐富的產品,如果一個新需求可以由現有的組件組合,那么需求交付的時間也會大大縮短。
    但是對于一個大型的開發團隊而言,一個開發同學是很難知道公司內到底有多少種組件庫,而且即便知道有某種組件庫,開發同學初期看到的也是代碼,找到合適的還是有一定的難度

    在 Xcode 12 中提供了更強大的工具,一個自定義組件,只需要遵守一個 LiberyContentProvider 協議就可被Xcode識別,可以像系統控件一樣直接從 Xcode 里面識別并預覽。

    截屏2020-07-22 下午3.37.20

    5. Switch Case Support

    在 SwiftUI 的 ViewBuilder DSL體系中也支持了 Switch case 語法

     var body: some View {
                switch c {
                case .a:
                    return Text("A")
                case .b:
                    return Text("B")
                case .c:
                    return Text("C")
                }
            }
    

    6. Data Flow 數據流

    SwiftUI中的界面是嚴格數據驅動的:運行時界面的修改,只能通過修改數據來間接完成,而不是直接對界面進行修改操作。

    6.1 數據處理的基本原則

    1. Data Access as a Dependency:在 SwiftUI 中數據一旦被使用就會成為視圖的依賴,也就是說當數據發生變化了,視圖展示也會跟隨變化,不會像 MVC 模式下那樣要不停的同步數據和視圖之間的狀態變化。

    2. A Single Source Of Truth: 保持單一數據源,在 SwiftUI 中不同視圖之間如果要訪問同樣的數據,不需要各自持有數據,直接共用一個數據源即可,這樣做的好處是無需手動處理視圖和數據的同步,當數據源發生變化時會自動更新與該數據有依賴關系的視圖。

    6.2 數據流工具

    6.2.1 Property 相對簡單, 在View內部定義常量, 變量, 之后在使用

    Example Code:

    struct Model {
        
        var title: String
        var info: String
    }
    
    struct DataFlowTest : View {
        
        var model = Model(title: "WWDC 2019", info: "SwiftUI是一個全新的UI框架")
        
        var body: some View {
            
            VStack {
                Text(model.title).font(.title)
                Text(model.info)
            }
        }
    }
    
    6.2.2 @State
    • 作用是讓被它標記的屬性可以在 View 內部進行修改,因為直接修改會報錯。

    • @State修飾的屬性,只要屬性改變,SwiftUI 內部會自動的重新計算 View的body部分,構建出View Tree,由于 View 都是結構體,SwiftUI 每次構建這個 View Tree 都極快,這使得性能有很強的保障。

    • 開發者不需要關心數據和視圖的狀態同步工作,只需要關心數據的獲取以及邏輯處理,使用起來非常簡單,大大提高了開發效率。

    • 使用的時候,屬性前添加 $ 符號,這種屬性稱之為projection property(投影屬性)。

    • 只能在當前 View 的 body 內修改,所以它的使用場景是只影響當前 View 內部的變化的操作。

    • 通常應該被標記private

    6.2.3 @Binding
    • 傳統的 GUI 程序中最復雜的部分莫過于狀態管理,尤其是多數據同步,一個數據存在于不同的 UI 中,針對某個數據導致的 UI 變化理論上應該同步,狀態量的變多加上異步的操作,會使程序的可讀性直線下降,并且伴隨著而來的就是各種 Bug,SwiftUI 的解決辦法就是使用 @Binding

    • 系統提供的 Control(可操作的View) 的構造器基本都需要 @Binding 屬性,可以自動的同步來自 API 調用方的數據。

    @Binding

    在不持有數據源的情況下,任意讀取。

    @State 中獲取數據應用,并保持同步。

    Example Code

    struct DataFlowStateBindingTest: View {
        // 用@State修飾需要改變的變量
        @State private var count: Int = 0
        
        var body: some View {
            VStack {
                Text("\(count)").foregroundColor(.orange).font(.largeTitle).padding()
                // $訪問傳遞給另外一個UI
                CountButton(count: $count)
            }
        }
    }
    
    struct CountButton : View {
        // 用@Binding修飾,綁定count的值
        @Binding var count: Int
        
        var body: some View {
            Button(action: {
                // 此處修改數據會同步到上面的UI
                self.count = self.count + 1
                
            }) { Text("CountButton點擊改變")
            }
        }
    }
    

    @State只能在當前修飾的屬性改變時會觸發UI刷新,所以很適合值類型,因為對值類型里面屬性的更新,也會觸發整個值類型的重新設置。不過值類型在傳遞時會發生復制操作,所以給傳遞后的值類型即使屬性更新了也不會觸發最初的傳過來的值類型的重新賦值,所以界面并不會刷新,此時需要用@Binding,因為它可以將值類型轉為引用類型,這樣在傳遞時,其實是一個引用,任何一方修改屬性都會觸發值類型的重新設置,UI界面也隨之更新

    6.2.4 ObservableObject
    • 在應用開發過程中,很多數據其實并不是在 View 內部產生的,這些數據有可能是一些本地存儲的數據,也有可能是網絡請求的數據,這些數據默認是與 SwiftUI 沒有依賴關系的,要想建立依賴關系就要用 ObservableObject,與之配合的是@ObservedObject@Published

    • @Published 是 Xcode11 beta5 之后新增的代理屬性,此屬性如果用在 ObservableObject 內,一旦修飾的屬性發送了變化,會自動觸發 ObservableObject 的objectWillChangesend方法,刷新頁面,SwiftUI 已經默認幫我實現好了,但也可以自己手動觸發這個行為。

    • ObservableObject 是一個協議,必須要去實現該協議。

    • ObservableObject 適用于多個 UI 之間的同步數據。

    Example Code:

    class UserSettings: ObservableObject {
        // 有可能會有多個視圖使用,所以屬性未聲明為私有
        // @Published修飾需要監聽的屬性,一旦變化就會發出通知,它是發布者
        @Published var score = 123
    }
    
    struct DataFlowObservableObjectTest: View {
        // @ObservedObject修飾UserSettings
        @ObservedObject var settings = UserSettings()
    
        var body: some View {
            VStack {
                Text("人氣值: \(settings.score)").font(.title).padding()
                Button(action: {
                    self.settings.score += 1
                }) {
                    Text("增加人氣")
                }
            }
        }
    }
    

    手動發送狀態 Example Code

    class UserSettings: ObservableObject {
        
        // 1.添加發布者,實現一個屬性,名字不能亂寫,否則沒有效果
        let objectWillChange = ObjectWillChangePublisher()
        
        //2.只要name發生更改,屬性觀察器就會調用,告訴objectWillChange發布者發布有關我們的數據已更改的消息,以便所有訂閱的視圖都可以刷新的消息
        var name = "" {
            willSet {
                
                // 3.使用發布者
                objectWillChange.send()
            }
        }
    }
    
    struct DataFlowObservableObjectTest: View {
        @ObservedObject var settings = UserSettings()
        
        var body: some View {
            VStack {
                TextField("姓名", text: $settings.name)
                    .textFieldStyle(RoundedBorderTextFieldStyle()).padding()
                
                Text("你的姓名: \(settings.name)")
            }
        }
    }
    
    6.2.5 @EnvironmentObject
    • 主要是為了解決跨組件(跨應用)數據傳遞的問題。

    • 組件層級嵌套太深,就會出現數據逐層傳遞的問題, @EnvironmentObject可以幫助組件快速訪問全局數據,避免不必要的組件數據傳遞問題。

    • 使用基本與@ObservedObject一樣,但@EnvironmentObject突出強調此數據將由某個外部實體提供,所以不需要在具體使用的地方初始化,而是由外部統一提供。

    • 使用@EnvironmentObject,SwiftUI 將立即在環境中搜索正確類型的對象。如果找不到這樣的對象,則應用程序將立即崩潰。

    Example Code

    class UserSettings2: ObservableObject {
        @Published var score = 123
    }
    
    struct DataFlowEnvironmentObjectTest: View {
        
        @EnvironmentObject var settings2: UserSettings2
        
        var body: some View {
            NavigationView{
                VStack {
                    // 顯示score
                    Text("人氣值: \(settings2.score)").font(.title).padding()
                    // 改變score
                    Button(action: {
                        self.settings2.score += 1
                    }) {
                        Text("增加人氣")
                    }
                    // 跳轉下一個界面
                    NavigationLink(destination: DetailView()) {
                        Text("下一個界面")
                    }
                }
            }
        }
    }
    
    struct DetailView: View {
        
        @EnvironmentObject var settings2: UserSettings2
        
        var body: some View {
            VStack {
                Text("人氣值: \(settings2.score)").font(.title).padding()
                Button(action: {
                    self.settings2.score += 1
                }) {
                    Text("增加人氣")
                }
            }
        }
    }
    
    

    7. New Controls

    2020年真正重要的是今年新增的各類新控件,其中通過導出來自 Xcode11.5 和 Xcode12.0 beta 版本的 Swift 聲明文件,可以觀察到整個聲明文件從原來的 10769 行增加到20564行。

    新增了約87 個 struct 16 個 protocol。有了這些豐富的組件非常利于我們更好的構建我們的 APP 。

    8. 復雜列表組件

    WWDC20 SwiftUI 2.0 推出了 LazyHStack 和 lazyVStack 加上 List 渲染模式默認就是 Lazy 的直接解決了最大的性能問題。

    9. 混合UIKit

    對于舊的技術 雖然經過了很多年的歷史沉淀,有很多的積累,但是這些積累同時變成了包袱,如何背著包袱負重前行,是任何一門新技術都要考慮的問題, 顯然 Swift UI 也考慮到了,目前官方給出的文檔中, SwiftUI 是可以和 UIKit 原有的體系很輕松的混合在一起。讓開發者可以漸進式的接入 SwiftUI。

    10. 版本支持

    官方聲稱 SwiftUI 目前僅支持 iOS 13.x 以上,很多 APP 目前還在兼容 iOS 10左右 ,看起來用上 Swift UI 還需要 3年左右,但是觀察今年 蘋果的重大改變,包括, iOS 12 以下 蜂窩網絡下載可以大于 200M , 蘋果官方包優化大小 減少 50% ,iOS 13 以上甚至完全不限制在蜂窩網絡下下載的大小等, 所以我在想是否為了推行新的SwiftUI, Apple做一下向下兼容, 也未可知吧~ [笑]

    11. 全平臺支持 - SwiftUI Apps

    蘋果在最近幾年的動作中一直在搞 Apple Platform 統一的事情,從最近幾年的 iPad 多任務 多窗口,到 Mac Catalyst 再到今年更進一步直接推出了 Apple silicon 芯片更是從硬件上做到了真正統一.

    • 寫法基本無差異(SwiftUI 的理念是 Learn once, Apply anywhere)
    • AppKit, UIKit, WatchKit 都有對應的視圖實現
    截屏2020-07-21 23.36.46

    相較于硬件的變化, 作為軟件工程師我還是要更關注軟件生態的變化, 首先了解下創建 APP 時的變化, 可以看到創建新工程時有了一套全新的模板基于 SwiftUI App Lifecycle 的跨平臺項目。以下是對比

    截屏2020-07-23 00.06.16

    Before

    class SceneDelegate: UIResponder, UIWindowSceneDelegate {
        var window: UIWindow?
    
        func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
            let contentView = ContentView()
            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 SwiftUI2App: App {
        var body: some Scene {
            WindowGroup {
                ContentView()
            }
        }
    }
    

    這次重要的變化是, 這是第一次跨平臺代碼,完全無需引入任何 UIKit, APPKit, WatckKit 等相關Framewok, 即可直接運行在不同平臺上。這意味著我們后續在UI布局系統上可以逐漸擺脫對傳統命令式 UI 編程的依賴。達到真正的平臺無關。

    SwiftUI 將整個原有的平臺差異部分抽象為 App 和 Scene,對于一個 mac/iOS/iPad/watch/tv/…應用,來說 App 代表了整個應用,Scene 代表了與 Window 相關的多窗口,有些設備只有一個 Scene 有些則有多個,雖然不同的 OS 確實存在差異,但是在語義層面達到了一致。

    12. SwiftUI 的機會在哪里?

    1. 效率

    從研發效率上來說, Swift 對比 Objective-C 的精簡程度不言而喻,我嘗試寫了一些頁面代碼量都有一定程度的下降, 如果編寫 UI 界面從 UIKit 轉向了 SwiftUI 代碼量更加的精簡

    1. 體驗

    更少的代碼意味者更小的包大小,目前國內巨頭 APP iOS 端 APP 包大小都朝著 200 MB 奔去,如果能減少更多的代碼對包大小也可以在 200MB 的限制下承載更多而業務。對用戶的體驗也有較大的提升。

    更進一步由于 Swift 選擇使用值類型構建整個APP,值類型的有點在于更扁平化的內聯數據結構去分配內存,而不是使用更多間接指針引用,減少了大量不必要的堆內存消耗,意味著整體內存使用量的降低。對整個 APP 的穩定性也有較大的提高

    Demo地址** 將會持續更新SwiftUI相關代碼
    https://github.com/summerxx27/SwiftUITutorials

    版權聲明:本文為sinat_30162391原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處鏈接和本聲明。
    本文鏈接:https://blog.csdn.net/sinat_30162391/article/details/107526787

    智能推薦

    深入理解flexbox

    Flexbox 是一個 CSS3 的盒子模型 ( box model ),顧名思義它就是一個靈活的盒子 ( Flexible Box ),為什麼最近這個屬性才紅起來呢?最主要也是因為 CSS3 的規範終於普及 ( 或 IE 終於敗亡 ),加上行動裝置的發展促成了響應式布局興起,自適應長寬彈性相當大的 Flexbox 就趁勢而起了。   Flexbox 模型概念 第一步要來看 Flexbo...

    深入理解vue

    一  理解vue的核心理念 使用vue會讓人感到身心愉悅,它同時具備angular和react的優點,輕量級,api簡單,文檔齊全,簡單強大,麻雀雖小五臟俱全. 倘若用一句話來概括vue,那么我首先想到的便是官方文檔中的一句話: 這句話可能大家并不陌生,但是真正理解這句話的可能并不多,其實,讀懂了這句話,也就明白了vue的核心理念. 那么,怎樣理解什么是漸進式框架?在這之前,我們首先要理解什么是框...

    HTML中常用操作關于:頁面跳轉,空格

    1.頁面跳轉 2.空格的代替符...

    freemarker + ItextRender 根據模板生成PDF文件

    1. 制作模板 2. 獲取模板,并將所獲取的數據加載生成html文件 2. 生成PDF文件 其中由兩個地方需要注意,都是關于獲取文件路徑的問題,由于項目部署的時候是打包成jar包形式,所以在開發過程中時直接安照傳統的獲取方法沒有一點文件,但是當打包后部署,總是出錯。于是參考網上文章,先將文件讀出來到項目的臨時目錄下,然后再按正常方式加載該臨時文件; 還有一個問題至今沒有解決,就是關于生成PDF文件...

    電腦空間不夠了?教你一個小秒招快速清理 Docker 占用的磁盤空間!

    Docker 很占用空間,每當我們運行容器、拉取鏡像、部署應用、構建自己的鏡像時,我們的磁盤空間會被大量占用。 如果你也被這個問題所困擾,咱們就一起看一下 Docker 是如何使用磁盤空間的,以及如何回收。 docker 占用的空間可以通過下面的命令查看: TYPE 列出了docker 使用磁盤的 4 種類型: Images:所有鏡像占用的空間,包括拉取下來的鏡像,和本地構建的。 Con...

    猜你喜歡

    requests實現全自動PPT模板

    http://www.1ppt.com/moban/ 可以免費的下載PPT模板,當然如果要人工一個個下,還是挺麻煩的,我們可以利用requests輕松下載 訪問這個主頁,我們可以看到下面的樣式 點每一個PPT模板的圖片,我們可以進入到詳細的信息頁面,翻到下面,我們可以看到對應的下載地址 點擊這個下載的按鈕,我們便可以下載對應的PPT壓縮包 那我們就開始做吧 首先,查看網頁的源代碼,我們可以看到每一...

    Linux C系統編程-線程互斥鎖(四)

    互斥鎖 互斥鎖也是屬于線程之間處理同步互斥方式,有上鎖/解鎖兩種狀態。 互斥鎖函數接口 1)初始化互斥鎖 pthread_mutex_init() man 3 pthread_mutex_init (找不到的情況下首先 sudo apt-get install glibc-doc sudo apt-get install manpages-posix-dev) 動態初始化 int pthread_...

    統計學習方法 - 樸素貝葉斯

    引入問題:一機器在良好狀態生產合格產品幾率是 90%,在故障狀態生產合格產品幾率是 30%,機器良好的概率是 75%。若一日第一件產品是合格品,那么此日機器良好的概率是多少。 貝葉斯模型 生成模型與判別模型 判別模型,即要判斷這個東西到底是哪一類,也就是要求y,那就用給定的x去預測。 生成模型,是要生成一個模型,那就是誰根據什么生成了模型,誰就是類別y,根據的內容就是x 以上述例子,判斷一個生產出...

    styled-components —— React 中的 CSS 最佳實踐

    https://zhuanlan.zhihu.com/p/29344146 Styled-components 是目前 React 樣式方案中最受關注的一種,它既具備了 css-in-js 的模塊化與參數化優點,又完全使用CSS的書寫習慣,不會引起額外的學習成本。本文是 styled-components 作者之一 Max Stoiber 所寫,首先總結了前端組件化樣式中的最佳實踐原則,然后在此基...

    精品国产乱码久久久久久蜜桃不卡