余晖落尽暮晚霞,黄昏迟暮远山寻
本站
当前位置:网站首页 > 编程知识 > 正文

干货-Swift父子组件通信(哪两个 pc 组件通过南桥芯片组与 cpu 通信)

xiyangw 2022-12-03 12:04 22 浏览 0 评论

置顶

菜鸟入门,各位大佬轻喷,如有谬误之处欢迎讨论建议,也欢迎各位道友与我同行

“不积跬步,无以至千里;不积小流,无以成江海”

继续

上文中已经实现将 TODO 项分组,已完成的 todo 和未完成的 todo 理应分开展示。

并且在 todo 项为空的时候进行提示。

并且根据这个分组,我们已经将设置页面做了出来,类似于iOS原生的设置界面。

但是上文的实现中有一个问题,即两个分组的代码重复了。

所以,本文我们将进行封装,既然要封装,那么必然会涉及到传参的问题。

本次操作不会对UI和交互发生改变,因此本次没有演示图片,部分的演示放到了部分的讲解中。

最简单的封装:函数

观察我们实现的 TodoView.swift 页面,我们可以发现,主要重复的地方在于两个 Section 中。

其实两个 Section 基本上是一致的,只是对数据的过滤方式不一样。

所以我们可以将 Section 封装为一个函数。

同时,我们也能够看到,TodoItem 里面的层级过多,会导致很多个缩进,缩进最里面的代码,已经跑到了很右边去了,这很显然看着很难受。

因此我们可以将 TodoItem 也抽象为一个函数,由 Section 函数进行调用。

这样代码量进一步减少,并且相较之前更加直观、美观。

最终实现代码如下 :

import SwiftUI

struct TodoView: View {
    // 省略一堆变量定义。。。
    // todo项分组
    func todoSectionView(isFinished:Bool = false) -> some View{
        return Section(isFinished ? "已完成":"未完成") {
            ForEach(todos.todoList.filter{(item) -> Bool in
                return item.isFinished == isFinished;
            }){ item in
                // 这里就直接调用下面封装的 todoitemview 方法了
                todoItemView(item: item)
                    .contentShape(Rectangle())
                    .onTapGesture {
                        // todos.toggle(item: item)
                        showId = item.id;
                        showDetail = true;
                    }
            // 这个调用将实现横滑删除功能
            }.onDelete{ IndexSet in
                todos.delete(offsets: IndexSet,isFinished: isFinished)
            }
        }
    }
    
    // todo项,将原来的 Todo 项的内容放到这儿来
    func todoItemView(item:TodoItem) -> some View{
        return HStack{
            VStack{
                HStack{
                    Text("\(item.name)")
                    Spacer()
                }
                HStack{
                    Text("\(item.createdAt)").font(.subheadline)
                    Spacer()
                }
            }.foregroundColor(item.isFinished ? .gray : .primary)
            Group{
                item.isFinished ?
                Image(systemName: "circle.fill") :
                Image(systemName: "circle")
            }.onTapGesture {
                todos.toggle(item: item)
            }
        }
    }
    
    var body: some View {
        VStack{
            // ...省略顶部的输入框部分
            // 如果有todo项的时候才显示todo列表,否则提示没有数据
            if(todos.todoList.count > 0){
                List{
                    todoSectionView(isFinished: false);
                    todoSectionView(isFinished: true );
                }.animation(.default,value:todos.todoList)
            }else{
                Text("请添加TODO项").foregroundColor(.gray)
                Spacer()
            }
        }.sheet(isPresented: $showDetail, content: {
            Text("String(showId)");
        })
    }
}
// ... 省略previewView 定义部分

组件封装:普通传参,父传子

首先,我们的 TodoView.swift 既是页面,同时也可以当做组件,它被 IndexView.swift 所调用。

然后,我们现在从 IndexView.swift 中传入一个title 到 TodoView.swfit 中,作为 section 的前缀名称使用

  • 第一步

IndexView.swift 中应该有一个传入的变量,给 TodoView.swfit

import SwiftUI

struct IndexView: View{
    // 。。。省略部分变量定义
    // 给一个变量,用于传值给子组件
    @State private var test:String = "test";
    var body: some View{
        // 。。。 省略
            TabView {
                // 向子组件传参
                TodoView(title:test)
                    .tabItem {
                        Image(systemName: "list.dash")
                        Text("TODO")
                    }.tag(0)
                    .environmentObject(todos)
                SettingView()
                    .tabItem {
                        Image(systemName: "gear.circle")
                        Text("设置")
                    }.tag(1)
            }
            .font(.headline)
        }
        // 。。。省略
    }
}
  • 第二步:在TodoView.swift 中应该有一个变量,接收 IndexView.swfit 传入的变量

struct TodoView: View {
    // 。。。省略无关变量定义部分
    // 接收父组件传入的 title,一定要是个 public,不然外面没法传
    @State public var title:String = "test";
    
    // todo项分组
    func todoSectionView(isFinished:Bool = false) -> some View{
        return Section(isFinished ? title:"未完成") {
            // 。。。省略
        }
    }
    // 。。。省略主体部分

最终效果如下:



组件传参:子传父

子传父时我们可以利用 @Binding 的特性,让子组件对变量的操作可以响应到父组件中

  • 第一步:父组件传入一个 @Binding

import SwiftUI

struct IndexView: View{
    // 。。。省略部分变量定义
    // 给一个变量,用于传值给子组件
    @State private var test:String = "test";
    var body: some View{
        // 。。。 省略
            TabView {
                // 向子组件传参
                TodoView(title:$test)
                    .tabItem {
                        Image(systemName: "list.dash")
                        Text(test) // 让这个变量显示出来
                    }
            }
            .font(.headline)
        }
        // 。。。省略
    }
}
  • 第二步:子组件中接收 @Binding 参数

struct TodoView: View {
    // 。。。省略参数定义
    // @Binding 也是一个 public,同时不能定义默认值,否则会报错
    @Binding public var title:String;
    // 。。。省略
    var body: some View {
        VStack{
            HStack{
                // 我们将 title 绑定到输入框中以便观察效果
                TextField("请输入新的TODO",text:$title).onSubmit {
                    todos.add(name: newItem)
                    newItem = ""
                }
                Button("添加"){
                    todos.add(name: newItem)
                    newItem = ""
                }
            }.padding()
        }
           // 。。。省略
    }

得到以下结果



可以看到,TextField的绑定值的变化,同时影响了section的标题和父组件中TabItem的标题

组件传参:@EnvironmentObject

以上我们已经有了父子传递,那么假设我们现在有这么一个需求:

点击 TodoItem 的时候需要弹出一个表单,用来展示 TodoItem的所有信息,并且组件内所有的数据修改都会影响到点击的哪一条 TodoItem

当然,我们可以只用 @Binding 传递,一个参数一个参数地处理,这很显然不是一个很好的处理方式。

最好的办法是让 TodoItem 的表单和外面可以共用一份数据,这样,List 就只需要传一个 id 到表单内部即可,由表单自己去处理。

此时,我们可以借助 @EnvironmentObject进行传递,顾名思义,这是一个环境对象,一旦有所引用,大家都是同一份数据模型。

  • 第一步:定义 @EnvironmentObject

import SwiftUI

struct IndexView: View{
    // 省略。。。
    let todos = TodoLists(todoList: [])
    var body: some View{
        // 省略。。。
        VStack{
            // 一个简单的tabview,底部导航栏
            TabView {
                TodoView()
                    .tabItem {
                        Image(systemName: "list.dash")
                        Text("TODO")
                    }.tag(0)
                    // 此处将环境对象带上去
                    .environmentObject(todos)
                // 省略。。。
            }
            .font(.headline)
        }
        // 省略。。。
    }
}
  • 第二步 :子组件中获取

import SwiftUI

struct TodoView: View {
    // 使用 @EnvironmentObject 获取即可
    @EnvironmentObject var todos:TodoLists;
    // 省略。。。
}

本次修改不会对项目的UI和交互等造成任何影响

接下来将在 TodoView 中在点击 TodoItem 时弹出一个表单,并在表单中使用这个环境对象,以及找出要编辑的对象,将数据回传。

这些内容下章再进行讨论。

总结

  1. 函数式的组件片段封装与 react 中的渲染逻辑比较类似,可以把某一段view分离出来。

  2. 既然是函数式的封装,那么参数的传递自然遵从函数的参数传递方法。

  3. 暂时没有考虑事件的传递,既然一切都可以是数据,那么完全可以把事件视作一个数据的变化,有数据的子父级影响和全局影响我觉得大部分的场景已经足够了。

  4. @EnvironmentObject 还有很多其他的用法,例如关闭 Sheet 等,前文中已有使用,此处不做赘述。

相关推荐

Hibernate 能够满足我们的验证需求(hibernate increment)
Hibernate 能够满足我们的验证需求(hibernate increment)

注释简化了数据验证有时会有一种工具,它可以真正满足开发人员和架构师的需求。开发人员在第一次下载这种工具当天就可以在自己的应用程序中开始使用这种工具。理论上来说,...

2023-03-21 19:49 xiyangw

2020Web前端开发面试题汇总整理(web前端开发面试技巧)
2020Web前端开发面试题汇总整理(web前端开发面试技巧)

2020Web前端面试题权限管理如何实现?前端控制:前端的控制比较简单,从后台获取到用户的权限之后,可以存在session或者cookie中,然后在页面加载的...

2023-03-21 19:49 xiyangw

老板知道会点赞,前端开发人员的10个安全建议
老板知道会点赞,前端开发人员的10个安全建议

Web安全是前端开发人员经常忽略的主题。当我们评估网站的质量时,我们通常会查看性能,SEO友好性和可访问性等指标,而网站抵御恶意攻击的能力却常常被忽略。即使敏感...

2023-03-21 19:49 xiyangw

关于Python的Selenium框架全解,一篇完整的说明书

目录selenium基础语法一、环境配置1、安装环境安装selenium第三方库pipinstallselenium下载浏览器驱动:Firefox浏览器驱动:geckodriverCh...

关于document.write(关于document)

document.write的用处document.write是JavaScript中对document.open所开启的文档流(documentstream操作的API方法,它能够直接在文档流中写...

听听别人怎么说:VueJS 与 ReactJS(请简单介绍vue和react之间的区别)
听听别人怎么说:VueJS 与 ReactJS(请简单介绍vue和react之间的区别)

使用所有新的库和框架,很难跟上所有这些库和框架,也就是说,这就需要您决定哪些是值得花时间的。让我们看看人们说什么:“和Vue.JS一起工作是很愉快的,我发现学习...

2023-03-21 19:48 xiyangw

学习笔记-身份认证攻击漏洞,文件上传漏洞(认证身份信息)

身份认证攻击漏洞身份认证机制:基于会话的验证使用基于会话的身份验证(或会话cookie身份验证或基于cookie的身份验证),用户的状态存储在服务器上。它不需要用户在每个请求中都提供用户名或密码。...

2020Web前端常见面试题及答案-每日三道题(web前端面试常问知识)
2020Web前端常见面试题及答案-每日三道题(web前端面试常问知识)

web前端面试题及答案问题:网页验证码是干嘛的,是为了解决什么安全问题?解析:(1)区分用户是计算机还是人的公共全自动程序。可以防止恶意破解密码、刷票、论坛灌水...

2023-03-21 19:48 xiyangw

Web 应用常见安全漏洞(web安全十大漏洞)

1、SQL注入SQL注入就是通过给web应用接口传入一些特殊字符,达到欺骗服务器执行恶意的SQL命令。SQL注入漏洞属于后端的范畴,但前端也可做体验上的优化。原因当使用外部不可信任的数据...

文件上传漏洞全面渗透姿势总结(文件上传漏洞常发生在哪些功能点)
文件上传漏洞全面渗透姿势总结(文件上传漏洞常发生在哪些功能点)

文件上传的场景真的随处可见,不加防范小心,容易造成漏洞,造成信息泄露,甚至更为严重的灾难。比如某博客网站评论编辑模块,右上角就有支持上传图片的功能,提交带有恶意...

2023-03-21 19:47 xiyangw

这一次 带你彻底了解Cookie(cookiev)
这一次 带你彻底了解Cookie(cookiev)

前言网络早期最大的问题之一是如何管理状态。简而言之,服务器无法知道两个请求是否来自同一个浏览器。当时最简单的方法是在请求时,在页面中插入一些参数,并在下一个请求...

2023-03-21 19:47 xiyangw

【反诈征文展播】(二)谨防网络诈骗(反网络诈骗文章)
【反诈征文展播】(二)谨防网络诈骗(反网络诈骗文章)

谨防网络诈骗甘州区青年东街小学四4班家长:王飞随着计算机技术、互联网的发展及信息技术的进步,网上银行、网络炒股、网络购物等网络商业活动的日渐兴起,在人们沉...

2023-03-21 19:46 xiyangw

四月前端面试题总结:前端面试总结(下篇)(2021前端的面试题及答案)
四月前端面试题总结:前端面试总结(下篇)(2021前端的面试题及答案)

接着昨天的前端面试题总结写的哈,有在找工作的伙伴,建议仔细阅读!10.什么是原型链原型:每个javascript创建的时候都会关联另一个对象,这个对象就是原型,...

2023-03-21 19:46 xiyangw

客户端JavaScript的限制(javascript客户端脚本编程)

处于安全的考虑,客户端的js运行会受到种种限制,下面就让我们来了解一下。浏览器的限制浏览器限制了JavaScript任务的运行时间。这种限制是有必要的,它确保某些恶意代码不能通过永不停止的密集操作锁住...

前端面试干货:四月份前端面试题总指南(下篇)
前端面试干货:四月份前端面试题总指南(下篇)

接着昨天的前端面试题总结写的哈,有在找工作的伙伴,建议仔细阅读!10.什么是原型链原型:每个javascript创建的时候都会关联另一个对象,这个对象就是原型,...

2023-03-21 19:46 xiyangw

取消回复欢迎 发表评论: