swift x输入流_SwiftUI 探索 - 状态和数据流
SwiftUI是iOS13新出的声明式UI框架,将会完全改变以前命令式操作UI的开发方式。此文章主要介绍SwiftUI中状态管理的方式。
可变状态
@State
与React和Flutter中的State类似,只不过React和Flutter中需要显式调用setState方法。在SwiftUI 中直接修改State属性值,就触发视图更新。
因为State是使用了@propertyDelegate修饰的属性值,其内部实现应该是在状态值set方法中进行变更视图的操作。
class Model: BindableObject {
var didChange = PassthroughSubject()
var count: Int = 0 {
didSet {
didChange.send(self)// 调用didChange触发变更操作
}
}
}
struct ContentView: View {
@State private var text: String = "a"// 使用@State修饰
@State private var model = Model()// 使用@State修饰
var body: some View {
VStack {
Text(text)
Text(model.count)
Button(action: {
self.text = "b"// 修改text会更新视图
self.count += 1
}) {
Text("update-text")
}
}
}
}
如果想使用class类型属性作为State属性,类对象需要实现BindableObject协议。当调用didChange的send方法时,会通知关联的View更新视图。didChange是Publisher(新出的Combine异步事件处理框架,类似RxSwift)类型,调用send时会发送一个新的值给订阅者。
当修改的State属性值没有在body中使用或者修改后的State属性值和上一次相同,并不会触发重新计算body。
State属性修改时,会检测State属性被使用和检测值变更来决定要不要更新视图和触发body方法。
State属性用class类型。在触发body重新计算前会检查State值有没有改变,当修改类对象属性时,因为类对象指针并没有改变,所以并不会触发视图更新。如果想触发视图变更,可以在修改State时生成新的对象(这种方式不太好)或者使用BindableObject。
属性
Property
与React中的Props类似,用于父视图向子视图传递值。
struct PropertyView: View {
let text: String// 当text改变时,会重新计算`body`。
var body: some View {
Text(text)
}
}
struct ContentView: View {
var body: some View {
PropertyView(text: "a")
}
}
使用let变量。使用var变量修饰属性,在body方法里也不能修改,因为修改属性会创建新的结构体。
@Binding
与Property功能类似,用于父视图向子视图传递值。只不过Binding属性可以修改,修改Binding属性会触发父视图State改变重新计算body。可以实现反向数据流的功能,有点类似MVVM的双向绑定。
struct BindingView : View {
@Binding var text: String // 使用@Binding修饰
var body: some View {
VStack {
Button(action: {
self.text = "b"
}) {
Text("update-text")
}
}
}
}
struct ContentView : View {
@State private var text: String = "a" // State
var body: some View {
VStack {
BindingView(text: $text)// State变量使用$获取Binding
Text(text)
}
}
}
@ObjectBinding
@ObjectBinding似乎和State相似,暂时不太清楚使用上有什么区别。@State替换@ObjectBinding使用没有问题,@Binding替换@ObjectBinding使用也没有问题。
class Model: BindableObject {
var didChange = PassthroughSubject()
var count: Int = 0 {
didSet {
didChange.send(self)
}
}
}
struct ChildView: View {
// @Binding var model: Model
// @ObjectBinding var model: Model
var body: some View {
VStack {
Text("count2-\(model.count)")
Button(action: {
self.model.count += 1
}) {
Text("update")
}
}
}
}
struct ContentView : View {
// @State private var model = Model()
// @ObjectBinding private var model = Model()
var body: some View {
VStack {
ChildView(model: model)
Text("count1-\(model.count)")
}
}
}
上面State,ObjectBinding,Binding注释的地方任意使用结果都一样,视图能正确更新。
@EnvironmentObject
通过Property或者Binding的方式,我们只能显式的通过组件树逐层传递。
显式逐层传递的缺点
当组件树复杂的时候特别繁琐,修改起来也很麻烦。
有些属性在视图树中间的层级不会使用到,只有底层会使用。会增加中间层级视图的复杂度。也可以避免中间的层级重复计算body触发视图更新。
为了避免层层传递属性,可以使用Environment变量。Environment属性可以在任意子视图获取并使用。和React中的Context很相似。
struct EnvironmentView1: View {
var body: some View {
return VStack {
EnvironmentView2()
EnvironmentView3()
}
}
}
struct EnvironmentView2: View {
@EnvironmentObject var model: Model// 使用@EnvironmentObject修饰属性
var body: some View {
Button(action: {
self.model.change()
}) {
Text("update-Environment")
}
}
}
struct EnvironmentView3: View {
@EnvironmentObject var model: Model// EnvironmentObject
var body: some View {
Text(model.text)
}
}
struct ContentView: View {
var body: some View {
//EnvironmentObject需要使用environmentObject方法注入到组件树中
EnvironmentView1().environmentObject(Model())
}
}
通过environmentObject方法注入对象到组件树中,子组件树中共享同一个对象并且可以监听变更。
@EnvironmentObject查找如何能获取到对应的对象,大概是根据属性的类型进行查找,所以多个属性只要类型相同,就能取到同样的对象。当组件树有多个组件使用environmentObject方法注入同类型的对象时,获取时会查找最近的父组件的对象。
目前好像没有方式实现根据不同的key来注入多个对象并获取。
数据流
父视图 -> 子视图向下传递
不需要修改使用Property
需要修改使用@Binding
父视图 -> 子视图跨层级向下传递
Environment
全局状态层管理
应该是结合Combine框架根据模块功能领域分层进行管理。
视图更新流程
修改State触发视图更新,检测State是否被使用以及值是否被改变。
重新计算body生成新的视图树,会重新创建所有子视图的View结构体。
遍历所有子视图,判断View结构体与更新前是否一致。当不一致时,触发子视图更新,调用子视图body。
Tips
关于 State
class Model: BindableObject {
var didChange = PassthroughSubject()
var count: Int = 0 {
didSet {
didChange.send(self)
}
}
init() {
print("Model-init-\(count)")// 这里count始终为0
}
}
struct Struct {
private(set) var count = 0
init() {
print("Struct-\(count)")// 这里count始终为0
}
mutating func update() {
print("update-\(count)")
count += 1
}
}
struct ChildView: View {
@State private var model2 = Struct()
@State private var model = Model2()
@State private var count = 0
var body: some View {
return VStack {
Text("\(model.count)")
Text("\(model2.count)")
Text("\(count)")
Button(action: {// 修改 State
self.model.count += 1
self.count += 1
self.model2.update()
}) {
Text("update")
}
}
}
}
struct ContentView: View {
@State private var count = 0
var body: some View {
return VStack {
ChildView()
Button(action: {
self.count += 1
}) {
Text("update")
}
Text("\(count)")
}
}
}
当ContentView更新时,会重新创建ChildView结构体。
ChildView中的State都会重新创建,Struct和Model初始化方法中,count一直为0,即使ContentView里State曾经修改过。但是下一次修改State值时,State会使用之前的值做运算。
不太清楚这里是如何处理的,State虽然重新初始化了一次,似乎还是使用的之前的State。
例如当点击Button时,会修改ChildView中model, model2中count+=1,当前count=1。
当ChildView重新创建时,model,model2初始化方法中,count=0。
当下一次点击Button修改count值时,count会在1的基础上+1,之后count=2。
性能
当视图发生变更时,由于body会经常重新计算,所以应该尽量避免在body中进行重复和耗时计算。
视图变更时,视图组件View结构体会重新创建,所以应该避免在init方法中进行重复和耗时计算。(包括属性的重新生成)
根据上面 State的特性,当State属性为结构体或类时,应避免在init方法中访问或修改属性。因为当State修改过后,在init方法中获取到的值不是正确的,修改值也会生效。
相关文章:

qt 控件 背景色 透明 除去边框
在调试ui的时候,需要将背景色变为透明,与母控件的颜色一致,并且除去边框。 参考链接: http://www.qtcentre.org/threads/12148-how-QTextEdit-transparent-to-his-parent-window 除去背景色,使透明。ui->textBrowse…

A Strange Bitcoin Transaction
链客,专为开发者而生,有问必答! 此文章来自区块链技术社区,未经允许拒绝转载。 在之前的谈谈比特币的地址安全问题这篇文章中,我们谈到一个名为”LBC”的项目,这个项目通过暴力碰撞企图打捞到一些什么东西…

Jsoncpp 使用方法解析
Jsoncpp是目前比较好用的开源Json解析库,现在总结一下它的使用方法,理解,以供以后查阅。 在引入Jsoncpp的时候我们看到Jsoncpp里边的常用的cpp文件,有json_reader.cpp,json_value.cpp,json_writer.cpp, 其中: json_val…

tomcat限速_WEB服务的下载限速(二)(限速模块安装与配置)
一、准备工作1、下载mod_bw-0.92.tgz2、安装httpd-develyum install httpd-devel二、安装限速模块tar -xvf mod_bw-0.92.tgzapxs -c -i -a mod_bw.c三、配置apachevim /usr/local/apache/conf/httpd.conf查看是否已加载 LoadModule bw_module modules/mod_bw.so如果没…

EntityFrameworkCore 安装
映射现有(多个)数据库: 安装Microsoft.EntityFrameworkCore最新版本Tools -> NuGet Package Manager -> Package Manager Console 分别输入: Scaffold-DbContext "Server.;DatabaseSxh;Trusted_ConnectionTrue;" …

python神秘的魔法函数_Python魔法函数
1.什么是魔法函数魔法函数即Python类中以__(双下划线)开头,以__(双下划线)结尾的函数,Python提供的函数,可让咱们随意定义类的特性示例:class Company(object):def __init__(self, employee_list):self.employee employee_listde…

HDU-4738-Caocao's Bridges(tarjan)
转载于:https://www.cnblogs.com/GrowingJlx/p/6642692.html

博客园美化技巧汇总
首先得有js权限 1.1 页脚js代码 <script type"text/javascript"> /*功能:生成博客目录的JS工具测试:IE8,火狐,google测试通过zhang_derek2018-01-03 */ var BlogDirectory {/*获取元素位置,距浏览器左…

数据事务四种隔离机制和七种传播行为
数据事务四种隔离机制和七种传播行为 一、隔离级别: 数据库事务的隔离级别有4个,由低到高依次为Read uncommitted、Read committed、Repeatable read、Serializable,这四个级别可以逐个解决脏读、不可重复读、幻读这几类问题。 1.…

vue注册新节点_vue怎么重新组装slots节点
在后台列表中通常会有比较多的操作按钮 过多的按钮影响布局 也影响操作 因此想通过vue的组件来控制显示的按钮个数 多余的按钮自动被收进一个特殊的 更多 按钮里面,效果图:组件定义:Vue.component(button-groups, {render(createElement) {re…

什么是EOS?
链客,专为开发者而生,有问必答! 此文章来自区块链技术社区,未经允许拒绝转载。 关于EOS有很多炒作。2017年5月,EOS的创始人丹•拉莫(Dan Larimer)在纽约宣布这一消息时,一幅巨大的巨型屏幕广告在时代广场上…

【GDKOI2016Day1T1-魔卡少女】【拆位】线段树维护区间内所有连续子区间的异或和...
题意:给出N个数,M个操作。操作有修改和询问两种,每次修改将一个数改成另一个数,每次询问一个区间的所有连续子区间的异或和。n,m<100000,ai<1000 题解: 当年(其实也就是今年)做不出来的题…

用composer安装laravel-bjyblog
前面讲了两行命令composer的安装,现在我们来操作一下composer安装基于laravel的博客laravel-bjyblog。测试环境是linux,bt面板,php7.2安装扩展fileinfo/opcache/redis/imagemagick/imap/exif,禁用 proc_open 函数 下面开始安装&am…

微信小程序多项选择器_微信小程序三级联动之多列选择器
有些时候,三级联动业务场景并不只是全国地区选择,可能还涉及到自定义分类的三级联动,这时就需要使用微信的多列选择器。如果只是一列字段,或者每次拖动一次都去服务端取,会比较容易。 如果想一次定义好json,关联数据相…

eosjs-ecc中文文档
链客,专为开发者而生,有问必答! 此文章来自区块链技术社区,未经允许拒绝转载。 eosjs-ecc是eos官方处理密钥和签名的javascript开发包。访问地址:eosjs-ecc中文手册。 eosjs-ecc安装 nodejs环境下,使用N…

rocketmq 组监听_最全的RocketMQ学习指南,程序员必备的中间件技能
一、简介RocketMq是阿里开发出来的一个消息中间件,后捐献给Apache。官网上是这样介绍的: Apache RocketMQ™ is a unified messaging engine, lightweight data processing platform.RocketMQ是一个统一的处理消息引擎,轻量级的数据处理平台。…

【刷题】BZOJ 4516 [Sdoi2016]生成魔咒
Description 魔咒串由许多魔咒字符组成,魔咒字符可以用数字表示。例如可以将魔咒字符 1、2 拼凑起来形成一个魔咒串 [1,2]。 一个魔咒串 S 的非空字串被称为魔咒串 S 的生成魔咒。 例如 S[1,2,1] 时,它的生成魔咒有 [1]、[2]、[1,2]、[2,1]、[1,2,1] 五种…

jQuery的文档操作方法
jQuery 文档操作方法 这些方法对于 XML 文档和 HTML 文档均是适用的,除了:html()。 方法描述addClass()向匹配的元素添加指定的类名。after()在匹配的元素之后插入内容。append()向匹配元素集合中的每个元素结尾插入由参数指定的内容。appendTo()向目标结…

原 EOS智能合约开发入门
链客,专为开发者而生,有问必答! 此文章来自区块链技术社区,未经允许拒绝转载。 EOS智能合约的开发需要使用llvm和abigen来生成abi文件。 为此eos提供了一个 名为eosiocpp的工具。 在这篇文章中,我们介绍如何使用这个工…

python安装虚拟环境virtualenv
虚拟环境 虚拟环境是一个将不同项目所需求的依赖分别放在独立的地方的一个工具,它给这些工程创建虚拟的Python环境。它解决了“项目X依赖于版本1.x,而项目Y需要项目4.x”的两难问题,而且使你的全局site-packages目录保持干净和可管理。 比如&…

可变分区存储管理实验报告总结_操作系统实验报告-可变分区存储管理方式的内存分配回收...
一.实验目的(1)深入了解可变分区存储管理方式的内存分配回收的实现。二.实验内容编写程序完成可变分区存储管理方式的内存分配回收,要求有内存空间分配表,并采用最优适应算法完成内存的分配与回收。三.实验原理在可变分…

ubuntu/linuxmint如何添加和删除PPA源
【添加】 1、sudo add-apt-repository ppa:user/ppa-name 2、sudo apt-get update (然后再安装软件sudo apt-get install <package-name>或更新软件sudo apt-get upgrade) 【删除】 1、cd /etc/apt/source.list.d/ 2、sudo rm <ppa-name> 转载于:https://www.cnblo…

了解EOS看这一篇就够了一、团队二、技术三、项目进度四、争议和风险五、展望
链客,专为开发者而生,有问必答! 此文章来自区块链技术社区,未经允许拒绝转载。 无论是混迹于币圈、链圈还是矿圈,对BTC(比特币)、ETH(以太坊)、EOS这三大主流币一定不会…

qt 多个模型如何显示在表格中_Qt MOOC系列教程 第五章第四节:QML中的C++模型
我们已经多次讨论过如何创建自己的模型来表示QML中的数据,并且在上一节中我们看到了QStandardItemModel的基本示例。通常,出于性能和功能方面的原因,需要从一开始就要实现自己的模型。QAbstactItemModel类为项目模型类提供了抽象接口…

ubuntu终端基础命令
1. 启动终端的快捷键: ctr alt t2. 终端字体放大: ctrshift3. 终端字体放大: ctr-4. ls : 查看当前目录的文件信息 4.1 ls 路径: 查看指定目录的信息 4.1. pwd: 查看目录所在的路径5. touch: 创建文件 5.1 touch 1.txt 2.txt 创建多个文件6. mkdir: 创建…

js中操作数组的一些方法
增 push 在数组的末尾添加一个或多个元素,并返回新的长度。 array.push(1,2,3.........) unshift 在数组的开头添加一个或多个元素,并返回新的长度。 array.unshift(1,2,3......) splice 在制定位置添加一个活多个元素,splice(s…

a标签怎么传参_jsp页面中怎么利用a标签的href进行传递参数以及需要注意的地方...
jsp页面中:这是正确写法。需要注意的地方:1、传递的参数是数字2、传递的参数是字符串注意多了个单引号后台直接用request.getParameter("productIdStr"); 接收就可以了。此处也有要注意的地方:接收后要进行判空,否则会报…

转】windows下使用批处理脚本实现多个版本的JDK切换
原博文出自于: http://www.cnblogs.com/xdp-gacl/p/5209386.html 感谢! 一.JDK版本切换批处理脚本 我们平时在window上做开发的时候,可能需要同时开发两个甚至多个项目,有时不同的项目对JDK的版本要求有区别,这时候…

mysql引擎介绍
1.myisam存储引擎:不支持事务,也不支持外键,优势是访问速度快,对事务完整性没有要求或者以select,insert为主的应用基本上可以用这个引擎来创建表。 2.innodb存储引擎:innodb引擎提供了具有提交,…

为什么2100万个BTC发行总量少了0.0231?
链客,专为开发者而生,有问必答! 此文章来自区块链技术社区,未经允许拒绝转载。 许多人只是听说比特币的总数为2100万个,但不知道这个数字的准确来源。实际上,2100万个只是一个近似数,精确的数…