Search code examples
iosswiftswift3core-graphicscgcontext

Overriding drawRect not rendering


I'm overriding drawRect and I see my breakpoints getting hit. I'm expecting a series of red squares to be shown, but I only see the background color.

class GameViewController: UIViewController {
    var board: Board!

    override func viewDidLoad() {
        super.viewDidLoad()

        let w = UIScreen.main.bounds.width
        board = Board(frame:  CGRect(x: 0, y: view.frame.size.height - w - 45 , width: w, height: w))
        view.addSubview(board)
    }

}




import UIKit

class Board: UIView {
    var rows = [[Tile]]()
    var w: CGFloat!
    var squareSize: CGFloat!
    var tilesize: CGFloat!
    var sepsize: CGFloat!
    var halfsep: CGFloat!

    override init(frame: CGRect) {
        super.init(frame: frame)

        backgroundColor = .clear
            //UIColor(colorLiteralRed: 76/255, green: 89/255, blue: 89/255, alpha: 1)
        makeRows()

        configDrawing()
        setNeedsDisplay()
    }

    func configDrawing() {
        w = frame.size.width
        squareSize = w / 7
        tilesize = squareSize * 0.96
        sepsize = squareSize * 0.04
        halfsep = squareSize * 0.5
    }

    override func layoutSubviews() {
        super.layoutSubviews()
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
    }

    func makeRows() {
        for i in 0...6 {
            var row = [Tile]()
            for j in 1...7 {
                let tile = Tile(j, i)
                row.append(tile)
            }
            rows.append(row)
        }
    }

    func update(_ newRows: [[Tile]]) {
        rows = newRows
        //subview?
    }

    override func draw(_ rect: CGRect) {
        //render dividers
        let context = UIGraphicsGetCurrentContext();

        for i in 0..<rows.count {
            let row = rows[i]
            for j in 0..<row.count {
                let tile = rows[i][j]
                context?.beginPath()
                let x = CGFloat(j) //CGFloat(tile.x)
                let y = CGFloat(i) //CGFloat(tile.y)

                context?.setStrokeColor(red: 1, green: 0, blue: 0, alpha: 1)

                let ul = CGPoint(x: (x * squareSize) + halfsep, y: (y * squareSize) + halfsep)
                let ur = CGPoint(x: ((x + 1) * squareSize) - halfsep, y: (y * squareSize) + halfsep)
                let br = CGPoint(x: ((x + 1) * squareSize) - halfsep, y: ((y + 1) * squareSize) - halfsep)
                let bl = CGPoint(x: ((x * squareSize) + halfsep), y: ((y + 1) * squareSize) - halfsep)
                let corners = [ul, ur, br, bl]
                context?.addLines(between: corners)
                context?.strokePath()

            }
        }
//        CGPoint corners[4];

        //render tiles
    }
}

The squares should be slightly smaller than the full space, so I should see a red square of 96% of the size with the 4% filled by lines of the background as a grid.

I see nothing, why?


Solution

  • Everywhere you used halfsep, you meant to use sepsize. As you've written it, the entire rectangle winds up being zero-size (it's inset from all sides by half its size; 1-(1/2+1/2) = 0).

    This is the code you intended:

    let ul = CGPoint(x: (x * squareSize) + sepsize, y: (y * squareSize) + sepsize)
    let ur = CGPoint(x: ((x + 1) * squareSize) - sepsize, y: (y * squareSize) + sepsize)
    let br = CGPoint(x: ((x + 1) * squareSize) - sepsize, y: ((y + 1) * squareSize) - sepsize)
    let bl = CGPoint(x: ((x * squareSize) + sepsize), y: ((y + 1) * squareSize) - sepsize)
    

    You also need to close the boxes:

    let corners = [ul, ur, br, bl, ul]
    

    Note the final (repeated) ul.

    This can be simplified a lot, however. There is a built-in way to draw rectangles, and there is a built-in way to "shrink" (inset) rectangles. You also can build the entire path and then stroke it one time rather than restarting the path.

    override func draw(_ rect: CGRect) {
        //render dividers
        let context = UIGraphicsGetCurrentContext();
    
        let size = CGSize(width: squareSize, height: squareSize)
        context?.setStrokeColor(red: 1, green: 0, blue: 0, alpha: 1)
        context?.beginPath()
        for i in 0..<rows.count {
            let row = rows[i]
            for j in 0..<row.count {
    //          let tile = rows[i][j] 
                let x = CGFloat(j) //CGFloat(tile.x)
                let y = CGFloat(i) //CGFloat(tile.y)
    
                let rect = CGRect(origin: CGPoint(x: x * squareSize, y: y * squareSize),
                                  size: size)
                    .insetBy(dx: sepsize, dy: sepsize)
                context?.addRect(rect)
            }
        }
        context?.strokePath()
        //render tiles
    }