Sagaya Abdul
Sagaya Abdul

Reputation: 161

How to make transparent holes in a UIView

What is the best way to create round circles around the edges of a UIView also the circles has to be transparent i create this with sketch

enter image description here

Upvotes: 2

Views: 3574

Answers (3)

avsmirnov567
avsmirnov567

Reputation: 186

I have a subclass of UIView for such tasks. Basically, it uses CAShapeLayer, created with UIBezierPath, as a mask for view. You can customize frames and corner radius of holes. Here is my code:

import UIKit

struct TransparentPart {
    var rect: CGRect
    var cornerRadius: CGFloat
}


class PartiallyTransparentView: UIView {
    var transparentParts: [TransparentPart] = []

    override func draw(_ rect: CGRect) {
        super.draw(rect)

        backgroundColor?.setFill()
        UIRectFill(rect)

        let clipPath = UIBezierPath(rect: self.bounds)

        for part in transparentParts {
            let holeRect = part.rect
            let radius = part.cornerRadius

            let path = UIBezierPath(roundedRect: holeRect, cornerRadius: radius)

            clipPath.append(path)
        }

        let layer = CAShapeLayer()
        layer.path = clipPath.cgPath
        layer.fillRule = kCAFillRuleEvenOdd
        self.layer.mask = layer
        self.layer.masksToBounds = true
    }
}

Upvotes: 0

Shubham
Shubham

Reputation: 61

I have achieved this thing by using UIBezierPath and CAShapeLayer

I am taking outlet of view from storyboard.

@IBOutlet weak var myView: UIView!

Create an object of UIBezierPath()

var path = UIBezierPath()

Create a method which take center point of circle as parameter and we create another UIBezierPath() as circlePath which is circle and we append the circle on previous UIBezierPath() path.
Know take a CAShapeLayer and cut the circlePath

func overLay(points: CGPoint) {
    let sizes = CGSize(width: 30, height: 30)
    let circlePath = UIBezierPath(ovalIn: CGRect(origin: points, size: sizes))
    path.append(circlePath)

    let maskLayer = CAShapeLayer() //create the mask layer
    maskLayer.path = path.cgPath // Give the mask layer the path you just draw
    maskLayer.fillRule = kCAFillRuleEvenOdd // Cut out the intersection part
    myView.layer.mask = maskLayer
}

Create updateUI() and call overLay methods with all points.

func updateUI() {
    path = UIBezierPath(rect: myView.bounds)
    let viewFrames = myView.bounds
    overLay(points: CGPoint(x: viewFrames.origin.x - 15, y: viewFrames.origin.y - 15))
    overLay(points: CGPoint(x: viewFrames.origin.x + viewFrames.width - 15, y: viewFrames.origin.y - 15))
    overLay(points: CGPoint(x: viewFrames.origin.x - 15, y: viewFrames.origin.y + viewFrames.height - 15))
    overLay(points: CGPoint(x: viewFrames.origin.x + viewFrames.width - 15, y: viewFrames.origin.y + viewFrames.height - 15))
    overLay(points: CGPoint(x: viewFrames.origin.x - 15 , y: viewFrames.origin.y + viewFrames.height/2))
    overLay(points:  CGPoint(x: viewFrames.origin.x + viewFrames.width - 15, y: viewFrames.origin.y + viewFrames.height/2))
}

call updateUI from viewDidLayoutSubviews() method.

override func viewDidLayoutSubviews() {
    updateUI()
}

it will create the overlay on view with transparency.

Full code snippet

import UIKit

class ViewController: UIViewController {

@IBOutlet weak var myView: UIView!

var path = UIBezierPath()
override func viewDidLoad() {
    super.viewDidLoad()

    // Do any additional setup after loading the view, typically from a nib.
}

override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
    // Dispose of any resources that can be recreated.
}

func overLay(points: CGPoint) {
    let sizes = CGSize(width: 30, height: 30)
    let circlePath = UIBezierPath(ovalIn: CGRect(origin: points, size: sizes))
    path.append(circlePath)

    let maskLayer = CAShapeLayer() //create the mask layer
    maskLayer.path = path.cgPath // Give the mask layer the path you just draw
    maskLayer.fillRule = kCAFillRuleEvenOdd // Cut out the intersection part
    myView.layer.mask = maskLayer
}

override func viewDidLayoutSubviews() {
    updateUI()
}

func updateUI() {
    path = UIBezierPath(rect: myView.bounds)
    let viewFrames = myView.bounds
    overLay(points: CGPoint(x: viewFrames.origin.x - 15, y: viewFrames.origin.y - 15))
    overLay(points: CGPoint(x: viewFrames.origin.x + viewFrames.width - 15, y: viewFrames.origin.y - 15))
    overLay(points: CGPoint(x: viewFrames.origin.x - 15, y: viewFrames.origin.y + viewFrames.height - 15))
    overLay(points: CGPoint(x: viewFrames.origin.x + viewFrames.width - 15, y: viewFrames.origin.y + viewFrames.height - 15))
    overLay(points: CGPoint(x: viewFrames.origin.x - 15 , y: viewFrames.origin.y + viewFrames.height/2))
    overLay(points:  CGPoint(x: viewFrames.origin.x + viewFrames.width - 15, y: viewFrames.origin.y + viewFrames.height/2))
}

}

Upvotes: 3

KiranMayee Maddi
KiranMayee Maddi

Reputation: 297

I have created an enum with all the corners where the circle need to be placed.

enum Corners {
    case topLeft
    case topRight
    case bottomLeft
    case bottomRight
    case centerLeft
    case centerRight
}

I am adding the a circular view programmatically to the viewcontroller's view and setting it's color. Here, I have given white, you can change it to your respective transparent background.

func addCircularViews(toCorners corners: [Corners]) {
    for corner in corners {
        let circleView = UIView(frame: CGRect(x: 0, y: 0, width: 30, height: 30))
        circleView.layer.cornerRadius = circleView.frame.width/2
        circleView.backgroundColor = UIColor.white
        self.view.addSubview(circleView)
        setPosition(forCircularView: corner, circleView)
    }
}

I am setting the postion of the circle by changing the center of the circular view.

func setPosition(forCircularView corner: Corners,_ circleView: UIView) {
    let screenFrame = UIScreen.main.bounds
    switch corner {
    case .topLeft:
        circleView.center = CGPoint(x: screenFrame.origin.x, y: screenFrame.origin.y)
    case .topRight:
        circleView.center = CGPoint(x: screenFrame.origin.x + screenFrame.width, y: screenFrame.origin.y)
    case .bottomLeft:
        circleView.center = CGPoint(x: screenFrame.origin.x , y: screenFrame.origin.y + screenFrame.height)
    case .bottomRight:
        circleView.center = CGPoint(x: screenFrame.origin.x + screenFrame.width , y: screenFrame.origin.y + screenFrame.height)
    case .centerLeft:
        circleView.center = CGPoint(x: screenFrame.origin.x , y: screenFrame.origin.y + screenFrame.height/2)
    case .centerRight:
        circleView.center = CGPoint(x: screenFrame.origin.x + screenFrame.width , y: screenFrame.origin.y + screenFrame.height/2)
    }
}

You can call the above function where ever required

addCircularViews(toCorners: [.topLeft, .topRight, .centerRight, .centerLeft, .bottomLeft, .bottomRight])

As the above is an array, the corners can be set according to requirement. Hope it helps!!

Upvotes: 0

Related Questions