SwiftUI入門教程
SwiftUI寫一個簡單的頁面
上面github鏈接教程寫的很詳細(xì)辐啄,但有的api可能有點(diǎn)出入,最好還是clone項(xiàng)目,自己跑起來研究下。
廢話有點(diǎn)多,介意的直接看 正題三
寫在前面
這段時間公司業(yè)務(wù)不是很忙授嘀,我們一直處于新技術(shù)探究階段。前段時間學(xué)過flutter,總體來說還是很不錯姿染,不管是集成三方包,還是vscode新api學(xué)習(xí)成本,又或狀態(tài)管理和路由管理的三方框架(如GetX) 悬赏,使用起來都特別順手方便狡汉。之前因?yàn)闃I(yè)務(wù)需求,一直用的都是熟悉的oc闽颇,對SwiftUI也只是聽聽盾戴,沒有怎么入手研究過”啵可能對沒有接觸過的人尖啡,對SwiftUI來說聽的最多的詞 應(yīng)該就是聲明式語言,UI庫剩膘,一統(tǒng)蘋果所有終端設(shè)備的新框架庫等等衅斩。通過這兩天的學(xué)習(xí)接觸 SwiftUI確實(shí)是很棒。不啰嗦啦怠褐,進(jìn)入正題~
@[TOC]
一畏梆、簡潔高效的@State
二、好用的全局環(huán)境變量@EnvironmentObject
1奈懒、優(yōu)點(diǎn)
2奠涌、代碼使用
三、SwiftUI調(diào)用舊vc
1磷杏、創(chuàng)建wrapper并實(shí)現(xiàn)UIViewControllerRepresentable協(xié)議
2溜畅、SwiftUI中調(diào)用TestViewController
四、ViewController里調(diào)用SwiftUI頁面
一茴丰、簡潔高效的@State
在flutter中若改變某個值需更新界面的話达皿,需要setState,顯得很麻煩贿肩。但SwiftUI里面只要聲明一個@State變量就好峦椰,真的是太好用。例如下面代碼就是動態(tài)顯示slider滑動的值汰规,binding也很簡潔汤功,直接通過$加相應(yīng)的變量即可。這種binding在TextField真的是特別好用溜哮。oc那種delegate回調(diào)方式根本沒法比滔金。
import SwiftUI
import Combine
struct SliderPage : View {
@State var rating = 0.5
var body: some View {
VStack {
Text("Slider Value: \(self.rating)")
Slider(value: $rating)
.padding(30)
}.navigationBarTitle(Text("Slider"))
}
}
二、好用的全局環(huán)境變量@EnvironmentObject
1茂嗓、優(yōu)點(diǎn):整個app能全局共享該數(shù)據(jù)餐茵,且一旦數(shù)據(jù)更新,用到該數(shù)據(jù)的地方都會相應(yīng)的更新述吸。
2忿族、代碼使用
全局order變量
import SwiftUI
@main
struct iDineApp: App {
@StateObject var order = Order()
var body: some Scene {
WindowGroup {
MainView()
.environmentObject(order)
}
}
}
存儲信息的Order 模型
import SwiftUI
class Order: ObservableObject {
@Published var items = [MenuItem]()
var total: Int {
if items.count > 0 {
return items.reduce(0) { $0 + $1.price }
} else {
return 0
}
}
func add(item: MenuItem) {
items.append(item)
}
func remove(item: MenuItem) {
if let index = items.firstIndex(of: item) {
items.remove(at: index)
}
}
}
使用全局Order
import SwiftUI
struct OrderView: View {
@EnvironmentObject var order: Order
var body: some View {
NavigationView {
List {
Section {
ForEach(order.items) { item in
HStack {
Text(item.name)
Spacer()
Text("$\(item.price)")
}
}
.onDelete(perform: deleteItems)
}
Section {
NavigationLink(destination: CheckoutView()) {
Text("Place Order")
}
}
.disabled(order.items.isEmpty)
}
.navigationTitle("Order")
.listStyle(InsetGroupedListStyle())
.toolbar {
EditButton()
}
}
}
func deleteItems(at offsets: IndexSet) {
order.items.remove(atOffsets: offsets)
}
}
以上代碼來自github上iDine項(xiàng)目。
三、SwiftUI調(diào)用舊vc
SwiftUI如何調(diào)用項(xiàng)目中原來就有的ViewController?
思路:創(chuàng)建一個wrapper道批,實(shí)現(xiàn)UIViewControllerRepresentable協(xié)議错英,做相應(yīng)的映射。例如隆豹,現(xiàn)在有個TestViewController椭岩,我們?nèi)绾卧赟wiftUI點(diǎn)擊某個link跳轉(zhuǎn)到該頁面。
1璃赡、創(chuàng)建wrapper并實(shí)現(xiàn)UIViewControllerRepresentable協(xié)議
import UIKit
import SwiftUI
// 這里只能用struct判哥,不能用class
struct BFOViewControllerWrapper<T : UIViewController>: UIViewControllerRepresentable {
typealias UIViewControllerType = T
func makeUIViewController(context: UIViewControllerRepresentableContext<BFOViewControllerWrapper>) -> BFOViewControllerWrapper.UIViewControllerType {
return BFOViewControllerWrapper.UIViewControllerType()
}
func updateUIViewController(_ uiViewController: BFOViewControllerWrapper.UIViewControllerType, context: UIViewControllerRepresentableContext<BFOViewControllerWrapper>) {
//
}
}
2、SwiftUI中調(diào)用TestViewController
NavigationLink(
destination: BFOViewControllerWrapper<TestViewController>()) {
Text("跳轉(zhuǎn)到TestViewController頁面")
}
細(xì)心的網(wǎng)友鉴吹,肯定注意到了上面的一個亮點(diǎn)姨伟,對 就是泛型的使用。這樣使用雖然調(diào)用的時候感覺寫的字母多了點(diǎn)豆励,復(fù)雜了點(diǎn)夺荒,但BFOViewControllerWrapper卻可以統(tǒng)一重復(fù)的使用,不但功能清晰良蒸,后續(xù)維護(hù)也很高效技扼。
四、ViewController里調(diào)用SwiftUI頁面
將SwiftUI頁面放到一個UIHostingController里面嫩痰,之后再正常跳轉(zhuǎn)剿吻。如下某個vc點(diǎn)擊按鈕跳轉(zhuǎn)到一個SwfitUI頁面
import UIKit
import SwiftUI
class TestViewController: UIViewController {
lazy var lb: UILabel = {
let lb = UILabel(frame: CGRect(x: 50, y: 100, width: 200, height: 50));
lb.text = "我是UIViewController的文案"
lb.textColor = .red
lb.font = UIFont.systemFont(ofSize: 14)
return lb
}()
lazy var btn: UIButton = {
let btn = UIButton(frame: CGRect(x: 50, y: 200, width: 200, height: 50))
btn.setTitle("點(diǎn)我展示SwiftUI頁面", for: .normal)
btn.setTitleColor(.blue, for: .normal)
btn.setTitleShadowColor(.red, for: .normal)
btn.backgroundColor = .green
btn.titleLabel?.font = UIFont.systemFont(ofSize: 14)
btn.layer.cornerRadius = 10
btn.layer.masksToBounds = true
btn.addTarget(self, action: #selector(clickBtn(button:)), for: .touchUpInside)
return btn
}()
// 點(diǎn)擊按鈕跳轉(zhuǎn)到SwiftUI頁面
@objc func clickBtn(button: UIButton){
print("you click btn")
if #available(iOS 13, *) {
let testSwiftUIView = UIHostingController(rootView: TextPage())
navigationController?.pushViewController(testSwiftUIView, animated: true)
}
}
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(lb);
view.addSubview(btn)
}
}
附:Swift高階函數(shù)map,filter串纺,reduce簡單使用
map用于映射丽旅,可將一個列表轉(zhuǎn)化成另一個列表,例如下面將數(shù)字列表轉(zhuǎn)成字符串列表
//$0代表數(shù)組的元素
let array = [1, 2, 3, 4, 5 , 6, 7]
let result = array.map {
String($0)
}
filter 用于過濾纺棺,可以對數(shù)組中的元素按照某種規(guī)則進(jìn)行過濾榄笙,例如下面輸出大于5的元素
let array = [1, 2, 3, 4, 5 , 6, 7]
let result = array.filter {
$0 > 5
}
print("result: \(result)");
reduce用于計算或合并
//計算數(shù)組array元素的和
//在這里$0和$1的意義不同,$0代表元素計算后的結(jié)果祷蝌,$1代表元素
//10代表初始化值茅撞,在這里可以理解為 $0初始值 = 10
let result3 = array.reduce(10){
$0 + $1
}
合并
let result4 = array.reduce(""){$0 + "\($1)"}// 轉(zhuǎn)換為字符串并拼接
print("result4:",result4);
end