I have a custom UITextView
as an UIViewRepresentable
in my SwiftUI app. Using TextKit2, I determine regions where I do drawing.
Everything works fine, except when I rotate the device (Simulator or Preview), the drawing is not updated.
Before rotation:
After rotation:
What I did find, is that when I tap the orientation twice, the redraw does occur:
What am I missing?
Here is an MRE:
import SwiftUI
struct ContentView: View {
var body: some View {
TextViewRepresentable()
}
}
#Preview {
ContentView()
}
struct TextViewRepresentable: UIViewRepresentable {
let alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
func makeUIView(context: Context) -> UITextView {
let textView = UITextView()
let tc = NSTextContainer(size: textView.textContainer.size)
tc.heightTracksTextView = true
tc.widthTracksTextView = true
let lm = NSTextLayoutManager()
lm.textContainer = tc
let ts = NSTextContentStorage()
ts.addTextLayoutManager(lm)
let myTextView = MyTextView(frame: textView.frame, textContainer: tc)
myTextView.font = UIFont.monospacedSystemFont(ofSize: 44, weight: .regular)
return myTextView
}
func updateUIView(_ uiView: UITextView, context: Context) {
uiView.text = alphabet + alphabet
}
}
class MyTextView: UITextView {
override func draw(_ rect: CGRect) {
super.draw(rect)
debugPrint("draw")
guard let context = UIGraphicsGetCurrentContext() else { return }
context.setFillColor(red: 1.0, green: 0.0, blue: 0.0, alpha: 1.0)
for rect in rects(for: NSMakeRange(3, 12)) {
context.fill(rect)
}
}
func rects(for range: NSRange) -> [CGRect] {
var rects: [CGRect] = []
if let textLayoutManager = textLayoutManager, let contentManager = textLayoutManager.textContentManager {
guard let start = contentManager.location(contentManager.documentRange.location, offsetBy: range.location),
let end = contentManager.location(start, offsetBy: range.length),
let textRange = NSTextRange(location: start, end: end)
else {
return []
}
textLayoutManager.enumerateTextSegments(in: textRange, type: .standard, options: .rangeNotRequired) { _, rect, _, _ in
var adjustedRect = rect
adjustedRect.size.height -= CGFloat(4.0)
adjustedRect = CGRectOffset(rect, 0, CGFloat(8.0))
rects.append(adjustedRect)
return true
}
}
return rects
}
}
It seems like you just want draw
to be called again whenever the frame of the MyTextView
changes. You can just set its contentMode
to .redraw
:
myTextView.contentMode = .redraw