打通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
  • .Net8.0 Blazor Hybird 桌面端 (WPF/Winform) 實測可以完整運行在 win7sp1/win10/win11. 如果用其他工具打包,還可以運行在mac/linux下, 傳送門BlazorHybrid 發佈為無依賴包方式 安裝 WebView2Runtime 1.57 M ...
  • 目錄前言PostgreSql安裝測試額外Nuget安裝Person.cs模擬運行Navicate連postgresql解決方案Garnet為什麼要選擇Garnet而不是RedisRedis不再開源Windows版的Redis是由微軟維護的Windows Redis版本老舊,後續可能不再更新Garne ...
  • C#TMS系統代碼-聯表報表學習 領導被裁了之後很快就有人上任了,幾乎是無縫銜接,很難讓我不想到這早就決定好了。我的職責沒有任何變化。感受下來這個系統封裝程度很高,我只要會調用方法就行。這個系統交付之後不會有太多問題,更多應該是做小需求,有大的開發任務應該也是第二期的事,嗯?怎麼感覺我變成運維了?而 ...
  • 我在隨筆《EAV模型(實體-屬性-值)的設計和低代碼的處理方案(1)》中介紹了一些基本的EAV模型設計知識和基於Winform場景下低代碼(或者說無代碼)的一些實現思路,在本篇隨筆中,我們來分析一下這種針對通用業務,且只需定義就能構建業務模塊存儲和界面的解決方案,其中的數據查詢處理的操作。 ...
  • 對某個遠程伺服器啟用和設置NTP服務(Windows系統) 打開註冊表 HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\W32Time\TimeProviders\NtpServer 將 Enabled 的值設置為 1,這將啟用NTP伺服器功 ...
  • title: Django信號與擴展:深入理解與實踐 date: 2024/5/15 22:40:52 updated: 2024/5/15 22:40:52 categories: 後端開發 tags: Django 信號 松耦合 觀察者 擴展 安全 性能 第一部分:Django信號基礎 Djan ...
  • 使用xadmin2遇到的問題&解決 環境配置: 使用的模塊版本: 關聯的包 Django 3.2.15 mysqlclient 2.2.4 xadmin 2.0.1 django-crispy-forms >= 1.6.0 django-import-export >= 0.5.1 django-r ...
  • 今天我打算整點兒不一樣的內容,通過之前學習的TransformerMap和LazyMap鏈,想搞點不一樣的,所以我關註了另外一條鏈DefaultedMap鏈,主要調用鏈為: 調用鏈詳細描述: ObjectInputStream.readObject() DefaultedMap.readObject ...
  • 後端應用級開發者該如何擁抱 AI GC?就是在這樣的一個大的浪潮下,我們的傳統的應用級開發者。我們該如何選擇職業或者是如何去快速轉型,跟上這樣的一個行業的一個浪潮? 0 AI金字塔模型 越往上它的整個難度就是職業機會也好,或者說是整個的這個運作也好,它的難度會越大,然後越往下機會就會越多,所以這是一 ...
  • @Autowired是Spring框架提供的註解,@Resource是Java EE 5規範提供的註解。 @Autowired預設按照類型自動裝配,而@Resource預設按照名稱自動裝配。 @Autowired支持@Qualifier註解來指定裝配哪一個具有相同類型的bean,而@Resourc... ...