I’m trying to draw rectangles pattern using DrawRect like this:
Currently, I’m doing this like so:
class PatternView: UIView {
override func draw(_ rect: CGRect) {
let context = UIGraphicsGetCurrentContext()
let numberOfBoxesPerRow = 7
let boxSide: CGFloat = rect.width / CGFloat(numberOfBoxesPerRow)
var yOrigin: CGFloat = 0
var xOrigin: CGFloat = 0
var isBlack = true
for y in 0...numberOfBoxesPerRow - 1 {
yOrigin = boxSide * CGFloat(y)
for x in 0...numberOfBoxesPerRow - 1 {
xOrigin = boxSide * CGFloat(x)
let color = isBlack ? UIColor.red : UIColor.blue
isBlack = !isBlack
context?.setFillColor(color.cgColor)
let rectnagle = CGRect(origin: .init(x: xOrigin, y: yOrigin), size: .init(width: boxSide, height: boxSide))
context?.addRect(rectnagle)
context?.fill([rectnagle])
}
}
}
}
It’s working but I’m trying to optimize it.
Any help will be highly appreciated!
2
Answers
I think your first move would be to first draw a big red square, then to draw only the blue ones on top of it. It would spare half the computations, even if it does not change the order of magnitude.
EDIT
Note : it is always the drawing itself that consumes time, rarely the other computations. So that is what we have to minimize.
So, my second move would be to replace drawing squares by creating just one complicated BezierPath, that makes all the squares into just one form, and then display it only once.
I do not know if it is possible to do the whole in just one form, but it is possible to make two columns of blue squares into one form.
EDIT 2
Also, I do not understant why there are two instructions here :
Shouldn’t only the second be enough ?
It’s difficult to answer "abstract" questions… which this one is, without knowing if you’ve run some tests / profiling to determine if this code is slow.
However, a couple things you can do to speed it up…
Take a look at this modification:
There are other options… create a "pattern" background color… use
CAShapeLayer
s and/orCAReplicatorLayer
s… for example.Edit
The reason you are getting "blurry edges" is because, as you guessed, you’re drawing on partial pixels.
If we modify the values to use whole numbers (using
floor()
), we can avoid that. Note that the wholeNumberBoxSide * numBoxes may then NOT be exactly equal to the view’s rect, so we’ll also want to inset the "grid":We could also get the scale of the main screen (which will be 2x or 3x) and round the boxSide to half- or one-third points to align with the pixels… if really desired.
Edit 2
Additional modifications… settable colors and number of boxes.
Also, using this extension:
We can round the coordinates to match the screen scale.
PatternView class
Example Controller View class
Sets up two pattern views… both start at 7 boxes… each tap anywhere increments the boxes per row in the bottom view.
Here’s how it looks with 21 boxes per row (actual size – so really big image):
and zoomed-in 1600%:
Note the red borders… I set the background of the view to red, so we can see that the grid must be inset to account for the non-whole-number box size.
Edit 3
Options to avoid "blurry edges" …
Suppose we have a view width of
209
and we want10
boxes.That gives us a box width of 20.9 … which results in "blurry edges" — so we know we need to get to a whole number.
If we round it, we’ll get
21
—21 x 10 = 210
which will exceed the width of the view. So we need to round it down (floor()
).So…
Option 1:
Option 2:
Option 3: