Swift 3: Drawing a Rectangle

Swift 3: Drawing a rectangle

In order to see the view, you need to create one and give it frame so that it knows how big to make it.

If you put your code in a Playground, and then add this line:

let d = Draw(frame: CGRect(x: 0, y: 0, width: 100, height: 100))

You'll be able to click on the Quick View on the right, and then you'll see the view.

Yellow square in a playground


You can also add the view as a subview of view in your ViewController and then you'll see it on the iPhone:

override func viewDidLoad() {
super.viewDidLoad()

let k = Draw(frame: CGRect(
origin: CGPoint(x: 50, y: 50),
size: CGSize(width: 100, height: 100)))

// Add the view to the view hierarchy so that it shows up on screen
self.view.addSubview(k)
}

Note that you never call draw(_:) directly. It is called for you by Cocoa Touch to display the view.

Yellow square on iPhoneSE

Swift draw rectangle

I was able to solve my problem by doing the following:

increase the width and height here:

let rectangleWidth = CGFloat(100)
let rectangleHeight = rectangleWidth

and my rectangle method

func drawRectangle()
{
// Get the Graphics Context
let context = UIGraphicsGetCurrentContext();

// Set the rectangle outerline-width
CGContextSetLineWidth(context, 5.0);

// Set the rectangle outerline-colour
UIColor.redColor().set()

// Create Rectangle
CGContextAddRect(context, CGRectMake(0, 0, 100, 100));

// Draw
CGContextStrokePath(context);

}

for swift 4.2 :

this code uses for swift 4.2

func drawRectangle()
{
// Get the Graphics Context
let context = UIGraphicsGetCurrentContext()

// Set the rectangle outerline-width
context?.setLineWidth( 5.0)

// Set the rectangle outerline-colour
UIColor.red.set()

// Create Rectangle
context?.addRect( CGRect(x: 0, y: 0, width: 100, height: 100))

// Draw
context?.strokePath()

}

How to draw a rectangle and fill it with color based on battery level

This is a starting point.

Paste the code in a Playground and click on Show Result at the end of the last line

class LevelView : UIView {

init(frame: CGRect, level: CGFloat) {
super.init(frame: frame)
self.layer.borderWidth = 1.0
let levelLayer = CAShapeLayer()
levelLayer.path = UIBezierPath(roundedRect: CGRect(x: frame.origin.x,
y: frame.origin.y,
width: frame.width * level,
height: frame.height),
cornerRadius: 0).cgPath
levelLayer.fillColor = UIColor.red.cgColor
self.layer.addSublayer(levelLayer)

}

required init?(coder aDecoder: NSCoder) {
fatalError("Required, but Will not be called in a Playground")
}
}

let view = LevelView(frame: CGRect(x: 0, y: 0, width: 64, height: 32), level: 0.75)

Drawing a rectangle on UIImageView using Core Graphics

You are replacing your image with a new image composed of the previous image plus a rectangle drawn over it. Rather than drawing the image from the image view, draw the original image.

Alternatively, you could render the the rectangle as a shape layer and just update that shape layer's path:

class ViewController: UIViewController {

@IBOutlet weak var imageView: UIImageView!

private let shapeLayer: CAShapeLayer = {
let _shapeLayer = CAShapeLayer()
_shapeLayer.fillColor = #colorLiteral(red: 0, green: 0, blue: 0, alpha: 0).cgColor
_shapeLayer.strokeColor = #colorLiteral(red: 0, green: 0, blue: 0, alpha: 1).cgColor
_shapeLayer.lineWidth = 3
return _shapeLayer
}()

private var startPoint: CGPoint!

override func viewDidLoad() {
super.viewDidLoad()

imageView.layer.addSublayer(shapeLayer)
}

override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
startPoint = touches.first?.location(in: imageView)
}

override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
guard let startPoint = startPoint, let touch = touches.first else { return }

let point: CGPoint

if let predictedTouch = event?.predictedTouches(for: touch)?.last {
point = predictedTouch.location(in: imageView)
} else {
point = touch.location(in: imageView)
}

updatePath(from: startPoint, to: point)
}

override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
guard let startPoint = startPoint, let touch = touches.first else { return }

let point = touch.location(in: imageView)

updatePath(from: startPoint, to: point)
imageView.image = imageView.snapshot(afterScreenUpdates: true)
shapeLayer.path = nil
}

override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?) {
shapeLayer.path = nil
}

private func updatePath(from startPoint: CGPoint, to point: CGPoint) {
let size = CGSize(width: point.x - startPoint.x, height: point.y - startPoint.y)
let rect = CGRect(origin: startPoint, size: size)
shapeLayer.path = UIBezierPath(rect: rect).cgPath
}

}

Where:

extension UIView {
func snapshot(afterScreenUpdates: Bool = false) -> UIImage {
UIGraphicsBeginImageContextWithOptions(bounds.size, isOpaque, 0)
drawHierarchy(in: bounds, afterScreenUpdates: afterScreenUpdates)
let image = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
return image
}
}

This is not only simpler, but more efficient, too.

That yields:

Sample Image

By the way, I might suggest using predictive touches in touchesMoved. On a device (not the simulator) that can yield a slightly more responsive UI.

Draw several rectangles inside one UIView

What you're describing is about as basic as life gets. Simply think about the notion of a grid and the xy coordinates of one piece within the grid. Now you know where to place each subview.

Here, to show you the general idea, is a simplified version of what you're doing. Imagine that we have no blank areas: just a solid grid of squares that are either red or gray. And let's imagine that this grid is 12x8.

So to start with, how would we just construct a 12x8 grid of gray squares? In your code, you are doing it with actual drawing, in draw(_:); my approach uses subviews (and sublayers is also a possibility, if you think subviews is too much overhead), but the underlying principle is exactly the same:

// assume 12x8 squares
let xmax = 12, ymax = 8
let w = self.view.bounds.width / CGFloat(xmax)
for i in 0..<xmax {
for j in 0..<ymax {
let rect = CGRect(x: w*CGFloat(i), y: w*CGFloat(j), width: w, height: w).insetBy(dx: 2, dy: 2)
let v = UIView(frame:rect)
// v.backgroundColor = withRedOnes.contains([i,j]) ? .red : .gray
v.backgroundColor = .gray
self.view.addSubview(v)
}
}

Sample Image

Do you see the key concept? i and j are the grid placement of a square. From that, we can easily calculate the rect where that square goes. That's all there is to it!

Now specifying that something different, like a red view, goes at any particular coordinate, is trivial:

func makeGrid(withRedOnes: [[Int]]) {
// assume 12x8 squares
let xmax = 12, ymax = 8
let w = self.view.bounds.width / CGFloat(xmax)
for i in 0..<xmax {
for j in 0..<ymax {
let rect = CGRect(x: w*CGFloat(i), y: w*CGFloat(j), width: w, height: w).insetBy(dx: 2, dy: 2)
let v = UIView(frame:rect)
v.backgroundColor = withRedOnes.contains([i,j]) ? .red : .gray
self.view.addSubview(v)
}
}
}

If we call that method like this:

self.makeGrid(withRedOnes:[[6,3], [7,3]])

Then we get this:

Sample Image

Okay, now. In real life, you're not doing to use a mere UIView as each square. You will have your own UIView subclass that knows how to draw itself: with a color, with a number, whataever. And you won't reconstruct all the views every time something changes; you will just change that one view within the grid, removing it or placing it, changing the way it draws itself, whatever.

As practice, I suggest you try writing a simpler grid-based app, such as a tic-tac-toe game (3x3 grid of "O" and "X"). Once you have that down, you will just tweak it successively to do all sorts of fancy things, like offsetting or centering a row of the grid and other requirements you may have in order to "draw" a theatre layout.

Drawing a rectangle in UIView

- (void)drawRect:(CGRect)rect
{
// Drawing code
CGRect rectangle = CGRectMake(0, 100, 320, 100);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetRGBFillColor(context, 1.0, 1.0, 1.0, 0.0); //this is the transparent color
CGContextSetRGBStrokeColor(context, 0.0, 0.0, 0.0, 0.5);
CGContextFillRect(context, rectangle);
CGContextStrokeRect(context, rectangle); //this will draw the border

}

the effect is like this (backgroundColor is blue)

Sample Image

How to draw a just the corners of a rectangle (without lines connecting them)

Here's a custom UIView class that draws itself with the four corners. You can set various properties to get the look you need.

class CornerRect: UIView {
var color = UIColor.black {
didSet {
setNeedsDisplay()
}
}
var radius: CGFloat = 5 {
didSet {
setNeedsDisplay()
}
}
var thickness: CGFloat = 2 {
didSet {
setNeedsDisplay()
}
}
var length: CGFloat = 30 {
didSet {
setNeedsDisplay()
}
}

override func draw(_ rect: CGRect) {
color.set()

let t2 = thickness / 2
let path = UIBezierPath()
// Top left
path.move(to: CGPoint(x: t2, y: length + radius + t2))
path.addLine(to: CGPoint(x: t2, y: radius + t2))
path.addArc(withCenter: CGPoint(x: radius + t2, y: radius + t2), radius: radius, startAngle: CGFloat.pi, endAngle: CGFloat.pi * 3 / 2, clockwise: true)
path.addLine(to: CGPoint(x: length + radius + t2, y: t2))

// Top right
path.move(to: CGPoint(x: frame.width - t2, y: length + radius + t2))
path.addLine(to: CGPoint(x: frame.width - t2, y: radius + t2))
path.addArc(withCenter: CGPoint(x: frame.width - radius - t2, y: radius + t2), radius: radius, startAngle: 0, endAngle: CGFloat.pi * 3 / 2, clockwise: false)
path.addLine(to: CGPoint(x: frame.width - length - radius - t2, y: t2))

// Bottom left
path.move(to: CGPoint(x: t2, y: frame.height - length - radius - t2))
path.addLine(to: CGPoint(x: t2, y: frame.height - radius - t2))
path.addArc(withCenter: CGPoint(x: radius + t2, y: frame.height - radius - t2), radius: radius, startAngle: CGFloat.pi, endAngle: CGFloat.pi / 2, clockwise: false)
path.addLine(to: CGPoint(x: length + radius + t2, y: frame.height - t2))

// Bottom right
path.move(to: CGPoint(x: frame.width - t2, y: frame.height - length - radius - t2))
path.addLine(to: CGPoint(x: frame.width - t2, y: frame.height - radius - t2))
path.addArc(withCenter: CGPoint(x: frame.width - radius - t2, y: frame.height - radius - t2), radius: radius, startAngle: 0, endAngle: CGFloat.pi / 2, clockwise: true)
path.addLine(to: CGPoint(x: frame.width - length - radius - t2, y: frame.height - t2))

path.lineWidth = thickness
path.stroke()
}
}

Sample usage:

let cr = CornerRect(frame: CGRect(x: 0, y: 0, width: 300, height: 500))
cr.color = .yellow
cr.thickness = 5
cr.backgroundColor = .white

Copy and paste that into a playground. Try different values for the properties.

Drawing rectangles using the CGPoint coordinate system - SWIFT

The points returned by the Vision framework are coordinates represented by a percentage from the full size of the viewport. If your viewport is 640 x 480 (for example) then your first point is CGPoint(x: 0.154599294066429 * 640, y: 0.904223263263702 * 480). Taking that into account change your code to something like this and it should be fine:

let rectangle = UIBezierPath.init()
let width = // your width - Maybe UIScreen.main.bounds.size.width ?
let height = // your height - Maybe UIScreen.main.bounds.size.height ?

rectangle.move(to: CGPoint.init(x: width * 0.154599294066429, y: height * 0.904223263263702))

rectangle.addLine(to: CGPoint.init(x: width * 0.8810795545578, y: height * 0.970198452472687))
rectangle.addLine(to: CGPoint.init(x: width * 0.16680309176445, y: height * 0.0157230049371719))
rectangle.addLine(to: CGPoint.init(x: width * 0.878569722175598, y: height * 0.128135353326797))

rectangle.close()

let rec = CAShapeLayer.init()
rec.path = rectangle.cgPath
rec.fillColor = UIColor.red.cgColor
self.view.layer.addSublayer(rec)


Related Topics



Leave a reply



Submit