1

I'm trying to draw a string in a UIImageView in Swift's CoreGraphics for a dynamic geometry software app. I'm using context graphics for graphing points and lines, but when I try to draw a text string (for example, the length of a line segment), I either get no text (such as in these two examples), or everything else I'm drawing is erased except the text string (sorry, I didn't save examples of that). In the latter case, I could draw other stuff on top of the string, but that isn't really helpful either, since I could only draw a single string (trying to draw a second string erased the first).

Here are two attempts, both of which result in no text string drawn:

        UIGraphicsBeginImageContext(canvas.frame.size)
        canvas.draw(CGRect(x: 0, y: 0, width: canvas.frame.width, height: canvas.frame.height))
        let context = UIGraphicsGetCurrentContext()
        let text="\(value)"
        let font=UIFont(name: "Helvetica", size: 12)!
        let text_style=NSMutableParagraphStyle()
        text_style.alignment=NSTextAlignment.center
        let text_color=UIColor.black
        let attributes=[NSAttributedString.Key.font:font, NSAttributedString.Key.paragraphStyle:text_style, NSAttributedString.Key.foregroundColor:text_color]
        let text_x=coordinates.x
        let text_y=coordinates.y
        let text_rect=CGRect(x: text_x, y: text_y, width: 100, height: font.lineHeight)
        text.draw(in: text_rect.integral, withAttributes: attributes)
        UIGraphicsEndImageContext()
    }```

```override func draw(_ canvas: UIImageView, _ isRed: Bool) {
        UIGraphicsBeginImageContext(canvas.frame.size)
        canvas.draw(CGRect(x: 0, y: 0, width: canvas.frame.width, height: canvas.frame.height))
        let renderer = UIGraphicsImageRenderer(size: CGSize(width: 512, height: 512))
        let img = renderer.image { ctx in
            let paragraphStyle = NSMutableParagraphStyle()
            paragraphStyle.alignment = .center
            let attrs = [NSAttributedString.Key.font: UIFont(name: "HelveticaNeue-Thin", size: 12)!, NSAttributedString.Key.paragraphStyle: paragraphStyle]
            let string = "\(value)"
            string.draw(with: CGRect(x: coordinates.x, y: coordinates.y, width: 100, height: 16), options: .usesLineFragmentOrigin, attributes: attrs, context: nil)
        }
    }```

I have also found a page that says something to the effect that it is impossible to draw text in a UIImageView.  However, it is hard to believe that a graphics system as mature as core graphics wouldn't allow drawing text.

1 Answers1

0

I am working on a barChart in MacOS Cocoa, using core Graphic. The program embed some usefull link for SwiftUI. You should be able to adapt my code to your program.

//  ViewController.swift
//  Chart_test
//
//  Created by slareau on 2022-10-10.
//

//https://stackoverflow.com/questions/38079917/drawing-in-cocoa-swift
//https://www.raywenderlich.com/1101-core-graphics-on-macos-tutorial
//https://stackoverflow.com/questions/10627557/mac-os-x-drawing-into-an-offscreen-nsgraphicscontext-using-cgcontextref-c-funct
//https://nshipster.com/cggeometry/
//https://www.hackingwithswift.com/example-code/core-graphics/how-to-draw-a-text-string-using-core-graphics


import Cocoa

import CoreGraphics

class ChartsVC: NSViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
 
        print("ChartsVC")
        let dr = Drawing(frame: NSRect(x: 50, y: 50, width: 550, height: 500))
        self.view.addSubview(dr)
   
    }
}
    

class Drawing: NSView {
    
    //var facts_list: [myfact] = [] //complete database
    //var fact_total: [Double] = [0.0, 0.0, 0.0, 0.0, 0.0]
    var fact_total: [Double] = [12454, 22365,18989, 14656, 16777]
    
    //var comm_list: [myComm] = []
    // var comm_total: [Double] = [0.0, 0.0, 0.0, 0.0, 0.0]
    var comm_total: [Double] = [8976, 14989, 13989, 12065, 15040]
    
    var Current_Year : Int = 0
    var Current_Month : Int = 0
    
    override func draw(_ dirtyRect: NSRect) {
        super.draw(dirtyRect)
        
        
        let todays_date = Date()
        let dateFormatter = DateFormatter()
        dateFormatter.dateFormat = "yyyy"
        Current_Year = Int(dateFormatter.string(from: todays_date))!
        
        // print ("VC60:", dateFormatter.string(from: todays_date), Current_Year!) OK
        
        
        dateFormatter.dateFormat = "MM"
        Current_Month = Int(dateFormatter.string(from: todays_date))!
        
        // Load_CommDB()
        // Load_FactDB()
        
        
        var j = 0
        while j < 5 {  // last 5 years
            print ( "VCH165:  ", Current_Year - j, fact_total[j], comm_total[j] )
            j += 1
        }
        
        
        // frame background
        myRect(x: 0, y: 0, wd: 500, ht: 400, color: NSColor.lightGray)
        
        for i in 0...4 {
            
            let factT = String(fact_total[4 - i])
            let commT = String(comm_total[4 - i])
            let diffT = String(fact_total[4 - i] - comm_total[4 - i])
            
            myRect(x: Double(50 + 80 * i) , y: 80, wd: 50, ht: Double(fact_total[4 - i]/100), color: NSColor.blue)
            myRect(x: Double(60 + 80 * i) , y: 80, wd: 50, ht: Double(comm_total[4 - i]/100), color: NSColor.orange)
            myRect(x: Double(70 + 80 * i) , y: 80, wd: 50, ht: Double((fact_total[4 - i] - comm_total[4 - i])/100), color: NSColor.green)
            mytext(txt:  factT.toCurrencyFormat(), x: Double(45 + 80 * i), y: 60)
            mytext(txt:  commT.toCurrencyFormat(), x: Double(45 + 80 * i), y: 40)
            mytext(txt:  diffT.toCurrencyFormat(), x: Double(45 + 80 * i), y: 20)
            mytext(txt:  String(Current_Year - (4 - i)), x: Double(45 + 80 * i), y: 3)
        }
        
        let context = NSGraphicsContext.current!.cgContext;
        context.beginPath()
        context.move(to: CGPoint(x: 10.0, y: 78.0))
        context.addLine(to: CGPoint(x: 420.0, y: 78.0))
        //context.setStrokeColor(red: 1, green: 0, blue: 0, alpha: 1)
        context.setStrokeColor(NSColor.black.cgColor)
        context.setLineWidth(2.0)
        context.strokePath()
        
    }
    
    func myRect( x: Double, y: Double, wd:  Double, ht :Double, color: NSColor) {
        let context = NSGraphicsContext.current!.cgContext;
        let rectangle = CGRect(x: x, y: y, width: wd, height: ht) // NSRect or CGRect work
        //context.setFillColor(NSColor.yellow.cgColor)
        context.setFillColor(color.cgColor)
        context.setStrokeColor(NSColor.black.cgColor)
        context.setLineWidth(2)
        context.addRect(rectangle)
        context.drawPath(using: .fillStroke)
        
        
    }
    
    func mytext ( txt: String,  x: Double, y: Double) {
        // let myfont = CTFontCreateWithName("Papyrus" as CFString, 12, nil)
        let myfont = CTFontCreateWithName("Arial" as CFString, 12, nil)
        let attributedString = NSAttributedString(string: txt, attributes: [NSAttributedString.Key.font: myfont])
        //  let attributedString = NSAttributedString(string: "allo le monde")
        let line = CTLineCreateWithAttributedString(attributedString)
        
        // Draw text
        if let context = NSGraphicsContext.current?.cgContext {
            context.textPosition = .init(x: x , y: y)
            
            CTLineDraw(line, context)
        }
        //https://blog.krzyzanowskim.com/2020/07/09/coretext-academy-part-1/
        //https://blog.krzyzanowskim.com/2020/07/10/coretext-swift-academy-part-2/
        //https://blog.krzyzanowskim.com/2020/07/13/coretext-swift-academy-part-3/
    }
}
S Lareau
  • 159
  • 1
  • 5