Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

Force refresh of UIViewRepresentable on orientation change

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:

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

enter image description here

After rotation:

enter image description here

What I did find, is that when I tap the orientation twice, the redraw does occur:

enter image description here

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

>Solution :

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
Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading