CGContext always creating a black rect.


Alex Zavatone
 

Hi.  I’m trying to draw a bezier shape in CGContext on a UIView in Swift with a transparent background and the background is always black.  Nothing online helps.

Any ideas?  I’ve checked .isOpaque, backgroundColor.   Nothing I can do to get rid of the black rect background.  Any ideas?  

Thanks in advance.


    override func draw(_ rect: CGRect)
    {
        self.backgroundColor = .clear
        self.isOpaque = false
        super.draw(rect)
        roundRect(rect)
    }

    

    internal func roundRect(_ rect: CGRect)
    {
        self.backgroundColor = UIColor.clear

        

        // Make a pill shape if rectCornerRadius < 0
        let xHalf = self.frame.width / 2
        let yHalf = self.frame.height / 2

        

        if (self.rectCornerRadius < 0) {
            if (self.rectWidth <= self.rectHeight) {
            self.rectCornerRadius = (yHalf / 2)
            } else if (self.rectHeight <= self.rectWidth) {
                self.rectCornerRadius = (xHalf / 2)
            }
        }

        

        let ctx: CGContext = UIGraphicsGetCurrentContext()!

        ctx.clear(rect)
        ctx.saveGState()

        

        ctx.setLineWidth(rectBorderWidth)
        ctx.setStrokeColor(rectBorderColor.cgColor)

        

        let rect = CGRect(x: 0, y: 0, width: self.rectWidth, height: self.rectHeight)

        let clipPath: CGPath = UIBezierPath(roundedRect: rect, cornerRadius: self.rectCornerRadius).cgPath
        let linePath: CGPath = UIBezierPath(roundedRect: rect, cornerRadius: self.rectCornerRadius).cgPath

        

        ctx.addPath(clipPath)

        

        ctx.setFillColor(self.rectBgColor.cgColor)
        ctx.closePath()
        ctx.fillPath()

        

        ctx.addPath(linePath)
        ctx.strokePath()

        

        ctx.restoreGState()
    }


Ben Kennedy
 

On 24 Jul 2021, at 11:37 am, Alex Zavatone via groups.io <zav=mac.com@groups.io> wrote:

Hi. I’m trying to draw a bezier shape in CGContext on a UIView in Swift with a transparent background and the background is always black. Nothing online helps.

Any ideas? I’ve checked .isOpaque, backgroundColor. Nothing I can do to get rid of the black rect background. Any ideas?
Move your `backgroundColor` and `isOpaque` calls to the init method(s). I set up a test project with your code, and that solves the problem.

I was going to make that suggestion before I even tested it, though, because it's an obvious smell to me: there's no need to repeatedly set those properties every time you draw, but rather just once, at setup.

A couple of other code style comments:

- You don't need all the `self.` prefixes.

- The `roundRect(…)` function would be better named as `drawRoundRect(…)`.

- That same function seems like it ought to be pure; i.e., have no side effects -- just draw a rect based on its argument. However, it does math and sets instance variables. If it needs to affect and act on persistent state, it ought to be refactored.

-ben


Alex Zavatone
 

Thanks.

I know I don’t need the self, but I want the context.  A variable just sitting around tells me nothing about the context in which it exists.  I want to see the context and want to see the self.

- The `roundRect(…)` function would be better named as `drawRoundRect(…)`.

Good point.

On Jul 24, 2021, at 2:32 PM, Ben Kennedy <ben-groups@...> wrote:

On 24 Jul 2021, at 11:37 am, Alex Zavatone via groups.io <zav@...> wrote:

Hi.  I’m trying to draw a bezier shape in CGContext on a UIView in Swift with a transparent background and the background is always black.  Nothing online helps.

Any ideas?  I’ve checked .isOpaque, backgroundColor.   Nothing I can do to get rid of the black rect background.  Any ideas?

Move your `backgroundColor` and `isOpaque` calls to the init method(s). I set up a test project with your code, and that solves the problem.

I was going to make that suggestion before I even tested it, though, because it's an obvious smell to me: there's no need to repeatedly set those properties every time you draw, but rather just once, at setup.

A couple of other code style comments:

- You don't need all the `self.` prefixes.

Want them.  They indicate scope.  I don’t need to think.  I just look at it and know the scope.  Anything that makes code more vague sucks.  I want easier understanding rather than “but we can use less words!”  People’s time is $$.

- The `roundRect(…)` function would be better named as `drawRoundRect(…)`.

- That same function seems like it ought to be pure; i.e., have no side effects -- just draw a rect based on its argument. However, it does math and sets instance variables. If it needs to affect and act on persistent state, it ought to be refactored.

Refactored how?

It can’t exist without its configuration being set and it will be drawing with those internal settings once I have the details set up.  Then it’s possible that I’ll move the configuration internal.  Eventually, it will have a gradient interior.  It’s replacing a UISwitch.

Here’s how I currently call it.

        self.roundedRect = RoundedRectUIView()
        self.roundedRect.backgroundColor = .clear
        self.roundedRect.clipsToBounds = true
        let rectBorderWidth = CGFloat(2)
        let rectWidth = CGFloat(100)
        let rectHeight = CGFloat(40)
        let rectBorderColor = UIColor.blue
        let rectBgColor = UIColor.systemGray3
        let rectCornerRadius = CGFloat(-1)
        let origin = CGPoint(x: 10, y: 200)
        viewRect = CGRect(origin: origin,
                          size: CGSize(width: rectWidth + rectBorderWidth * 2.0,
                                       height: rectHeight + (rectBorderWidth * 2.0)))
        self.roundedRect.frame = viewRect

                          

        self.roundedRect.configure(rectWidth: rectWidth, rectHeight: rectHeight, rectBgColor: rectBgColor , rectBorderColor: rectBorderColor, rectBorderWidth: rectBorderWidth, rectCornerRadius: rectCornerRadius)

        self.view.addSubview(roundedRect)

In Swift, I HATE how they have done method parameters, so I’m opting for redundant redundancy over terseness or brevity.

Thanks, Ben.
-ben








Ben Kennedy
 

On 24 Jul 2021, at 12:56 pm, Alex Zavatone via groups.io <zav=mac.com@groups.io> wrote:

I know I don’t need the self, but I want the context. A variable just sitting around tells me nothing about the context in which it exists. I want to see the context and want to see the self.
I initially felt that way too, several years ago, when first coming from Obj-C. However, I learned to embrace the tools (which make it easy to find the declaration) and also be mindful of writing clear and well-organized code to keep the mental burden of symbol bookkeeping to a minimum.

Moreover, following conventional style (which, in Swift, eschews the redundant `self.`) makes it easier to work with other developers.

- That same function seems like it ought to be pure; i.e., have no side effects -- just draw a rect based on its argument. However, it does math and sets instance variables. If it needs to affect and act on persistent state, it ought to be refactored.
Refactored how? [...]

Here’s how I currently call it. [...]

self.roundedRect.configure(rectWidth: rectWidth, rectHeight: rectHeight, rectBgColor: rectBgColor , rectBorderColor: rectBorderColor, rectBorderWidth: rectBorderWidth, rectCornerRadius: rectCornerRadius)

self.view.addSubview(roundedRect)
Seems like your `configure(…)` method is the ideal place for those calculations.

In Swift, I HATE how they have done method parameters, so I’m opting for redundant redundancy over terseness or brevity.
I'm not sure what you're referring to about "how they have done method parameters".

-ben