SwiftUI中UIViewRepresentable的使用(UIKit与SwiftUI的桥梁)

UIViewRepresentable是一个协议,用于创建一个SwiftUI视图,该视图包装了一个UIKit视图。通过实现UIViewRepresentable协议,我们可以在SwiftUI中使用自定义的UIKit视图,并与SwiftUI进行交互。

实现UIViewRepresentable

创建一个遵循UIViewRepresentable协议的自定义结构体,比如我们用TextField举例,创建一个TextFieldViewRepresentable,并实现该协议最基础的两个协议方法。

  • makeUIView(context:):创建并返回UIKit视图。
  • updateUIView(_:context:):更新UIKit视图。
struct TextFieldViewRepresentable: UIViewRepresentable {

  func makeUIView(context: Context) -> some UIView {
    let textField = UITextField()
    return textField
  }
  
  func updateUIView(_ uiView: UIViewType, context: Context) {

  }
}

如果在makeUIView方法中明确了要返回的UIKit控件的类型,那么方法的返回值就可以改为指定的类型,而不是some UIView,改完后,把updateUIView方法删了,再重新添加,就会发现updateUIView方法中的uiView的类型不是UIViewType了,而是我们在makeUIView方法中的返回类型。

struct TextFieldViewRepresentable: UIViewRepresentable {

  func makeUIView(context: Context) -> UITextField {
    let textField = UITextField()
    textField.backgroundColor = UIColor.gray
    return textField
  }
  
  func updateUIView(_ uiView: UITextField, context: Context) {

  }
}

SwiftUI中调用:

struct UIViewRepresentableDemo: View {
  var body: some View {
    VStack {
      Text("Hello, World!")
      TextFieldViewRepresentable()
    }
  }
}

UIKit向SwiftUI传值

上面的代码中,我们在TextField中输入任何内容都不会显示在SwiftUI的界面上的,也就是SwiftUI拿不到TextField中输入的内容,这里我们就需要绑定一个值来传递了。

要想拿到输入的内容,我们组要一个协调员去处理TextFieldDelegate的方法,这里就需要实现makeCoordinator()方法。makeCoordinator()方法也是UIViewRepresentable的协议方法。下面在TextFieldViewRepresentable结构体里面定义一个协调员Coordinator

struct TextFieldViewRepresentable: UIViewRepresentable {

  @Binding var text: String

  func makeUIView(context: Context) -> UITextField {
    let textField = UITextField()
    textField.backgroundColor = UIColor.gray
    textField.delegate = context.coordinator
    return textField
  }
  
  func makeCoordinator() -> Coordinator {
    return Coordinator(text: $text)
  }

  class Coordinator: NSObject, UITextFieldDelegate {
    @Binding var text: String

    init(text: Binding<String>) {
      self._text = text
    }

    func textFieldDidChangeSelection(_ textField: UITextField) {
      text = textField.text ?? ""
    }
  }
}

上面代码中我们创建一个类Coordinator,注意是class类型,该类实现了UITextFieldDelegate协议。

Coordinator类中定义了一个@Binding修饰的text属性,用于绑定输入的值。在textFieldDidChangeSelection协议方法中进行赋值。

TextFieldViewRepresentable中我们也定义了一个@Binding修饰的text属性,用于绑定SwiftUI中的@State修饰的String类型的值。

在实现的makeCoordinator()方法中,创建并返回Coordinator的具体实例,并绑定text

makeUIView方法中,这里我们通过contextcoordinator属性能拿到刚才通过makeCoordinator()方法创建的Coordinator实例,并将UITextField的代理设置为该实例。

textField.delegate = context.coordinator

SwiftUI部分调用代码:

struct UIViewRepresentableDemo: View {
  @State private var text: String = ""

  var body: some View {
    VStack {
      Text(text)
      TextFieldViewRepresentable(text: $text)
        .frame(height: 50)
        .padding()
    }
  }
}

使用效果如下,在输入框输入的时候,界面显示出了输入的内容。
在这里插入图片描述

SwiftUI向UIKit传值

有些时候SwiftUI界面不断刷新的时候,也需要想UIKit组件传递数据,比如刚才的代码,修改一下增加了一个SwiftUITextField组件。
在这里插入图片描述
UIKitTextField输入的时候,SwiftUITextField也显示了输入的字符串,因为我们已经将text值传到SwiftUI界面了。
但是当SwiftUITextField在输入的时候,UIKitTextField确没有任何显示。要实现这个,我们需要用到前面提到的updateUIView方法了,该方法在SwiftUI界面刷新的时候调用。

func updateUIView(_ uiView: UITextField, context: Context) {
  uiView.text = text
}

text是通过@Binding绑定的值,我们将这个textUITextField即可。

定义修饰符方法

TextFieldViewRepresentable是我们定义的一个用在SwiftUI中的结构体,我们给它加一些方法,这样在SwiftUI中通过点语法就可以修改TextFieldViewRepresentable的某些属性了。

TextFieldViewRepresentable组件可能用在很多地方,不同的用处可能会设置不同的样式属性等,比如说背景色,如果在SwiftUI中直接调用.background(Color.red)方法,是不起作用的,因为没有直接修改到UITextField

在这里插入图片描述
那么现在在TextFieldViewRepresentable中添加一个属性和一个方法。

var backgroundColor: UIColor?
func backgroundColor(_ color: UIColor) -> TextFieldViewRepresentable {
  var clone = self
  clone.backgroundColor = color
  return clone
}

该方法传入UIColor,并返回TextFieldViewRepresentable实例,即当前的self,这样在SwiftUI中就可以连续调用点语法了。

makeUIView方法中,我们给UITextField设置backgroundColor

func makeUIView(context: Context) -> UITextField {
  let textField = UITextField()
  textField.delegate = context.coordinator
  textField.placeholder = "Please input..."
  textField.borderStyle = .roundedRect
  textField.backgroundColor = backgroundColor
  return textField
}

最终调用和效果如下:
在这里插入图片描述
切记自定义的方法要在SwiftUI中初始化组件后就调用,如果在系统的修饰符方法后调用则会报错的,因为其他修饰符方法返回的是some View类型,该类型下是没有我们定义的方法的。

完整代码

struct UIViewRepresentableDemo: View {
  @State private var text: String = ""

  var body: some View {
    VStack {
      Text(text)
      HStack {
        Text("SwiftUI:")
        TextField("Please input...", text: $text)
          .textFieldStyle(.roundedBorder)
      }
      HStack {
        Text("UIKit:")
        TextFieldViewRepresentable(text: $text)
          .backgroundColor(UIColor.cyan)
          .frame(height: 50)
          .padding()
      }
    }
  }
}

struct TextFieldViewRepresentable: UIViewRepresentable {

  @Binding var text: String
  var backgroundColor: UIColor?

  func makeUIView(context: Context) -> UITextField {
    let textField = UITextField()
    textField.delegate = context.coordinator
    textField.placeholder = "Please input..."
    textField.borderStyle = .roundedRect
    textField.backgroundColor = backgroundColor
    return textField
  }
  
  func updateUIView(_ uiView: UITextField, context: Context) {
    uiView.text = text
  }

  func backgroundColor(_ color: UIColor) -> TextFieldViewRepresentable {
    var clone = self
    clone.backgroundColor = color
    return clone
  }

  func makeCoordinator() -> Coordinator {
    return Coordinator(text: $text)
  }

  class Coordinator: NSObject, UITextFieldDelegate {
    @Binding var text: String

    init(text: Binding<String>) {
      self._text = text
    }

    func textFieldDidChangeSelection(_ textField: UITextField) {
      text = textField.text ?? ""
    }
  }
}

写在最后

本文主要介绍了在SwiftUI中如何包装UIKit组件,当SwiftUI组件无法满足我们的App设计需求的时候,可以使用UIKit组件。

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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Flink任务如何跑起来之 2.算子 StreamOperator

Flink任务如何跑起来之 2.算子 StreamOperator 前文介绍了Transformation创建过程&#xff0c;大多数情况下通过UDF完成DataStream转换中&#xff0c;生成的Transformation实例中&#xff0c;核心逻辑是封装了SimpleOperatorFactory实例。 UDF场景下&#xff0c;DataStream到…

机器学习python实践——关于ward聚类分层算法的一些个人心得

最近在利用python跟着参考书进行机器学习相关实践&#xff0c;相关案例用到了ward算法&#xff0c;但是我理论部分用的是周志华老师的《西瓜书》&#xff0c;书上没有写关于ward的相关介绍&#xff0c;所以自己网上查了一堆资料&#xff0c;都很难说清楚ward算法&#xff0c;幸…

数据分析常用6种分析思路(下)

作为一名数据分析师&#xff0c;你又没有发现&#xff0c;自己经常碰到一些棘手的问题就没有思路&#xff0c;甚至怀疑自己究竟有没有好好学过分析&#xff1f; 在上篇文章里&#xff0c;我们讲到了数据分析中的流程、分类、对比三大块&#xff0c;今天&#xff0c;我们继续讲…

为Nanopi m1交叉编译opencv

为Nanopi m1交叉编译opencv 一、下载交叉编译器 根据之前的博客进行 二、下载opencv和必要库 sudo apt-get install cmake git libgtk2.0-dev pkg-config libavcodec-dev libavformat-dev libswscale-devgit clone https://github.com/opencv/opencv.git cd opencv三、进行编…

计算机网络实验(15):基于Socket的网络编程(附JAVA源码.txt)

一、实验名称 UDP客户服务器即时通信程序 二、实验目的&#xff1a; 掌握基于SOCKET的网络编程方法。 基于JAVA语言&#xff0c;编写一个SOCKET的即时通信小程序 三、实验内容和要求 实验内容&#xff1a; 基于JAVA语言&#xff0c;编写一个SOCKET的即时通信小程序 实…

docker一些常用命令以及镜像构建完后部署到K8s上

docker一些常用命令以及镜像构建完后部署到K8s上 1.创建文件夹2.删除文件3.复制现有文件内容到新建文件4.打开某个文件5.查看文件列表6.解压文件&#xff08;tar格式&#xff09;7.解压镜像8.查看镜像9.删除镜像10.查看容器11.删除容器12.停止运行容器13.构建镜像14.启动容器15…

Mongodb在UPDATE操作中使用$push向数组中插入数据

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

无需破解,基于AI翻译的Poedit翻译小助手PoeditHelper

背景&#xff1a; 应用在做国际化的时候是一件比较让人头大的事情&#xff0c;需要进行多国语言互译&#xff0c;做国际化的方式有很多&#xff0c;现阶段比较常用的方式是gettext的形式&#xff0c;并输出一个.po文件来做国际化&#xff0c;与之配套的有一款半开源软件叫Poedi…

【PB案例学习笔记】-21小大写金额转换

写在前面 这是PB案例学习笔记系列文章的第21篇&#xff0c;该系列文章适合具有一定PB基础的读者。 通过一个个由浅入深的编程实战案例学习&#xff0c;提高编程技巧&#xff0c;以保证小伙伴们能应付公司的各种开发需求。 文章中设计到的源码&#xff0c;小凡都上传到了gite…

晶振的匹配电容的计算

晶振 等效电路 C0是晶振的静态电容 L1是晶振的等效电感 C1是晶振的等效电容 R1是晶振的等效串联电阻 芯片内部已有反相器和负载电阻 计算公式 参考1 参考2

Blender骨骼创建

骨骼系统 建立 使用Shift A添加骨骼或在添加|骨架中添加一段骨骼 骨骼的三种模式 -物体模式&#xff1a;做动画&#xff0c;摆人物pose时在该模式 -编辑模式&#xff1a;进行骨骼搭建&#xff08;选择一段骨骼&#xff0c;然后按E挤出一段骨骼并进行调整&#xff09; -姿…

matlab 任意二维图像转点云

目录 一、概述二、代码实现三、结果展示本文由CSDN点云侠原创,原文链接。如果你不是在点云侠的博客中看到该文章,那么此处便是不要脸的爬虫。 一、概述 给定任意一张图片,通过代码操作将图片转成点云。图像中包含大量可用信息,其中必不可少的信息为像素坐标和像素值,将像…

【乐吾乐2D可视化组态编辑器】导出HTML,下载离线部署包

乐吾乐2D可视化组态编辑器地址&#xff1a;https://2d.le5le.com/ 使用步骤 1. 从“文件”菜单导出HTML 导出为 HTML 需要一定的开发能力&#xff0c;后续不再维护&#xff0c;即将下线&#xff0c;推荐使用 下载离线部署包&#xff08;html&#xff09; 2. 解压 3. 下载后端…

Intellij IDEA开发Android项目打包生成APK

在 IntelliJ IDEA 左上方中选择 “Build” -> “Generate Signed Bundle / APK…”选择“APK”——“Next”——“Create New…”&#xff08;Password随便填123456即可&#xff09; “Next”——选择release&#xff08;APK生成后默认存放在本项目的release文件夹里&#x…

Leetcode 力扣119. 杨辉三角 II (抖音号:708231408)

给定一个非负索引 rowIndex&#xff0c;返回「杨辉三角」的第 rowIndex 行。 在「杨辉三角」中&#xff0c;每个数是它左上方和右上方的数的和。 示例 1: 输入: rowIndex 3 输出: [1,3,3,1]示例 2: 输入: rowIndex 0 输出: [1]示例 3: 输入: rowIndex 1 输出: [1,1]提示…

Cisco Packet Tracer实验(二)

二、用交换机构建 LAN 构建物件如下&#xff1a; 四个PC 两个交换机 一个Multi Switch多功能拓展控制器 连线必须是这个直线&#xff01;&#xff01;&#xff01;不是虚线 最后实现效果如下&#xff1a; 全部的线是绿的&#xff0c;就表示是通的。 尝试一下&#xff0c;看PC…

SpringBoot系列——使用Spring Cache和Redis实现查询数据缓存

文章目录 1. 前言2. 缓存2.1 什么是缓存2.2 使用缓存的好处2.3 缓存的成本2.4 使用Spring Cache和Redis的优点 3. Spring Cache基础知识3.1 Spring Cache的核心概念3.2 Spring Cache的注解3.2.1 SpEL表达式3.2.2 Cacheable3.2.3 CachePut3.2.4 CacheEvict 4. 实现查询数据缓存4…

量化交易入门——盘口

今天接着上一期讲解开盘定势的种类&#xff0c;在讲之前&#xff0c;科普一下“盘口五档”的成交知识。 每个炒股软件上&#xff0c;都会有某只个股的成交信息&#xff0c;在其中会出现一个五档的行情列表&#xff0c;里面列出了买家和卖家各五个价格及其对应的数量。这五档价…

深入浅出 Go 语言的 GPM 模型(Go1.21)

引言 在现代软件开发中&#xff0c;有效地利用并发是提高应用性能和响应速度的关键。随着多核处理器的普及&#xff0c;编程语言和框架如何高效、简便地支持并发编程&#xff0c;成为了软件工程师们评估和选择工具时的一个重要考量。在这方面&#xff0c;Go 语言凭借其创新的并…

基于51单片机的教室智能照明控制系统

一.硬件方案 本系统以51单片机作为控制模块的核心部件&#xff0c;采用热释红外人体传感器检测人体的存在&#xff0c;采用光敏三极管构成的电路检测环境光的强度&#xff1b;根据教室合理开灯的条件&#xff0c;通过对人体存在信号和环境光信号的识别与判断&#xff0c;完成对…