打通SwiftUI任督二脈

来源:https://www.cnblogs.com/mysweetAngleBaby/p/18041170
-Advertisement-
Play Games

序言 開年的第一篇文章,今天分享的是SwiftUI,SwiftUI出來好幾年,之前一直沒學習,所以現在才開始;如果大家還留在 iOS 開發,這們語言也是一個趨勢; 目前待業中.... 不得不說已逝的2023年,大家開始都抱著一解封,經濟都會向上轉好,可是現實不是我們想象那樣;目前我也在學習 Swif ...


swiftUI

序言

開年的第一篇文章,今天分享的是SwiftUI,SwiftUI出來好幾年,之前一直沒學習,所以現在才開始;如果大家還留在 iOS 開發,這們語言也是一個趨勢; 目前待業中.... 不得不說已逝的2023年,大家開始都抱著一解封,經濟都會向上轉好,可是現實不是我們想象那樣;目前我也在學習 SwiftUI,並且努力找工作中....;至於 2024 年經濟如何,咱們作為老百姓在大環境和全球經濟影響下;坦然面對,提升自己。 這裡不得不說國人堅韌不拔的精神。“捲” -- 努力吧Coding人

SwiftUI體驗

Xcode創建項目之後出現工程預設創建的UI界面;如下

swiftUI

一開始心裡對自己說:"SwiftUI作為iOS開發新的UI體系,為啥初創的項目這麼多代碼,給初學者看到,一種壓迫感,心想這語法好複雜,不想學了";不管你是不是這樣心裡,我剛開始看見,這麼一坨代碼,沒什麼心思,於是索性刪掉;按自己能理解學習的方式來操作;於是做了簡化:

import SwiftUI
import SwiftData

struct ContentView: View {
   
    var body: some View {
        Text("hello,word")
    }
}

#Preview {
    ContentView()
        .modelContainer(for: Item.self, inMemory: true)
}

關鍵字 some

關鍵字some啥玩意兒,完全陌生;先看看View;點擊進入源碼結構查看:

@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *)
public protocol View {

    /// 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 ``View/body-swift.property`` property.
    associatedtype Body : View

    @ViewBuilder @MainActor var body: Self.Body { get }
}

一堆英文註解估計大家不喜歡看,我就沒貼出來了;簡單來說:
View 是一個泛型協議,它定義了所有視圖類型需要遵循的介面,通過some修飾;表示 "我返回一個滿足View 協議的某種類型"。some關鍵字告訴 Swift,雖然我們知道body必須返回一個View,但我們不確定具體是哪種 View(例如,TextImageVStack 等)。

協議里有一個associatedtypebody,其實這種協議就是當作約束形式使用;只要遵守這種協議編譯器每次閉包中返回的一定是一個確定,遵守View協議的類型。

那麼蘋果工程師利用Swift5.1 Opaque return types特性,開發者提供了一個靈活的開發模式,抹掉了具體的類型,不需要修改公共API來確定每次閉包的返回類型,也降低了代碼書寫難度。(學學蘋果那些大神思想,不錯)

在來看看Preview

struct ContentView_Previews:PreviewProvider{
    static var previews: some View{
        ContentView()
        
    }
}

PreviewProvider就一個協議類,它的額作用提供swiftUI不用運行,就能直接看到UI渲染變化,我覺得這個挺好,減少開發人員對UI運行測試次數和時間,而previews就是一個靜態屬性,返回一個 View 對象,用於在預覽面板中展示。

@State屬性包裝器

@State屬性包裝器解決UI界面上,數據同步以及及時刷新的功能。一般來說數據更新完,界面 UI 同時更新。在 SwiftUI裡面,視圖中聲明的任何狀態、內容和佈局,源頭一旦發生改變,會自動更新視圖,因此,只需要一次佈局,這個時候出現了@State,它來解決與UI之間數據狀態問題。

它的概念就是:@State 是一個屬性包裝器(property wrapper),用於聲明狀態屬性(state property)
當狀態屬性發生變化時,SwiftUI 會自動更新視圖以反映最新的狀態。

屬性的值被存儲在特殊的記憶體區域中,這個區域與 View struct 是隔離的 至於被它修飾的屬性記憶體存儲與分佈現在無從得知,還沒學習到那麼深入,這事兒慢慢來,不是一天兩天的,先上個代碼看看它怎麼使用的:

import SwiftUI

struct StateBootcamp: View {
    
    @State var bgkColor:Color = Color.blue
    @State var cut:Int = 0
    
    var body: some View {
        
        ZStack{
            
            bgkColor
                .ignoresSafeArea(.all)
            
            VStack(spacing: 20){
                
                Text("Hello, World!")
                    .font(.title)
                
                Text("count:\(cut)")
                    .font(.largeTitle)
                
                HStack(spacing: 20){
                    Button("Button01") {
                        cut+=1
                        bgkColor = Color.red
                    }
                    .font(.title)
                    .foregroundColor(.white)
                    
                    Button("Button02") {
                        cut-=1
                        bgkColor = .purple
                    }
                    .font(.title)
                    .foregroundColor(.white)
                }
                Button("預設"){
                    cut=0
                    bgkColor = .blue
                }
                .font(.title)
                .foregroundColor(.white)
            }
        }
    }
}

#Preview {
    StateBootcamp()
}

其實一看代碼,就一幕瞭然,知道它的使用與作用;如果你寫過swift代碼,這些東西很好理解,但是只會OC,那麼我建議你學習下swift;在來看swiftUI語法糖才更好理解。

在看看源碼:

@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *)
@frozen @propertyWrapper public struct State<Value> : DynamicProperty {
    public init(wrappedValue value: Value)
    public init(initialValue value: Value)
    public var wrappedValue: Value { get nonmutating set }
    public var projectedValue: Binding<Value> { get }
}


@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *)
extension State where Value : ExpressibleByNilLiteral {

    /// Creates a state property without an initial value.
    ///
    /// This initializer behaves like the ``init(wrappedValue:)`` initializer
    /// with an input of `nil`. See that initializer for more information.
    @inlinable public init()
}


可以看到State是一個結構體,由@propertyWrapper包裝的。@propertyWrapper是屬性包裝器。property wrapper 做的事情大體如下:

-   為底層的存儲變數`State<Int>`自動提供一組 **getter** 和 **setter** 方法,結構體內保存了`Int`的具體數值;

-   在 body 首次求值前,將`State<Int>`關聯到當前`View`上,為它在堆中對應當前`View`分配一個存儲位置。

-   為`@State`修飾的變數設置觀察,當值改變時,觸發新一次的`body`求值,並刷新 UI。

SwiftUI基礎組件

Spacer墊片:先貼貼代碼

import SwiftUI

struct SpacerBootcampDemo: View {
    var body: some View {
        Text("Spacer UP")
            .font(.largeTitle)
        
        Spacer()
            .frame(width: 37)
            .background(.blue)
        
        Text("Spacer Down")
            .font(.largeTitle)
        
    }
}

#Preview {
    SpacerBootcampDemo()
}

在看看效果圖:

Spacer

總結:Spacer 是一個靈活的空間視圖,它的主要作用是在佈局中自動調整自身的高度和寬度,以填滿特定的空間;簡單來說,它就是一個墊片,調整自身視圖的高度,如果它周圍有其他視圖,也會受到Spacer影響。

ScrollView 如果你之前使用UIkit框架開發,在用SwiftUI,一下有點不適應,代碼和之前的 UIkit 開發模式不太一樣,但是大大縮短UI編寫時間;先上代碼:


import SwiftUI

struct ScollViewBootcamp: View {
    
    var body: some View {
        
        ScrollView{
            LazyVStack{
                ForEach(0..<20){
                    (idx) in
                    
                    VStack {
                        
                        Text("Hello, World!")
                            .font(.title)
                            .foregroundStyle(.white)
                            .frame(width: UIScreen.main.bounds.width-20,height: 350)
                            .background(Color.init(cgColor: CGColor(red: CGFloat.random(in: 0..<215)/255.0, green: CGFloat.random(in: 0..<235)/255.0, blue: CGFloat.random(in: 0...247)/255.0, alpha: 0.9)))
                            .clipShape(RoundedRectangle(cornerRadius: 10))
                        
                        Rectangle()
                            .fill(Color.init(cgColor: CGColor(red: CGFloat.random(in: 0...187)/255.0, green: CGFloat.random(in: 0..<210)/255.0, blue: CGFloat.random(in: 0...237)/255.0, alpha: 0.9)))
                            .frame(width: UIScreen.main.bounds.width-20,height: 530)
                            .clipShape(RoundedRectangle(cornerRadius: 10))
                        
                        
                        ScrollView(.horizontal,showsIndicators: false,content: {
                            LazyHStack{
                                ForEach(0..<10){
                                    idx in
                                    Rectangle()
                                        .fill(Color.init(cgColor: CGColor(red: CGFloat.random(in: 0...167)/255.0, green: CGFloat.random(in: 0...131)/255.0, blue: CGFloat.random(in: 0...89)/255.0, alpha: 0.9)))
                                        .frame(width: 200, height: 300)
                                        .clipShape(RoundedRectangle(cornerRadius: 10))
                                    
                                }
                            }
                        })
                        .padding(.leading,10)
                        .padding(.trailing,10)
                        
                        
                    }
                }
            }
            .frame(width:UIScreen.main.bounds.width)
            
        }
    }
}


#Preview {
    ScollViewBootcamp()
}

上圖看看效果:

ScrollView

簡單幾句就能實現**ScrollView**的滑動效果;非常方便。

LazyVGrid 網格佈局,先上代碼:

import SwiftUI

struct GridViewBootcamp: View {
    
    let columns=[
        GridItem(.flexible(),spacing: 6   ,alignment: .center),
        GridItem(.flexible(),spacing: 6    ,alignment: .center),
        GridItem(.flexible(),spacing: 6  ,alignment: .center),
    ]
    
    var body: some View {
        
        ScrollView{
            LazyVGrid(columns: columns,
                      alignment: .center,
                      spacing: 6,
                      pinnedViews: [.sectionHeaders],content:
                        {
                Section(content: {}, header: {
                    Text("section header 一")
                        .font(.largeTitle)
                        .foregroundStyle(.blue)
                        .frame(width: UIScreen.main.bounds.width,height: 100,alignment: .leading)
                })
                
                ForEach(0..<41){
                    index in
                    Rectangle()
                        .fill(Color.init(cgColor: CGColor(red: CGFloat.random(in: 0..<255)/255.0, green: CGFloat.random(in: 0..<255)/255.0, blue: CGFloat.random(in: 0...255)/255.0, alpha: 0.9)))
                        .frame(height: 50)
                }
                
                //-------
                Section {
                    
                } header: {
                    Text("section header 二")
                        .font(.largeTitle)
                        .foregroundStyle(.blue)
                        .frame(width: UIScreen.main.bounds.width,alignment: .leading)
                    
                }
                
                ForEach(0..<41){
                    index in
                    Rectangle()
                        .fill(Color.init(cgColor: CGColor(red: CGFloat.random(in: 0..<255)/255.0, green: CGFloat.random(in: 0..<255)/255.0, blue: CGFloat.random(in: 0...255)/255.0, alpha: 0.9)))
                        .frame(height: 50)
                }
                
            })
            .padding(.leading,6)
            .padding(.trailing,6)
            .background(.gray)
        }.background(.blue)
    }
}

#Preview {
    GridViewBootcamp()
}

效果圖:

LazyVGrid

總結:LazyVGrid大家看到這個單詞有個Lazy懶載入的意思,它的內部載入item簡單來說,就是當視圖需要時,才會執行item內容渲染功能,展示UI上。也就這點註意。

SafeArea安全區域:

import SwiftUI

struct SafeAreaBootcamp: View {
    var body: some View {
        GeometryReader{
            src in
            Rectangle()
                .fill(.blue)
                .frame(maxWidth: .infinity,
                       maxHeight: .infinity)
        }
    }
}

#Preview {
    SafeAreaBootcamp()
}

效果圖:

safeArea

可以看到上下邊距存在安全區域的,如果禁用安全區域,使用 ignoresSafeArea(.all) 可以去掉。

代碼如下:

safeArea

最後說說SwiftUI函數表達

上上代碼:


import SwiftUI

struct ExtractFunctionsBootcamp: View {
    
    @State var bgc:Color = .red
    
    var body: some View {
        normolView
    }
    
    var normolView : some View {
        setUI()
    }
    
    func chageColor() -> Void {
        self.bgc = .red
    }
    
    func setUI()->some View {
        return ZStack{
            
            bgc
                .ignoresSafeArea(.all)
            
            VStack(spacing: 20, content: {
                
                Text("Hello, World!")
                    .font(.largeTitle)
                
                Button(action: {
                    bgc = .brown
                }, label: {
                    Text("Button")
                        .font(.largeTitle)
                        .foregroundStyle(.white)
                })
                
                Button {
                    self.chageColor()
                } label: {
                    Image(systemName: "button.horizontal.top.press")
                        .resizable()
                        .foregroundColor(.white)
                        .aspectRatio(contentMode: .fill)
                        .frame(width: 50,height: 50)
                }
            })
        }
    }
}


#Preview {
    ExtractFunctionsBootcamp()
}

其實函數表達跟我們swift語法糖一樣;func 命名;這點和swift語法糖沒什麼區別。

總結(說說我的感想)

優點:

簡潔性:Swift,SwiftUI語法簡潔,編寫代碼變得更加容易和快速。

安全性:是一種類型安全的編程語言,可以在編譯時檢測類型錯誤,這幫助我們避免許多常見的錯誤,提高代碼的質量和可靠性。

互操作性:它與Objective-C語言無縫互銜接,是的OC與swift代碼混編變的更加便捷。

說完優點在說缺點

功能限制:雖然SwiftUI提供了許多常見的UI組件,但與UIKit相比,功能仍然相對有限。在某些複雜的界面需求下,可能需要使用UIKit來實現。

錯誤提示不明確:有時SwiftUI, SwiftUI的錯誤提示可能不夠明確,導致難以定位問題。

UIkit與SwiftUI缺乏無縫相容:兩者相容性不夠理想,這在業務開發中,你可能才能發現。

目前蘋果與市面大量應用也在使用Swift,SwiftUI開發應用,這們語言在應用中占有讀也是成倍增長。

路漫漫其修遠兮,吾將上下而求索

後續更新中..........


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 指標是什麼? 業務發展過程中,企業內外部都會產生很多的業務數據,對這些數據進行採集、計算、落庫、分析後,形成的統計結果稱為指標。簡單來說,指標是業務被拆解、量化後形成的數量特征,企業利用數據指標對業務進行精準的號脈,實現對業務的科學管理和有效優化。 在我們對多家企業展開深入調研的過程中,發現數據指標 ...
  • 2024年2月27日,在“2024年世界移動通信大會”(Mobile World Congress 2024,簡稱MWC 2024)上,以“雲原生×AI,躍遷新機遇”為主題的創原會圓桌成功舉辦。會上,全球企業技術精英面對面交流,圍繞雲原生×AI技術變革,分享企業在架構、算力、存儲、數智、應用開發、媒 ...
  • 在大數據處理領域,Apache SeaTunnel 已成為一款備受青睞的開源數據集成平臺,它不僅可以基於Apache Spark和Flink,而且還有社區單獨開發專屬數據集成的Zeta引擎,提供了強大的數據處理能力。隨著SeaTunnel Web的推出,用戶界面(UI)操作變得更加友好,項目部署和管 ...
  • 當用戶需要的計算或者存儲資源冗餘超出業務需求時,可在管理控制台對已有集群進行縮容操作,以便充分利用GaussDB(DWS) 提供的計算資源和存儲資源。 ...
  • Android 修改系統息屏時間. 本篇文章主要記錄下android 如何修改手機息屏時間. 目前手機屏幕超時的時間範圍一般是: 15秒 30秒 1分鐘 2分鐘 5分鐘 10分鐘 30分鐘 那如何設置超過30分鐘呢? 代碼很簡單,如下: private void changeScreenOffTim ...
  • 兩個常用的組件:Material和Scaffold修飾App和H5一樣很固定。 1.Container 2.Text 3.picture import 'package:flutter/material.dart'; void main() { runApp(MaterialApp( home: S ...
  • 本文基於Glide 4.11.0 Glide載入過程有一個解碼過程,比如將url載入為inputStream後,要將inputStream解碼為Bitmap。 從Glide源碼解析一我們大致知道了Glide載入的過程,所以我們可以直接從這裡看起,在這個過程中我們以從文件中載入bitmap為例: De ...
  • 首發原創flutter3+bitsdojo_window+getx客戶端仿微信exe聊天Flutter-WinChat。 flutter3-dart3-winchat 基於flutter3+dart3+getx+bitsdojo_window+file_picker+media_kit等技術開發桌面 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...