Draw rectangle with clear background on top of imageView - ios

I am trying to draw a rectangle with a random border color with a clear background on top of an imageView. I need to loop through a set of co-ordinates and add appropriate rectangles to my image. I can't manage to get the rectangle background colour to be clear. Code below:
// the draw class
class Draw: UIView {
override init(frame: CGRect) {
super.init(frame: frame)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func draw(_ rect: CGRect) {
let h = rect.height
let w = rect.width
let color:UIColor = Palette.getRandomColour()
//var drect = CGRect(x: (w * 0.25),y: (h * 0.25),width: (w * 0.5),height: (h * 0.5))
let drect = CGRect(x: rect.minX ,y: rect.minY , width: w, height: h)
let bpath:UIBezierPath = UIBezierPath(rect: drect)
UIColor.clear.setFill()
color.set()
bpath.stroke()
print("it ran")
NSLog("drawRect has updated the view")
}
}
// and in my viewDidLoad
for bp in sbp {
if let master = Bodyparts.getID(bp.masterid) {
let width = master.x2 - master.x1
let height = master.y2 - master.y1
let k = Draw(frame: CGRect(origin: CGPoint(x: master.x1, y: master.y1), size: CGSize(width: width, height: height)))
self.imgView.addSubview(k)
}
}

You need to set the background to clear. Here is a playground that you can copy and paste:
import PlaygroundSupport
import UIKit
class Draw: UIView {
override init(frame: CGRect) {
super.init(frame: frame)
backgroundColor = .clear
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func draw(_ rect: CGRect) {
let h = rect.height
let w = rect.width
let color:UIColor = .red
let drect = CGRect(x: rect.minX ,y: rect.minY , width: w, height: h)
let bpath:UIBezierPath = UIBezierPath(rect: drect)
UIColor.clear.setFill()
color.set()
bpath.stroke()
}
}
let d = Draw(frame: CGRect(x: 0, y: 0, width: 100, height: 100))
let view = UIView(frame: CGRect(x: 0, y: 0, width: 200, height: 200))
view.backgroundColor = .green
view.addSubview(d)
PlaygroundPage.current.liveView = view

I'm not exactly sure if mean a transparent center of the rect by clear but you can use the following method.
You would need to set the style of the Paint to stroke, to draw a box instead of a filled rect. Basically you will draw a bounding box if you use the Paint.Style.Stroke option of your Paint.
// Give the canvas an image to draw on
val canvas: Canvas = bitmap?.let { Canvas(it) }!!
// Test points for rect
val left = 100f
val right = 300f
val top = 100f
val bottom = 500f
// Paint definition for bounding box
var borderPaint: Paint = Paint()
borderPaint.strokeWidth = 10f
borderPaint.color = Color.GREEN
borderPaint.style = Paint.Style.STROKE
// Draw border
canvas.drawRect(left, top, right, bottom+100, borderPaint)
// Show the bitmap on display
viewBinding.imageView.setImageBitmap(bitmap)

Related

How can I set image for UIBezierPath

I want to set image inside this path (i.e) I want imageview instead of red colour
enter image description here
Here is my code
import UIKit
#IBDesignable
class diagonalviewprofile: UIView {
#IBInspectable var color : UIColor? = UIColor.red {
didSet {
// self.layer.backgroundColor = self.color?.cgColor
}
}
override init(frame: CGRect) {
super.init(frame: frame)
backgroundColor = UIColor.clear
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
backgroundColor = UIColor.clear
}
// Only override draw() if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
override func draw(_ rect: CGRect) {
// Drawing code
//get the size of the view
let size = self.bounds.size
//get 4 points for the shape layer
let p1 = self.bounds.origin
let p2 = CGPoint(x: p1.x + size.width, y: p1.y)
let p3 = CGPoint(x: p2.x, y: size.height - 150)
let p4 = CGPoint(x: p1.x, y: size.height - 30)
//create the path
let path = UIBezierPath()
path.move(to: p1)
path.addLine(to: p2)
path.addLine(to: p3)
path.addLine(to: p4)
path.close()
(color ?? UIColor.red).set()
path.fill()
}
}
I want to set image inside this path (i.e) I want imageview instead of red colour
You can set image like:
// get an image of the graphics context
let image: UIImage = UIGraphicsGetImageFromCurrentImageContext()!

Weird gradient when using CAGradientLayer with radial gradient.

Using code:
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let v = View()
view.addSubview(v)
v.frame = CGRect(x: 100, y: 100, width: 200, height: 200)
}
}
class View : UIView {
let gradientLayer = CAGradientLayer()
override init(frame: CGRect) {
super.init(frame: frame)
self.backgroundColor = .blue
gradientLayer.frame = CGRect(x: 0, y: 0, width: 200, height: 200)
gradientLayer.startPoint = CGPoint(x: 0, y: 0.5)
gradientLayer.endPoint = CGPoint(x: 1, y: 0.5)
gradientLayer.type = .radial
gradientLayer.colors = [
UIColor.red.cgColor,
UIColor.green.cgColor
]
self.layer.addSublayer(gradientLayer)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
}
expect(generated by Sketch):
code result on a iOS 12 simulator:
code result on a real iOS device:
My Xcode version is 10.0 (10A255), I found the problem occurs only when
startPoint.x == endPoint.x || startPoint.y == endPoint.y
When the y of startPoint and endPoint are equal, that means the height of the gradient ellipse will be 0.
In your code snippet, you can set entPoint's y to 1 to achieve the Sketch effect.
In the QuartzCore framework, there are the following comments:
/* Radial gradient. The gradient is defined as an ellipse with its
* center at 'startPoint' and its width and height defined by
* '(endPoint.x - startPoint.x) * 2' and '(endPoint.y - startPoint.y) *
* 2' respectively. */
#available(iOS 3.2, *)
public static let radial: CAGradientLayerType

Transform UIView shape

I want to transform my UIView's shape into this (the white UIView - look at the right edge) using Swift:
Any ideas how to do it? I guess I should use the transform functionality, but I don't know how exactly. Thanks in advance!
Update:
Here is the update, I misunderstood your question in my first answer
You need to subclass UIView and override the default draw method with your own behaviour. To Adjust the angle you can change the offsetFactor
class ShapeView : UIView {
public var bgc = UIColor.clear
override init(frame: CGRect) {
super.init(frame: frame)
//backup old background color
self.bgc = backgroundColor!
backgroundColor = UIColor.clear
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.bgc = backgroundColor!
backgroundColor = UIColor.clear
}
override func draw(_ rect: CGRect) {
let size = self.bounds.size
let offsetFactor = size.width * 0.98
//define the points
let tl = self.bounds.origin //top left
let tr = CGPoint(x:tl.x + size.width, y:tl.y) //top right
let br = CGPoint(x:tl.x + offsetFactor, y:tr.y + size.height) // bottom right
let bl = CGPoint(x:tl.x, y:tr.y + size.height) //bottom left
let path = UIBezierPath()
path.move(to: tl)
path.addLine(to: tr)
path.addLine(to: br)
path.addLine(to: bl)
path.close()
//set old background color
bgc.set()
path.fill()
}
}
Image
I hope I did understand your question right.
You need to subclass UIView and override the default draw method with your own behaviour. To Adjust the spire you can change the offsetFactor
This should do the job
class ShapeView : UIView {
public var bgc = UIColor.clear
override init(frame: CGRect) {
super.init(frame: frame)
//backup old background color
self.bgc = backgroundColor!
backgroundColor = UIColor.clear
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.bgc = backgroundColor!
backgroundColor = UIColor.clear
}
override func draw(_ rect: CGRect) {
let size = self.bounds.size
let offsetFactor = size.height * 0.7
//define the points
let tl = self.bounds.origin //top left
let tr = CGPoint(x:tl.x + size.width, y:tl.y) //top right
let br = CGPoint(x:tr.x, y:tr.y + offsetFactor) // bottom right
let bm = CGPoint(x:size.width/2, y:size.height) ////bottom middle
let bl = CGPoint(x:tl.x, y:offsetFactor) //bottom left
let path = UIBezierPath()
path.move(to: tl)
path.addLine(to: tr)
path.addLine(to: br)
path.addLine(to: bm)
path.addLine(to: bl)
path.close()
//set old background color
bgc.set()
path.fill()
}
}
Storyboard and Simulator
Dudes, I found an easier solution that works perfectly as an extension to UIView:
extension UIView {
func makeItEdgy() {
let bounds = self.bounds
// Create path to mask the view
let path = CGMutablePath()
path.move(to: bounds.origin)
path.addLine(to: CGPoint(x: bounds.maxX, y: bounds.minY))
path.addLine(to: CGPoint(x: bounds.maxX - (bounds.height / 5), y: bounds.maxY))
path.addLine(to: CGPoint(x: bounds.minX, y: bounds.maxY))
path.closeSubpath()
let maskLayer = (self.layer.mask as? CAShapeLayer) ?? CAShapeLayer()
maskLayer.path = path
self.layer.mask = maskLayer
}
}

How to center a UIBezierPath oval within a view of unknown width and height in Swift?

I am using the draw function in a custom UIView to create a custom texture. The issue is that the width and height are determined by a UIStackView. When I create the view I use CustomView(frame: CGRect(x: 0, y: 0, width: 50, height: 50, color: color) If the actual width and height are unknown at the time of the custom UIView uses the draw function. Is there a way to guarantee a UIBezierPath is centered within the bounds given by the UIStackView?
import Foundation
import UIKit
class CustomView: UIView {
let color: UIColor
required init(coder aDecoder: NSCoder) {
color = UIColor()
super.init(coder: aDecoder)!
}
init(frame: CGRect, color: UIColor) {
self.color = color
super.init(frame: frame)
self.backgroundColor = UIColor.init(hexString: CustomColors.pale_blue)
}
override func draw(_ rect: CGRect) {
color.setFill()
color.setStroke()
let width = Int(rect.size.width)
let height = Int(rect.size.height)
let yValue = Int(rect.origin.y)
let xValue = Int(rect.origin.x)
let rectBottom = UIBezierPath(rect: CGRect(x: xValue , y: yValue + height - (height/4), width: width, height: height/4))
let ovalTop = UIBezierPath(ovalIn: CGRect(x: xValue + (width/4), y:yValue + (height/3), width: width/2, height: height/2))
rectBottom.stroke()
rectBottom.fill()
ovalTop.fill()
ovalTop.stroke()
}
}
At the time that your draw method is called, layout has already happened. Just look at self.bounds inside draw().
To be clear, don't look at the rect passed to draw to determine your geometry. You would only use that rect if you wanted to only redraw what bits needed updating.

iOS Swift Rectangle

I am trying to add a rectangular shape (curve as shown in picture) to my existing UIView.
.
This is the code I have implemented:
func drawRect(rect: CGRect) {
let y:CGFloat = 20
let curveTo:CGFloat = 0
let myBezier = UIBezierPath()
myBezier.move(to: CGPoint(x: 0, y: y))
myBezier.addQuadCurve(to: CGPoint(x: rect.width, y: y), controlPoint: CGPoint(x: rect.width / 2, y: curveTo))
myBezier.addLine(to: CGPoint(x: rect.width, y: rect.height))
myBezier.addLine(to: CGPoint(x: 0, y: rect.height))
myBezier.close()
let context = UIGraphicsGetCurrentContext()
context!.setLineWidth(4.0)
UIColor.yellow.setFill()
myBezier.fill()
}
Just to try and get a rectangle path to appear, however, when I open it in the simulator there is nothing. I believe it is because my other elements are overlayed on top of it is this correct? My full code is here
Thanks for your help.
Looking at your code. The UIViewController don't have overridden message drawRect. You should create custom class derived from UIView and override message drawRect there.
This could be made much more flexible, but it might suit your needs. If it doesn't, it could be a good starting point for you to get to what you want.
If you haven't looked at custom components / subclassing UIView / using IBInspectable and IBDesignable, this probably won't make much sense, so you might have some reading to do :)
//
// RoundedBottomImageView.swift
// SW3IBDesign
//
// Created by Don Mag on 2/27/17.
// Copyright © 2017 DonMag. All rights reserved.
//
import UIKit
#IBDesignable
class RoundedBottomImageView: UIView {
var imageView: UIImageView!
#IBInspectable var image: UIImage? {
didSet { self.imageView.image = image }
}
#IBInspectable var roundingValue: CGFloat = 0.0 {
didSet {
self.setNeedsLayout()
}
}
override init(frame: CGRect) {
super.init(frame: frame)
doMyInit()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
doMyInit()
}
func doMyInit() {
imageView = UIImageView()
imageView.backgroundColor = UIColor.red
imageView.contentMode = UIViewContentMode.scaleToFill
addSubview(imageView)
}
override func layoutSubviews() {
super.layoutSubviews()
imageView.frame = self.bounds
let rect = self.bounds
let y:CGFloat = rect.size.height - roundingValue
let curveTo:CGFloat = rect.size.height
let myBezier = UIBezierPath()
myBezier.move(to: CGPoint(x: 0, y: y))
myBezier.addQuadCurve(to: CGPoint(x: rect.width, y: y), controlPoint: CGPoint(x: rect.width / 2, y: curveTo))
myBezier.addLine(to: CGPoint(x: rect.width, y: 0))
myBezier.addLine(to: CGPoint(x: 0, y: 0))
myBezier.close()
let maskForPath = CAShapeLayer()
maskForPath.path = myBezier.cgPath
layer.mask = maskForPath
}
}
Example app: https://github.com/DonMag/IBDesignInspect
Edit: Example now includes buttons to demonstrate changing the image via code.
Edit2: Example app now has an Alternate Storyboard to show how the image can be "path clipped" without using a custom subclass.

Resources