SwiftUI简介
SwiftUI是苹果推出的一个新的UI框架,它使用了声明的方式,通过视图,基础控件和布局控件来进行页面的开发。
SwiftUI具有跨平台性,一份SwiftUI代码可以同时跑在iOS、macOS、tvOS、watchOS平台上。
SwiftUI编写的页面代码更简洁,广泛使用链式调用。
SwiftUI视图和UIKit视图可以互相转换,对于将旧的项目过度到新布局方式比较友好。
SwiftUI的运行速度优于UIKit,他减少了界面的层次结构,因此可以减少绘制步骤,并且他完全绕过了CoreAnimation,直接进入Metal,可以有优秀的渲染性能。
 
其实声明式页面布局前端已经出现了很久,像React, Vue都是使用的声明式布局,声明式布局与命令式布局相比有很多优势,
如:单向数据流,双向数据绑定,只要数据状态改变使用了这些数据的视图就会自动更新等。
声明式布局是UI布局方式的未来,这次苹果从命令式编程过度到声明式编程算是一个大的进步。
 
设计模式
采用Struct组成的树形结构组织页面。叶子节点是基本控件。
这棵Struct树类似于React的抽象语法树,它会在编译阶段将这些描述信息翻译成真实的UIKit中的UI控件。
 
视图结构
APP根入口
APP的根入口是一个Struct结构体,它遵守APP协议
@main
struct WorldLandMarkApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
App协议
public protocol App {
associatedtype Body : Scene @SceneBuilder @MainActor var body: Self.Body { get } @MainActor init()
}
页面结构体
some表示返回的是一个遵守了View协议的不透明类型,也就是var body: some View {} 这个计算属性中,只能return一种类型,不能出现if a {Text()} else {List{}} 这样的2种类型。
struct LandmarkList: View {
var body: some View {
Text(/*@START_MENU_TOKEN@*/"Hello, World!"/*@END_MENU_TOKEN@*/)
}
}
View协议
associatedtype Body : View 表示协议中定义了一个新类型Body,这个Body遵守View协议。
Self.Body 表示协议中的Body类型。Self表示类型本身,self表示实例变量本身
public protocol View {
associatedtype Body : View
@ViewBuilder @MainActor var body: Self.Body { get }
}
每个页面swift文件中都有2个结构体,一个表示要开发的页面,另一个是使用Canvas进行展示出来的视图,其中struct ContentView_Previews: PreviewProvider可以根据Debug需要在外层嵌套导航条,展示Group组。
import SwiftUI

struct ContentView: View {
var body: some View {
Text("Hello, world!")
.padding()
}
} struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
它们不一定保持一致,如:
struct LandmarkList_Previews: PreviewProvider {
struct DeviceType: Identifiable {
var id = UUID()
var name: String
} static var previews: some View {
//使用ForEach展示多个设备
ForEach([DeviceType(name: "iPhone 12"),DeviceType(name: "iPhone 13")]){ deviceItem in
LandmarkList().previewDevice(PreviewDevice(rawValue: deviceItem.name))
.previewDisplayName(deviceItem.name)
} }
}
状态双向绑定
@State单页面状态绑定
通过@State修饰的变量是做了双向绑定的,如果这个变量数据发生了改变,所有使用这个变量的视图都会自动更新。但是@State的修饰范围是当前的一个视图,如果想一个状态修改,整个APP范围内使用这个变量的视图全部都更新,则需要使用全局环境变量的模式。
@State private var isOpen
struct LandmarkList: View { @State private var isOpen: Bool = false
//@ObservedObject: 全局环境变量绑定
@ObservedObject var userData: UserData = UserData()
}
@ObservableObject+@Published全局状态变量
要使用全局状态变量,则需要创建一个class,并遵守ObservableObject协议。 然后在这个类中定义一个@Published修饰的变量 @Published var userLandmarks, 当@Published修饰的变量更新时,那么使用了@Published修饰的变量的视图就会对应更新。
定义一个UserData,遵守ObservableObject协议
import SwiftUI
import Combine class UserData: ObservableObject {
@Published var userLandmarks:[Landmark] = landmarks }
使用时,也要在对应的视图中加上 @ObservedObject修饰符,然后更新这个变量self.userData.userLandmarks[self.userLandmarkIndex].isFeatured.toggle()
struct LandmarkDetail: View {
var landmark: Landmark
@ObservedObject var userData: UserData var userLandmarkIndex: Int {
userData.userLandmarks.firstIndex(where: {$0.id == landmark.id})!
} var body: some View {
Button(action: {
self.userData.userLandmarks[self.userLandmarkIndex]
.isFeatured.toggle()
}){
if landmark.isFeatured {
Image("icon_rcxinhua_selected")
.resizable().frame(width: 20, height: 20, alignment: .center)
} else {
Image("icon_rcxinhua_defaultselected")
.resizable().frame(width: 20, height: 20, alignment: .center)
}
}
}
}
单向数据流
用户操作导致@State变量发生了改变,
@State变量改变导致使用了@State变量的UI视图就会自动更新
继续等待用户操作触发@State变量发生变化
 

UIKit控件与SwiftUI中控件的转换
UIKit转SwiftUI
通过UIViewRepresentable协议将UIView包装成SwifUI的View来使用。
struct MapView: UIViewRepresentable {
let view: UIView = UIView()
func makeUIView(context: Context) -> some UIView {
return view
}
func updateUIView(_ uiView: UIViewType, context: Context) {
view.backgroundColor = .red
}
}
SwiftUI转UIKit
通过UIHostingController将SwiftUI包装成UIView
UIHostingController(rootView: ContentView())
Model模型定义
List,ForEach等要求被循环的每个元素都要有一个唯一的标识
这样数据变更时,可以迅速定位刷新对应的UI,提高性能
所以,元素要遵循Identifiable协议(实现id协议)
struct Landmark: Identifiable {
var id = UUID()
let name: String
}
Demo地址
https://github.com/zhfei/SwiftBasicKnowledge
 
 
参考文章
https://www.jianshu.com/p/0f7215591b08
https://zhuanlan.zhihu.com/p/436779033
 
 

SwiftUI的认识与使用的更多相关文章

  1. SwiftUI 官方教程(八)

    8. 动态生成预览 接下来,我们会在 LandmarkList_Previews 中添加代码以在不同的设备尺寸上渲染列表.默认情况下,预览会以当前的 scheme 中设备的大小进行渲染.我们可以通过调 ...

  2. SwiftUI 官方教程(七)

    7. 给子 View 传递数据 LandmarkDetail 现在依然使用硬编码的数据来显示地标.像 LandmarkRow 一样,LandmarkDetail 类型和它组合的其他 view 都需要一 ...

  3. SwiftUI 官方教程(六)

    6. 在列表和详情之间设置导航 虽然列表已经能显示了,但是我们还不能通过点击单个地标来查看地标详情页面.SwiftUI教程 把 list 嵌入一个 NavigationView 中,并把每个 row  ...

  4. SwiftUI 官方教程(五)

    SwiftUI官方教程(五) 5. 同时使用 UIKit 和 SwiftUI 至此,我们已准备好创建 map view 了,接下来使用 MapKit 中的 MKMapView 类来渲染地图. 在 Sw ...

  5. SwiftUI 官方教程(四)

    SwiftUI 官方教程(四) 4. 自定义 Image View 搞定名称和位置 view 后,我们来给地标添加图片. 这不需要添加很多代码,只需要创建一个自定义 view,然后给图片加上遮罩.边框 ...

  6. SwiftUI 官方教程(三)

    3. 用 Stacks 组合 View 在上一节创建标题 view 后,我们来添加 text view,它用来显示地标的详细信息,比如公园的名称和所在的州. 在创建 SwiftUI view 时,我们 ...

  7. SwiftUI 官方教程(二)

    SwiftUI 官方教程(二) 2. 自定义 Text View 为了自定义 view 的显示,我们可以自己更改代码,或者使用 inspector 来帮助我们编写代码. 在构建 Landmarks 的 ...

  8. SwiftUI 官方教程(一)

    完整中文教程及代码请查看 https://github.com/WillieWangWei/SwiftUI-Tutorials   创建和组合 View 此部分将指引你构建一个发现和分享您喜爱地方的 ...

  9. SwiftUI 官方教程

    SwiftUI 官方教程 完整中文教程及代码请查看 https://github.com/WillieWangWei/SwiftUI-Tutorials   SwiftUI 官方教程 SwiftUI ...

  10. SwiftUI学习(一)

    总览 如果你想要入门 SwiftUI 的使用,那 Apple 这次给出的官方教程绝对给力.这个教程提供了非常详尽的步骤和说明,网页的交互也是一流,是觉得值得看和动手学习的参考. 不过,SwiftUI ...

随机推荐

  1. MyBatis 配置详解

    目录 mybatis-config.xml 核心配置文件 1. environments元素 1.1 子元素environment 1.2 transactionManager 事务管理器(共两种) ...

  2. FreeSWITCH使用L16编码通信及raw数据提取

    环境:CentOS 7.6_x64 FreeSWITCH版本 :1.10.9 Python版本:3.9.12 一.背景描述 PCM(Pulse Code Modulation,脉冲编码调制)音频数据是 ...

  3. 文心一言 VS chatgpt (11)-- 算法导论2.3 7题 3.1 4~5题

    四.2 ^ (n+1)=O(2 ^ n)成立吗? 2^(2n)=O(2 ^ n)成立吗? 文心一言: chatgpt: 第一个式子不成立,第二个式子成立. 2 ^ (n+1) = O(2 ^ n) 该 ...

  4. 2022-04-23:给定一个长度为4的整数数组 cards 。你有 4 张卡片,每张卡片上都包含一个范围在 [1,9] 的数字。您应该使用运算符 [‘+‘, ‘-‘, ‘*‘, ‘/‘] 和括号 ‘

    2022-04-23:给定一个长度为4的整数数组 cards .你有 4 张卡片,每张卡片上都包含一个范围在 [1,9] 的数字.您应该使用运算符 ['+', '-', '*', '/'] 和括号 ' ...

  5. 2022-04-07:给定一个只由‘a‘和‘b‘组成的字符串str, str中“ab“和“ba“子串都可以消除, 消除之后剩下字符会重新靠在一起,继续出现可以消除的子串... 你的任务是决定一种消除的

    2022-04-07:给定一个只由'a'和'b'组成的字符串str, str中"ab"和"ba"子串都可以消除, 消除之后剩下字符会重新靠在一起,继续出现可以消 ...

  6. 2021-07-30:两个有序数组间相加和的Topk问题。给定两个有序数组arr1和arr2,再给定一个整数k,返回来自arr1和arr2的两个数相加和最大的前k个,两个数必须分别来自两个数组。按照降

    2021-07-30:两个有序数组间相加和的Topk问题.给定两个有序数组arr1和arr2,再给定一个整数k,返回来自arr1和arr2的两个数相加和最大的前k个,两个数必须分别来自两个数组.按照降 ...

  7. Grafana系列-统一展示-9-Jaeger数据源

    系列文章 Grafana 系列文章 配置 Jaeger data source Grafana内置了对Jaeger的支持,它提供了开源的端到端分布式跟踪.本文解释了针对Jaeger数据源的配置和查询. ...

  8. AI DevOps | ChatGPT 与研发效能、效率提升(中)

    为啥 ChatGPT 突然火了? 简单概括就是:产品太过惊艳,体验超预期 之前人工智能发展多年,报道最多的也许就是曾经的李世石大战AlphaGo,现实中的特斯拉自动驾驶,还有波士顿动能放出的机器狗.对 ...

  9. SpringIOC个人笔记

    上一章讲解了SpringBoot中的 AutoConfiguration自动装配,而这一章就来讲讲自动装配时会用到的Spring三大特性之一的IOC控制反转. ​ 使用过Spring的人都熟知,Spr ...

  10. react中useRef的应用

    何为useRef useRef是随着react函数式组件发展而来的,是react众多官方hook中的一个,调用useRef可以返回一个伴随这组件整个声明周期不发生改变的对象,这个对象常见的用途有两个: ...