序言 開年的第一篇文章,今天分享的是SwiftUI,SwiftUI出來好幾年,之前一直沒學習,所以現在才開始;如果大家還留在 iOS 開發,這們語言也是一個趨勢; 目前待業中.... 不得不說已逝的2023年,大家開始都抱著一解封,經濟都會向上轉好,可是現實不是我們想象那樣;目前我也在學習 Swif ...
序言
開年的第一篇文章,今天分享的是SwiftUI,SwiftUI出來好幾年,之前一直沒學習,所以現在才開始;如果大家還留在 iOS
開發,這們語言也是一個趨勢; 目前待業中.... 不得不說已逝的2023年,大家開始都抱著一解封,經濟都會向上轉好,可是現實不是我們想象那樣;目前我也在學習 SwiftUI
,並且努力找工作中....;至於 2024
年經濟如何,咱們作為老百姓在大環境和全球經濟影響下;坦然面對,提升自己。 這裡不得不說國人堅韌不拔的精神。“捲” -- 努力吧Coding人
SwiftUI體驗
Xcode創建項目之後出現工程預設創建的UI界面;如下
一開始心裡對自己說:"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
(例如,Text
, Image
, VStack
等)。
協議里有一個associatedtype
,body
,其實這種協議就是當作約束形式使用;只要遵守這種協議編譯器每次閉包中返回的一定是一個確定,遵守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影響。
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**
的滑動效果;非常方便。
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大家看到這個單詞有個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()
}
效果圖:
可以看到上下邊距存在安全區域的,如果禁用安全區域,使用 ignoresSafeArea(.all)
可以去掉。
代碼如下:
最後說說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開發應用,這們語言在應用中占有讀也是成倍增長。