ViewRepresentable用法之Coordinator模式
从UIKit/AppKit转到SwiftUI,其实是模式上的转换。
UIView/NSView常以代理(delegate)接收事件的方式与界面通信,而SwiftUI通过“值”变化直接响应界面事件。
为了实现这种“UIKit/AppKit事件”到“SwiftUI属性”的映射,我们需要借用一个对象,用它来监听UIKit/AppKit事件,提取有用的信息,并赋值给SwiftUI属性。约定俗成的把这个对象叫做Coordinator。
示例一
封装UITextView
struct TextView: UIViewRepresentable {
@Binding var text: String
func makeCoordinator() -> Coordinator {
Coordinator(text: $text)
}
func makeUIView(context: Context) -> UITextView {
let view = UITextView()
view.font = .preferredFont(forTextStyle: .body)
view.delegate = context.coordinator
return view
}
func updateUIView(_ view: UITextView, context: Context) {
view.text = text
}
}
extension TextView {
class Coordinator: NSObject, UITextViewDelegate {
@Binding private var text: String
init(text: Binding<String>) {
_text = text
}
func textViewDidChange(_ textView: UITextView) {
text = textView.text
}
}
}
//使用
struct BiographyEditView: View {
@Binding var biography: String
var body: some View {
TextView(text: $biography)
.padding(10)
.border(Color.primary, width: 0.5)
.padding()
.navigationTitle("Edit your biography")
}
}
示例二
import SwiftUI
struct HighlightTextEditor: NSViewRepresentable {
@Binding var code: String
var language: String
var size: CGSize
func makeNSView(context: NSViewRepresentableContext<HighlightTextEditor>) -> NSTextView {
let textStorage = CodeAttributedString()
textStorage.language = language
let layoutManager = NSLayoutManager()
textStorage.addLayoutManager(layoutManager)
let textContainer = NSTextContainer(size: size)
layoutManager.addTextContainer(textContainer)
let textView = NSTextView(frame: .init(origin: .zero, size: size), textContainer: textContainer)
textView.string = code
textView.delegate = context.coordinator
return textView
}
func updateNSView(_ nsView: NSTextView, context: NSViewRepresentableContext<HighlightTextEditor>) {
nsView.string = code
}
func makeCoordinator() -> Coordinator {
Coordinator($code)
}
}
extension HighlightTextEditor{
class Coordinator: NSObject, NSTextViewDelegate{
@Binding var code: String
init(_ code: Binding<String>) {
_code = code
}
func textDidChange(_ notification: Notification) {
guard let textView = notification.object as? NSTextView else {
return
}
code = textView.string
}
}
}
struct HighlightTextEditor_Previews: PreviewProvider {
static var previews: some View {
HighlightTextEditor(code: .constant("let a = 123"), language: "swift", size: .init(width: 300, height: 300))
}
}