Drawing Text on Right Side of NSImage - Unwanted Spacing

柔情痞子 提交于 2020-05-17 05:58:24

问题


I'm using the following code to draw text on the right side on an image

         im.lockFocus()
         if let context = NSGraphicsContext.current?.cgContext {
         context.saveGState()
         let size = text.size(withAttributes: textFontAttributes)
         context.translateBy(x: imageRect.width-size.height, y: (imageRect.height - size.width) / 2)
         context.rotate(by: CGFloat.pi / 2)
         text.draw(at: .zero, withAttributes: textFontAttributes)
         context.restoreGState()
         im.unlockFocus()

As you can see there is some excessive spacing from the right side.How can i fix it?

Update:

class DemoView: NSView {
    override func draw(_ rect: NSRect) {

        // .. your drawing code here

        NSColor.red.set()  // just for demo
        rect.fill()        // just for demo

        if let context = NSGraphicsContext.current?.cgContext {
            context.saveGState()

            let text: NSString = "Hello World"
            let attributes = [
                NSAttributedString.Key.foregroundColor: NSColor.white, 
                NSAttributedString.Key.font: NSFont.systemFont(ofSize: 24)
            ]

            let size = text.size(withAttributes: attributes)
            context.translateBy(x: imageRect.width-size.height, y: (rect.height - size.width) / 2)
            context.rotate(by: CGFloat.pi / 2)

            text.draw(at: .zero, withAttributes: attributes)

            context.restoreGState()
        }
    }
}

Update: This is the desired result


回答1:


Use the following translation

context.translateBy(x: self.bounds.maxX, y: (rect.height - size.width) / 2)



回答2:


I hope you understand how CGContext transforms work, if not, learn it.

One of the simplest implementations of your problem is like this: the text is blue rectangle, you want to draw a red rectangle.

So it's enough to calculate a shift transform, then rotate-90. A shift transform in this case is a left-bottom of the blue rectangle, and at the same time the right-bottom of the red rectangle.

Your DemoView can look like this:

class DemoView: NSView {
    override func draw(_ rect: NSRect) {

        let image = NSImage(named: "test_image.jpg")!

        if let context = NSGraphicsContext.current?.cgContext {
            context.saveGState()

            NSColor.red.set()  // just for demo
            rect.fill()        // just for demo

            image.draw(in: rect)

            let text: NSString = "Hello World"
            let attributes = [
                NSAttributedString.Key.foregroundColor: NSColor.white,
                NSAttributedString.Key.font: NSFont.systemFont(ofSize: 24)
            ]

            let size = bounds.size
            let rotatedTextCenter = CGPoint(x: size.width*0.8, y: size.height/2)

            // draw the the circle in desired center to check if text was transformed correctly
            drawCircleInView(context: context, center: rotatedTextCenter)

            // draw the rotated text so its center fits the circle center
            drawAttributedTextInView(text: text, textAttributes: attributes, context: context, viewSize: size, centerInView: rotatedTextCenter)

            context.restoreGState()
        }
    }

    func drawCircleInView(context: CGContext, center: CGPoint) {
        context.setLineWidth(5)
        context.setFillColor(NSColor.red.cgColor)
        context.addArc(center: center, radius: 10, startAngle: 0, endAngle: CGFloat.pi*2, clockwise: false)
        context.fillPath()
    }

    func drawAttributedTextInView(text: NSString,
                                  textAttributes: [NSAttributedString.Key:Any],
                                  context: CGContext,
                                  viewSize: CGSize,
                                  centerInView textCenter: CGPoint) {

        // we assume everything else was already drawn and context has identity transform for the simplicity

        let textSize = text.size(withAttributes: textAttributes)

        context.translateBy(x: textCenter.x+textSize.height/2, y: textCenter.y-textSize.width/2)
        context.rotate(by: CGFloat.pi / 2)

        text.draw(at: .zero, withAttributes: textAttributes)
    }
}

In this code, the centerInView param is the center of your red rectangle.

The result looks like this:

Please note that we fill the entire view with red color, then we render the image, then we render the text. No excessive spacing is present. If it was present, we would see red lines below the image.

You can download the demo here.

And please don't draw everything in a one draw(_ rect: NSRect) call - try to extract at least the text drawing func - it'll give you much more flexibility in defining the variables of the problem.

UPDATE:

I you need the text to be "flipped", so its bottom side points toward the center, the transform can look like this:

context.translateBy(x: textCenter.x-textSize.height/2, y: textCenter.y+textSize.width/2)
context.rotate(by: -CGFloat.pi / 2)

and the text will look like this:



来源:https://stackoverflow.com/questions/61676256/drawing-text-on-right-side-of-nsimage-unwanted-spacing

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!