Loading

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")
    }
}

示例二

封装Highlightr

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))
    }
}

参考资料

Importing interactive UIKit views into SwiftUI

posted @ 2023-09-20 10:23  逆行  阅读(7)  评论(0编辑  收藏  举报