I'm looking to create an editable multi-line text box in Swift UI for macOS. I'd like to create a syntax highlighting text editor, so it'd be multi-line and change styles throughout the lines. Is this possible with the framework in its current state? I can find barely any documentation about it online.
Asked
Active
Viewed 2,604 times
3
-
It looks like SwiftUI now has a TextEditor component here: https://developer.apple.com/documentation/swiftui/texteditor But it doesn't support changing styles like colours throughout the line – deeBo Jul 30 '20 at 12:56
2 Answers
1
it can be useful, this is my first solution to get an NSTextView with SwiftUI:
import SwiftUI
import os
let uiLog = OSLog(subsystem: "com.visual-science.CryptiK", category: "UI")
class EditorCoordinator : NSObject, NSTextViewDelegate {
let textView: NSTextView;
let scrollView : NSScrollView
let text : Binding<NSAttributedString>
init(binding: Binding<NSAttributedString>) {
text = binding
textView = NSTextView(frame: .zero)
textView.autoresizingMask = [.height, .width]
textView.textStorage?.setAttributedString(text.wrappedValue)
textView.textColor = NSColor.textColor
scrollView = NSScrollView(frame: .zero)
scrollView.hasVerticalScroller = true
scrollView.autohidesScrollers = false
scrollView.autoresizingMask = [.height, .width]
scrollView.documentView = textView
super.init()
textView.delegate = self
}
func textDidChange(_ notification: Notification) {
switch notification.name {
case NSText.didChangeNotification :
text.wrappedValue = (notification.object as? NSTextView)?.textStorage ?? NSAttributedString(string: "")
default:
os_log(.error, log: uiLog, "Coordinator received unwanted notification")
}
}
}
struct DataTextEditorView: View, NSViewRepresentable {
typealias Coordinator = EditorCoordinator
typealias NSViewType = NSScrollView
let text : Binding<NSAttributedString>
func makeNSView(context: NSViewRepresentableContext<DataTextEditorView>) -> DataTextEditorView.NSViewType {
os_log(.info, log: uiLog, "%@", context.coordinator.scrollView)
return context.coordinator.scrollView
}
func updateNSView(_ nsView: NSScrollView, context: NSViewRepresentableContext<DataTextEditorView>) {
os_log(.debug, log: uiLog, "%@", context.coordinator.self)
os_log(.debug, log: uiLog, "%@", text.wrappedValue)
}
func makeCoordinator() -> EditorCoordinator {
os_log(.info, log: uiLog, "makeCoordinator")
let coordinator = EditorCoordinator(binding: text)
return coordinator
}
}
If like me, you just need to edit some text without attributes, you can replace NSAttributedString with just String and adapt the code for this simpler case.
igerard
- 66
- 5
-
Something important here, the SwiftUI struct is created a lot of time, but the coordinator is cached somewhat... then everything is truly handled by the coordinator to not create a scrollview+textview every time the text change :) – igerard Dec 27 '19 at 00:13
-4
You can have a multi-line TextField in SwiftUI (you just need to call .lineLimit(N) on it to become multi-line capable), but text with multiple separate styles isn't currently supported. A TextField just has a single font & style.
You can roll it yourself, though: create an NSViewRepresentable implementation that vends an NSTextView and bind it to an NSMutableAttributedText property. You'll need to handle all the text view model synchronization and bindings updates yourself, but it is certainly do-able.
marcprux
- 9,845
- 3
- 55
- 72