SwiftUI 中为什么应该经常用子视图替换父视图中的大段内容?

news2024/11/17 23:32:20

在这里插入图片描述

概览

在 SwiftUI 官方教程中,Apple 时常提出“化整为零”的界面布局思想。简单来说,Apple 推荐 SwiftUI 视图的构建方式是:用若干自定义小视图来构成上层的功能视图。

这是为什么呢?

在本篇博文中,我们将用一个通俗易懂的示例来讨论这样做的重要原因。

无需等待,Let’s Go!


1. 为何要“化整为零”?

1.1 遵循 DRY 原则

首先,从软件工程的角度来说,用自定义视图来代替原始布局代码有利于封装和代码重用。

比如 SwiftUI 中需要在各个功能视图顶部弹出 HUD 小视图:

在这里插入图片描述

如果在每个功能视图中都写一遍 HUD 视图代码,之后一旦需要修改 HUD 实现,在所有父视图中都需做出改动,这种“牵一发而动全身”的行为显然不符合日后的代码维护。

所以,这时把 HUD 的实现放在定制视图中无疑是一个明智的选择:

struct HUD<Content: View>: View {
  @ViewBuilder let content: Content

  var body: some View {
    content
      .padding(.horizontal, 12)
      .padding(16)
      .background(
        Capsule()
          .foregroundColor(Color.white)
          .shadow(color: Color(.black).opacity(0.16), radius: 12, x: 0, y: 5)
      )
  }
}

现在,在所有功能视图中我们都可以嵌入统一的 HUD 子视图,避免了代码重复:

在这里插入图片描述

1.2 自定义视图有利于 SwiftUI 优化界面刷新

SwiftUI 非常聪明,当视图对应的状态发生变化时,它会及时的刷新状态对应的内容。

不过在有些情况下, SwiftUI 仍然需要我们来为它提供“更优化”的刷新建议。

具体来说:如果一个状态对应的视图界面被刷新,其中所有的视图都会被刷新,尽管其中某些视图实际上无需刷新(绕口令?)。

这时,若我们用自定义子视图分割显示逻辑代码,且被更改的状态没有影响到该子视图,则 SwiftUI 就不会刷新这些子视图,从而有效的提高了视图渲染性能。

有些童鞋可能担心 SwiftUI 中将父视图划分为大量自定义子视图会带来显示性能上的灾难。

其实这种担心是多虑了。

首先,SwiftUI 中视图的实现非常轻量级,而且 Apple 对 SwiftUI 中整个视图继承体系的渲染在底层做了很好的优化,嵌入大量子视图一般不会显著影响显示性能。其次,如果确实有性能问题,我们随时可以通过性能检测工具来确定性能瓶颈点,再选择优化也不迟。

过早优化是“万恶之源”,切记切记!😉

2. 一个“栗子”

也许大家对上面 “自定义视图有利于 SwiftUI 优化界面刷新” 这一概念仍不是太理解。

没关系,下面我们就用一个简单的例子让小伙伴们直观感受界面刷新优化前后的差别。

extension ShapeStyle where Self == Color {
    static var random: Color {
        Color(
            red: .random(in: 0...1),
            green: .random(in: 0...1),
            blue: .random(in: 0...1)
        )
    }
}

struct HyButtonStyle: ButtonStyle {
    func makeBody(configuration: Configuration) -> some View {
        configuration.label
            .font(.title)
            .padding()
            .foregroundColor(.white)
            .background(Color.gray)
            .clipShape(RoundedRectangle(cornerRadius: 10))
    }
}

struct ContentView: View {
    @State var s_one = 0
    @State var s_two = 0
    
    var body: some View {
        NavigationStack {
            VStack(spacing: 50.0) {
                Text("one: \(s_one)")
                
                HStack {
                    Text("two: \(s_two)")
                }
                .padding()
                .background(Color.random)
                                
                HStack(spacing: 50) {
                    Button("add one"){
                        s_one += 1
                    }.buttonStyle(HyButtonStyle())
                    
                    Button("add two"){
                        s_two += 1
                    }.buttonStyle(HyButtonStyle())
                }
            }
            .font(.title2)
            .padding()
            .background(Color.random)
            .navigationTitle("视图“冗余”刷新演示")
        }
    }
}

顺面说一下,上述代码利用了

SwiftUI 如何快速识别视图(View)界面的刷新是由哪个状态的改变导致的? 博文中的调试技术。

简单来说,当视图被刷新时其背景色也会发生变化,我们可以用肉眼轻易辨别出每个视图是否被刷新了。


上面是一段非常简单的代码,按道理来说,状态 s_one 只会影响父视图中的 Text("one: ") 文本,而 s_two 只会影响 Text("two: ") 文本。但实际上它们会互相影响:

在这里插入图片描述

如上图所示:改变 s_one 会刷新 Text("two: "),而改变 s_two 同样也会强制不相干 Text("one: ") 视图的刷新(观察它们背景色的变化)。

下面,我们将 Text("two: ") 包装到自定义的子视图 HySubView 中去:

struct HySubView: View {
    @Binding var val: Int
    
    var body: some View {
        HStack {
            Text("s_two: \(val)")
        }
        .padding()
        .background(Color.random)
    }
}

将 ContentView 中原内容替换为 HySubView:

VStack(spacing: 50.0) {
	Text("one: \(s_one)")
	
	HySubView(val: $s_two)

	// 其它代码从略...
}

现在,我们再来看看更改 s_one 和 s_two 状态对它们的影响:

在这里插入图片描述

可以看到,现在 s_one 和 s_two 状态的变化只会影响其对应的视图,而不会影响到无关的视图了。于是乎我们利用自定义子视图避免了无必要的“冗余”刷新操作。

这只是一个非常简单的例子,在包含大量状态的复杂界面中,利用自定义子视图无疑会带来可观的界面渲染性能提升!棒棒哒!💯🚀

总结

在本篇博文里,我们讨论了 SwiftUI 中利用子视图代替父视图界面布局内容的重要优势,并为大家举了一个非常通俗易懂的例子。

那么,感谢观赏,再会!😎

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/63359.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

[Java反序列化]—CommonsCollections6

先贴个图 0x01: CC 6 应该是CC1 和 URLDNS 的综合&#xff0c;有一定联系&#xff0c;审一下吧 JDK版本需低于 8u71 AnnotationInvocationHandler类的readObject()方法在8u71以后逻辑就发生了改变&#xff0c;不能再利用了&#xff0c;所以就需要找一个绕过高版本的利用链…

Cadence Virtuoso Layout 版图绘制的使用技巧及其相关快捷键

1.版图前准备操作 画好原理图&#xff0c;打好pin脚&#xff08;pin最好以全大写的形式书写&#xff0c;以防后续操作中可能出现Bug&#xff09; 查看所使用工艺库的design rule文件&#xff0c;确定栅格单位设置大小 在准备绘制的原理图界面启动layout XL/GXL 在layout界面…

JS 正则表达式常用方法

1. JS 正则表达式 2. 使用字符串方法 3. 使用 RegExp 方法 1. JS 正则表达式 JS 正则表达式语法: # JS 的正则表达式不需要使用引号包裹&#xff0c;PHP 需要使用引号包裹。修饰符是可选的&#xff0c;可写可不写/正则表达式主体/修饰符JS 中使用正则表达式的方法比较多&am…

【强化学习论文合集】九.2018AAAI人工智能大会论文(AAAI2018)

强化学习(Reinforcement Learning, RL),又称再励学习、评价学习或增强学习,是机器学习的范式和方法论之一,用于描述和解决智能体(agent)在与环境的交互过程中通过学习策略以达成回报最大化或实现特定目标的问题。 本专栏整理了近几年国际顶级会议中,涉及强化学习(Rein…

Python中的Apriori关联算法-市场购物篮分析

数据科学Apriori算法是一种数据挖掘技术&#xff0c;用于挖掘频繁项集和相关的关联规则。本模块重点介绍什么是关联规则挖掘和Apriori算法&#xff0c;以及Apriori算法的用法。 去年&#xff0c;我们为一家公司进行了短暂的咨询工作&#xff0c;该公司正在构建一个主要基于Apr…

使用DIV+CSS技术设计的非遗文化网页与实现制作(web前端网页制作课作业)

&#x1f389;精彩专栏推荐 &#x1f4ad;文末获取联系 ✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 &#x1f482; 作者主页: 【主页——&#x1f680;获取更多优质源码】 &#x1f393; web前端期末大作业&#xff1a; 【&#x1f4da;毕设项目精品实战案例 (10…

m基于自适应遗传优化的IEEE-6建设费用和网络损耗费用最小化电网规划算法matlab仿真

目录 1.算法描述 2.仿真效果预览 3.MATLAB核心程序 4.完整MATLAB 1.算法描述 电力工业是当今世界各国经济的重要组成部分&#xff0c;随着世界经济的不断发展&#xff0c;电网的建设和中长期规划和经济发展之间的矛盾变得越来越突出&#xff0c;对电力系统的需求也变得越来…

微服务框架 SpringCloud微服务架构 16 SpringAMQP 16.7 DirectExchange

微服务框架 【SpringCloudRabbitMQDockerRedis搜索分布式&#xff0c;系统详解springcloud微服务技术栈课程|黑马程序员Java微服务】 SpringCloud微服务架构 文章目录微服务框架SpringCloud微服务架构16 SpringAMQP16.7 DirectExchange16.7.1 发布订阅 - DirectExchange16.7.…

基于遗传优化算法的小车障碍物避障路线规划matlab仿真

目录 1.算法描述 2.仿真效果预览 3.MATLAB核心程序 4.完整MATLAB 1.算法描述 一种通过模拟自然进化过程搜索最优解的方法&#xff0c;对于一个最优化问题&#xff0c;该算法通过一定数量的候选解种群迭代地执行选择、交叉、变异、评价等操作使得种群向更好的解进化。 遗传算…

MyBatisPlus简述

文章目录一、MyBatisPlus入门案例与简介1.入门案例2.springboot整合mybatis的方式3.springboot整合mybatisplus步骤1.创建环境&#xff0c;上面我们已经创建过了步骤2.创建数据库及表步骤2.pom.xml补全依赖步骤3.添加MP的相关配置信息步骤4.根据数据库表创建实体类步骤5.创建Da…

linux安装redis哨兵

安装环境 服务器一台&#xff1a; 服务器IP&#xff1a;172.169.3.251主从端口&#xff1a;6379、6380、6381哨兵端口&#xff0c;26379、26380、26381安装目录&#xff1a;/usr/local/redis配置文件目录&#xff1a;/usr/loca/redis/conf redis安装 1、下载redis wget ht…

设计模式--策略模式

文章目录前言一、未使用设计模式二、策略模式1.定义2.结构三、应用场景四、优缺点优缺参考资料前言 需求&#xff1a; 一天&#xff0c;产品经理走过来对你说。猫啊&#xff08;自称&#xff09;&#xff0c;帮我设计一个计算器&#xff0c;需要的功能有求最大值&#xff0c;最…

【JavaEE-Servlet】Filter过滤器详解

Filter过滤器熟悉的关键字-Filter&#xff08;回顾-联系-可以不看&#xff09;Filter概述何时使用&#xff1f;Filter生命周期过滤器单个实现doFilter方法关于Filter的配置路径Filter的执行顺序在web.xml文件中进行配置的时候&#xff0c;Filter的执行顺序是什么&#xff1f;使…

HTML期末学生大作业-班级校园我的校园网页设计与实现html+css+javascript

&#x1f389;精彩专栏推荐 &#x1f4ad;文末获取联系 ✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 &#x1f482; 作者主页: 【主页——&#x1f680;获取更多优质源码】 &#x1f393; web前端期末大作业&#xff1a; 【&#x1f4da;毕设项目精品实战案例 (10…

[附源码]Python计算机毕业设计Django停车场管理系统

项目运行 环境配置&#xff1a; Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术&#xff1a; django python Vue 等等组成&#xff0c;B/S模式 pychram管理等等。 环境需要 1.运行环境&#xff1a;最好是python3.7.7&#xff0c;…

使用 JavaScript 检测用户是否在线

有时您可能希望增强您的应用程序以通知用户他们可能已经失去了互联网连接。 用户可能正在访问您的网站并收到缓存版本&#xff0c;因此通常看起来他们的互联网仍在工作。 然而&#xff0c;他们失去了引擎盖下的连接&#xff0c;并且不会加载任何新内容。 在这里向他们显示一些…

博客系统(页面设计)

努力经营当下&#xff0c;直至未来明朗&#xff01; 文章目录前言一、【博客列表页】blog_list.html参考代码&#xff1a;二、【博客详情页】 blog_detail.html参考代码&#xff1a;三、【博客登录页】login.html参考代码&#xff1a;四、【博客编辑页】参考代码&#xff1a;【…

QFtp的使用

1. 环境 Win10 QT5.9.9 2. 相关说明 Qt5.0之后移除了QFtp类&#xff08;基于FTP协议实现的一个类&#xff09;&#xff0c;并且使用 QNetworkAccessManager 实现了 Ftp 的上传/下载功能。尽管后者在性能上和稳定性上有所提升&#xff0c;但有些原本 QFtp 有的功能 QNetwork…

什么是微服务?

文章目录什么是微服务微服务技术栈单体架构分布式架构认识微服务微服务技术框架SpringCloud什么是微服务 当我们提到微服务&#xff0c;很多人第一反应就是SpringCloud&#xff0c;但是微服务技术并不能与SpringCloud完全划等号&#xff1a; 微服务是分布式架构的一种&#x…

AI视频监控在畜牧养殖中的技术应用解决方案

一、方案概况 随着养殖业迅猛发展的同时也给养殖业主带来了严峻挑战。对养殖业来说&#xff0c;养殖场大多建立在偏远地区&#xff0c;给集中管理带来不便&#xff1b;畜禽养殖成本大&#xff0c;丢失、偷盗等情况时有发生&#xff0c;容易造成巨大的经济损失。建立一套远程视…