SwiftUI自定义的TextField的回退按键事件

在这篇博客中,将介绍如何结合使用 SwiftUI 结合 UIKit 来实现一个自定义TextField,当按下退格键的时, 触发指定的方法。

目录
  1. 文章背景
  2. 集成SwiftUI和UIkit
  3. UIViewRepresentable
  4. Coordinator
  5. 完整代码

1. 背景

最近帮别人做APP,为了钱,什么都学,后端有的是人。。。 于是看了一个下午的官方demogpt就搞起来了。现在弄得差不多了,回来写点东西,不然博客上啥都没有。下面是正文

SwiftUI 写个简单的界面确实挺方便的,几个VstackImageButton堆一下就有了 ,但在处理某些复杂的交互时就有点不太方便了。这个时候就要用UIKit了,例如,SwiftUI 中的 TextField 目前不支持直接检测退格键的按下事件。而在UIKit 中,我们可以通过重写 UITextFielddeleteBackward 方法来实现这一功能,这玩意在哪里用得上,在验证码界面上。。。为啥不用开源的?时间足,就自己写个,出问题了好定位,且容易改。

2. 集成 SwiftUI 和 UIKit

SwiftUI 的视图(View)必须是遵循 SwiftUI 的 View 协议的类型,而 UIKit 的 UITextField 是 UIKit 的类,并不符合 SwiftUI 的 View 协议。SwiftUI 和 UIKit 是两个独立的 UI 框架,因此直接在 SwiftUI 中使用 UIKit 的视图(如 UITextField)是不允许的。

因此我们需要使用 UIViewRepresentable 协议来创建和管理 UITextField,才能够在SwiftUI中使用UIKitUITextField
在这里插入图片描述

CustomTextField是我自己定义的继承了UITextField的组件
在这里插入图片描述

3. UIViewRepresentable

UIViewRepresentable 是 SwiftUI 提供的一个协议,用Java的语言来说,我简单地理解为接口。我们要做的就是自定义一个结构体,然后实现协议中几个方法,然后就可以在SwiftUI中用到UIKit的东西了

  • makeUIView(context:):用于创建并返回一个 UIKit 视图实例,该方法只会在初始化的时候执行一次

  • updateUIView(_:context:):用于更新 UIKit 视图的状态

  • makeCoordinator():用于创建并返回一个协调器实例,当你在界面交互,触发UIKit 视图的方法和事件时,就会调用该协调器中的对应的方法。

当我们ContentView实例化的时候,遇到

// 自定义的文本视图,绑定 text 和 onBackspace 回调
CustomTextView(...)

就会去创建我们的 UIViewRepresentable,实例化过程中会先调用makeCoordinator()创建一个coordinator对象放到context对象中。
然后调用makeUIView()创建UIKit组件,并context中取出coordinator对象交给UITextField,

当用户在界面上与UITextField交互的时候,UITextField就会调用Coordinator的方法,在Coordinator中可以修改我们在SwiftUI视图中的一些变量或者属性,例如@Binding的变量。这样子就完成了UIKit对象同步信息到SwiftUI对象。

这里可以看出UIKit中自己玩自己的(数据和行为和以前一样),只不过在自己玩的时候, 多做一步同步信息给你SwiftUI.

Coordinator使得UIkit对象可以同步信息给SwiftUI,那么SwiftUI如何同步信息给UIkit呢?答案是updateUIView方法。

当你通过其他的SwiftUI组件的交互导致text变化了,就要通过updateUIView方法来同步给UITextField,否则UITextField的值并不会变化。

例如我们可以通过一个按钮来改变text属性,因为UIViewRepresentable是View的一个子类,text是@State属性,就会触发View的重新绘制。在重新绘制的过程中,会调用updateUIView来同步UITextField中的值。当然这里也可以去让UITextField不可编辑之类的操作。

在这里插入图片描述

4. Coordinator

// Coordinator 类,用于处理 UITextField 的委托方法
    class Coordinator: NSObject, UITextFieldDelegate {
        var parent: CustomTextView

        // 初始化方法,传入 CustomTextView 实例
        init(parent: CustomTextView) {
            self.parent = parent
        }

        // 当 UITextField 的文本发生变化时调用
        func textFieldDidChangeSelection(_ textField: UITextField) {
            // 更新 CustomTextView 的 text 属性
            parent.text = textField.text ?? ""
        }
    }

Coordinator 类中,如果你希望处理 UITextField 的某些事件(例如文本输入变化),就需要实现对应的 UITextFieldDelegate 方法。如果没有特定需求,协议中的方法都可以省略。

这里我咋知道有哪些方法可以代理呢? 问GPT。。。。等要用了再研究官方文档

5. 完整代码示例

在这里插入图片描述

代码
import SwiftUI
import UIKit

struct ContentView: View {
    @State private var text = ""
    @State private var isBackspacePressed = false

    var body: some View {
        VStack {
            Text("用户按下了回退键?")
                .font(.title)
                .padding()
            Text("\(isBackspacePressed ? "Yes" : "No")").font(.title)

            // 自定义的文本视图,绑定 text 和 onBackspace 回调
            CustomTextView(text: $text, onBackspace: {
                // 当按下删除键时,将 isBackspacePressed 设置为 true
                isBackspacePressed = true
                // 1 秒后将 isBackspacePressed 重置为 false
                DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
                    isBackspacePressed = false
                }
            })
            .frame(height: 50) // 设置视图高度
            .background(Color.gray.opacity(0.2)) // 设置背景颜色和透明度
            .cornerRadius(8) // 设置圆角
            .padding() // 设置内边距
        }
        .padding()
    }
}

struct CustomTextView: UIViewRepresentable {
    @Binding var text: String // 绑定的文本属性
    let onBackspace: () -> Void // 删除键回调

    func makeUIView(context: Context) -> CustomTextField {
        let textField = CustomTextField() // 创建自定义的 UITextField
        textField.delegate = context.coordinator // 设置委托
        textField.onBackspace = onBackspace // 设置删除键回调
        textField.placeholder = "Enter text" // 设置占位符
        textField.borderStyle = .roundedRect // 设置边框样式
        return textField
    }

    func updateUIView(_ uiView: CustomTextField, context: Context) {
        uiView.text = text // 更新 UITextField 的文本
        //uiView.isHidden = true
    }

    func makeCoordinator() -> Coordinator {
        return Coordinator(parent: self) // 创建并返回 Coordinator 实例
    }

    // Coordinator 类,用于处理 UITextField 的委托方法
    class Coordinator: NSObject, UITextFieldDelegate {
        var parent: CustomTextView

        // 初始化方法,传入 CustomTextView 实例
        init(parent: CustomTextView) {
            self.parent = parent
        }

        // 当 UITextField 的文本发生变化时调用
        func textFieldDidChangeSelection(_ textField: UITextField) {
            // 更新 CustomTextView 的 text 属性
            parent.text = textField.text ?? ""
        }
    }
}

class CustomTextField: UITextField {
    // 定义一个回调,当用户按下删除��时调用
    var onBackspace: (() -> Void)?

    // 重写 deleteBackward 方法
    override func deleteBackward() {
        // 调用外部回调
        onBackspace?()
        // 打印日志信息
        print("用户按下了删除键,但是我不删除,哈哈")
    }
}

// 预览 ContentView
#Preview {
    ContentView()
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值