SwiftUI中的@ViewBuilder理解与使用

news2024/11/24 3:00:35

@ViewBuilder是一个属性包装器,也是一个自定义的函数包装器,用于构建一个或多个视图。在 SwiftUI 中,很多地方都使用了 @ViewBuilder,例如 VStackHStackZStackGroup 等。它可以接受多个视图并返回一个单一的组合视图。

比如我们最常用的View协议中的body,就是用了@ViewBuilder,下面是body的定义:

public protocol View {
    associatedtype Body : View
    @ViewBuilder @MainActor var body: Self.Body { get }
}

body属性前已经用@ViewBuilder进行了修饰,这样,我们在body闭包里就可以添加一个或者多个视图,也不需要些return这样的关键字,SwiftUI将接受多个视图并返回一个单一的组合视图。

如果仿写一个类似View的协议,去掉body前修饰的@ViewBuilder

protocol NonViewBuilderView {
  associatedtype Body : View
  @MainActor var body: Self.Body { get }
}


struct ViewBuilderDemo: NonViewBuilderView {
  var body: some View {
    Text("Hi")
  }
}

我们创建界面ViewBuilderDemo,遵守并实现NonViewBuilderView协议,在body中只放入了一个Text文本,运行起来是没问题的。

但是如果在里面放入了不同类型的视图,就会报错了: Branches have mismatching types 'Text' and 'Button<Text>'

在这里插入图片描述
解决这个问题也比较简单,给body加上@ViewBuilder即可:

struct ViewBuilderDemo: NonViewBuilderView {
  @State private var isText: Bool = false

  @ViewBuilder
  var body: some View {
    if isText {
      Text("Hi")
    } else {
      Button("Button") { }
    }
  }
}

@ViewBuilder修饰属性

在开发过程中,有时候为了让body瘦身,会把里面很多的UI逻辑部分抽出来到一个属性当中去,比如下面的代码:

enum ViewType {
  case one
  case two
  case three
}

struct ViewBuilderDemo: View {
  var viewType: ViewType = .one

  var body: some View {
    contentView
  }

  @ViewBuilder
  private var contentView: some View {
    switch viewType {
      case .one:
        Text("One")
      case .two:
        HStack {
          Text("Two")
          Image(systemName: "heart.fill")
        }
      case .three:
        Image(systemName: "heart.fill")
    }
  }
}

ViewBuilderDemo结构体中,我们定义了一个私有变量contentView,这个变量返回了一些根据逻辑要显示的不同的视图。
如果这个contentView属性不加@ViewBuilder修饰,那么必然会报错了。

在这里插入图片描述

@ViewBuilder修饰方法

有些时候我们更喜欢用方法去封装一些UI,比如上面同样的逻辑,改成使用方法。

@ViewBuilder
private func contentViewBaseOnType() -> some View {
  switch viewType {
    case .one:
      Text("One")
    case .two:
      HStack {
        Text("Two")
        Image(systemName: "heart.fill")
      }
    case .three:
      Image(systemName: "heart.fill")
  }
}

原理和修饰属性一样,方法里面也返回了很多类型的组件,一个或者多个,如果不用@ViewBuilder修饰就会报错的。
在这里插入图片描述

@ViewBuilder修饰参数

有时候自定义组件,需要从外部传入具体的显示内容,而组件只是负责一些布局类的逻辑,比如下面这段代码:

struct HeaderView<Content: View>: View {
  let isHorizontal: Bool
  let content: Content

  init(isHorizontal: Bool, content: () -> Content) {
    self.isHorizontal = isHorizontal
    self.content = content()
  }

  var body: some View {
    if isHorizontal {
      HStack {
        content
      }
    } else {
      VStack {
        content
      }
    }
  }
}

HeaderView组件有两个属性,isHorizontalcontent,根据isHorizontal的不同,进而选择不同的排列方式将content显示。这里面的Content采用了泛型,继承了View,如果直接定义一个View类型的content会报错的。

init方法中,传入isHorizontal和构建content的闭包,就目前的定义看似没有问题,但是在调用的时候报错了。
在这里插入图片描述
这里的错误主要是我们初始化方法里面构建content的闭包有问题,这个闭包里面可以放置很多类型的组件,SwiftUI认为这些组件组成的一个大组件不符合View协议。这就和我们上面说的很类似,我们需要告知SwiftUI,构建一个组合视图,修改一下init方法即可,在content参数前加上@ViewBuilder

 init(isHorizontal: Bool, @ViewBuilder content: () -> Content) {
  self.isHorizontal = isHorizontal
  self.content = content()
}

除了这种方法,我们也可以省略init方法,在定义content属性的地方添加@ViewBuilder。重构一下HeaderView组件:

struct HeaderView<Content: View>: View {
  let isHorizontal: Bool
  @ViewBuilder let content: () -> Content

  var body: some View {
    if isHorizontal {
      HStack {
        content()
      }
    } else {
      VStack {
        content()
      }
    }
  }
}

这里将content直接定义为闭包类型,并且前面加上了@ViewBuilder,这样在初始化的时候同样不会报错。

写在最后

@ViewBuilderSwiftUI 中一个非常有用的工具,它极大地简化了视图的组合和复用。通过在属性,方法以及初始化方法上合理使用 @ViewBuilder,可以更有效地构建复杂且高效的用户界面。

最后,希望能够帮助到有需要的朋友,如果觉得有帮助,还望点个赞,添加个关注,笔者也会不断地努力,写出更多更好用的文章。

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

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

相关文章

超详解——Python 元组详解——小白篇

目录 1. 元组简介 创建元组 2. 元组常用操作 访问元组元素 切片操作 合并和重复 成员操作符 内置函数 解包元组 元组方法 3. 默认集合类型 作为字典的键 作为函数参数 作为函数的返回值 存储多种类型的元素 4.元组的优缺点 优点 缺点 5.元组的使用场景 数据…

【iOS】UI学习——界面切换

界面切换 push和poppresent和dismiss示例程序 push和pop 在 Objective-C 中,pop 和 push 通常是与 UINavigationController 一起使用的方法,用于控制导航栈中视图控制器的跳转和回退。 push 和 pop 通常成对使用,用于实现导航栈的前进和后退功能。当用户进入新的视图控制器时…

Linux——自动化运维ansibe

一、自动化运维定义 自动化--- 自动化运维&#xff1a; 服务的自动化部署操作系统的日常运维&#xff1a;日志的备份、临时文件清理、服务器日常状态巡检、&#xff08;几乎包括了linux服务管理、linux 系统管理以及在docker 容器课程中涉及的所有内容&#xff09;服务架构的…

FPGA专项课程即将开课,颁发AMD官方证书

社区成立以来&#xff0c;一直致力于为广大工程师提供优质的技术培训和资源&#xff0c;得到了众多用户的喜爱与支持。为了满足用户需求&#xff0c;我们特别推出了“基于Vitis HLS的高层次综合及图像处理开发”课程。 本次课程旨在帮助企业工程师掌握前沿的FPGA技术&#xff…

谷歌工程师指责OpenAI阻碍AGI研究进展:推迟了5到10年

Google母公司Alphabet的一位软件工程师表示&#xff0c;OpenAI阻碍了人工通用智能&#xff08;AGI&#xff09;的发展5到10年。在最近的一次播客访谈中&#xff0c;Google软件工程师弗朗索瓦乔莱特&#xff08;Franois Chollet &#xff09;表达了他对AGI研究现状的担忧。这段对…

MySQL----常见的存储引擎

存储引擎 存储引擎就是数据库如何存储数据、如何为存储的数据建立索引和如何更新、查询数据等技术的实现方法。因为在关系数据库中数据的存储是以表的形式存储的&#xff0c;所以存储引擎也可以称为表类型&#xff08;即存储和操作此表的类型&#xff09;。 MySQL存储引擎 M…

【Apollo配置中心】集成springboot自动监听属性变更和动态发布配置

1. 背景 在实际项目中&#xff0c;Spring Boot项目结合使用Apollo配置中心时&#xff0c;经常会遇到需要更新Apollo上的项目的一些配置&#xff0c;比如测试环境或生产环境中&#xff0c;需要修改某个类的属性值&#xff0c;如果我们在Apollo上更新了配置&#xff0c;已经在运…

M41T00串行实时时钟-国产兼容RS4C400

RS4C400是一种低功耗串行实时时钟&#xff08;RTC&#xff09;。内置32.768 kHz振荡器&#xff08;外部晶体控制&#xff09;和RAM的前8个字节用于时钟/日历功能&#xff0c;并以二进制编码十进制&#xff08;BCD&#xff09;格式配置。地址和数据通过双线双向总线串行传输。内…

WPF Prism框架搭建

WPF Prism框架搭建 1.引入Prism框架 在Nuget包管理器中搜索Prism&#xff0c;并添加到项目中 2.在项目中使用prism框架 2.1 修改app.xaml 删除项目中自带的StartupUri 修改Application节点为prism:PrismApplication 引入prism命名空间 <prism:PrismApplication x:C…

2024年中国移动游戏市场研究报告

来源&#xff1a;点点数据&#xff1a; 近期历史回顾&#xff1a; 面向水泥行业的5G虚拟专网技术要求&#xff08;2024&#xff09;.pdf 2024年F5G-A绿色万兆全光园区白皮书.pdf 2024年全球废物管理展望报告.pdf 内容管理系统 2024-2025中国羊奶粉市场消费趋势洞察报告.pdf 20…

MySQL 示例数据库大全

前言&#xff1a; 我们练习 SQL 时&#xff0c;总会自己创造一些测试数据或者网上找些案例来学习&#xff0c;其实 MySQL 官方提供了好几个示例数据库&#xff0c;在 MySQL 的学习、开发和实践中具有非常重要的作用&#xff0c;能够帮助初学者更好地理解和应用 MySQL 的各种功…

糖尿病患者血糖控制困难,4个辅助降糖方法分享。

对于糖尿病患者来讲&#xff0c;血糖控制极为困难&#xff0c;稍不留意就会致使忽高忽低的情况出现&#xff0c;今天我来教你 4 个办法来辅助降糖。 第一&#xff0c;在饮食上可多进食全谷类食物&#xff0c;中医认为谷类食物是脾胃的主食。经常吃这类食物不但能够给脾胃提供充…

DSSA(Domain-Specific Software Architecture)方法论

DSSA&#xff08;Domain-Specific Software Architecture&#xff09;方法论是一种针对特定问题领域的软件架构设计方法。在软件开发中&#xff0c;有些问题在特定领域是共通的&#xff0c;这些问题可以通过通用的抽象和解决方案来处理。DSSA方法论正是利用这一特点&#xff0c…

比 Safari 阅读模式更强大的阅读助手

一、简介 1、一款专为浏览器设计的扩展程序&#xff0c;旨在提供更为简洁、高效的网页阅读体验。它能够对网页内容进行智能提取和排版&#xff0c;自动屏蔽广告和噪音&#xff0c;使读者能够专注于网页的核心内容。此外&#xff0c;Circle 阅读助手还具备多种个性化功能&#x…

Mongodb在UPDATE操作中使用$pull操作

学习mongodb&#xff0c;体会mongodb的每一个使用细节&#xff0c;欢迎阅读威赞的文章。这是威赞发布的第68篇mongodb技术文章&#xff0c;欢迎浏览本专栏威赞发布的其他文章。如果您认为我的文章对您有帮助或者解决您的问题&#xff0c;欢迎在文章下面点个赞&#xff0c;或者关…

c++实战知识点

c实战知识点 一、概述1.数据2.C11的原始字面量3.数据类型的别名4.const修饰指针5.void关键字6.内存模型7.二级指针8.函数指针和回调函数9.数组10.C风格字符串11.二维数组用于函数的参数行指针&#xff08;数组指针&#xff09; 12.引用引用与const 13.各种形参的使用场景14.重载…

数据结构01 栈及其相关问题讲解

栈是一种线性数据结构&#xff0c;栈的特征是数据的插入和删除只能通过一端来实现&#xff0c;这一端称为“栈顶”&#xff0c;相应的另一端称为“栈底”。 栈及其特点 用一个简单的例子来说&#xff0c;栈就像一个放乒乓球的圆筒&#xff0c;底部是封住的&#xff0c;如果你想…

Druid 参数配置详解

简介 Java 程序很大一部分要操作数据库&#xff0c;为了提高性能操作数据库的时候&#xff0c;又不得不使用数据库连接池。 Druid 是阿里巴巴开源平台上一个数据库连接池实现&#xff0c;结合了 C3P0、DBCP 等 DB 池的优点&#xff0c;同时加入了日志监控。 Druid 可以很好的…

基础-01-计算机网络概论

一. 计算机网络的发展与分类 1.计算机网络的形成与发展 计算机网络&#xff1a;计算机技术与通信技术的结合 ICTITCT 2.计算机网络标准阶段 3.计算机网络分类1:通信子网和资源子网 通信子网:通信节点(集线器、交换机、路由器等)和通信链路(电话线、同轴电缆、无线电线路、卫…

HashMap第3讲——JDK1.8红黑树细节

上篇文章对HashMap的put方法进行了源码解析&#xff0c;并介绍了其中的两个亮点设计——位运算取代%和扰动计算。其中还有几个细节&#xff0c;比如每次扩容都是2^n是怎么做到的、JDK1.8增加的红黑树结构&#xff0c;由于篇幅原因没有介绍&#xff0c;本节就先来介绍其中的一个…