How do I create a UIView with a transparent circle inside (in swift)?

前端 未结 4 1396
一生所求
一生所求 2020-12-12 20:30

I want to create a view that looks like this: \"Goal

I figure what I need is a uiview with some sort of

相关标签:
4条回答
  • 2020-12-12 20:55

    For Swift 3, here is rakeshbs' answer formatted so it returns the UIView needed:

    func createOverlay(frame : CGRect, xOffset: CGFloat, yOffset: CGFloat, radius: CGFloat) -> UIView
    {
        let overlayView = UIView(frame: frame)
        overlayView.alpha = 0.6
        overlayView.backgroundColor = UIColor.black
    
        // Create a path with the rectangle in it.
        let path = CGMutablePath()
        path.addArc(center: CGPoint(x: xOffset, y: yOffset), radius: radius, startAngle: 0.0, endAngle: 2 * 3.14, clockwise: false)
        path.addRect(CGRect(x: 0, y: 0, width: overlayView.frame.width, height: overlayView.frame.height))
    
        let maskLayer = CAShapeLayer()
        maskLayer.backgroundColor = UIColor.black.cgColor
        maskLayer.path = path;
        maskLayer.fillRule = kCAFillRuleEvenOdd
    
        // Release the path since it's not covered by ARC.
        overlayView.layer.mask = maskLayer
        overlayView.clipsToBounds = true
    
        return overlayView
    }
    
    0 讨论(0)
  • 2020-12-12 20:56

    You can use this function to create what you need.

    func createOverlay(frame : CGRect)
    {
        let overlayView = UIView(frame: frame)
        overlayView.alpha = 0.6
        overlayView.backgroundColor = UIColor.blackColor()
        self.view.addSubview(overlayView)
    
        let maskLayer = CAShapeLayer()
    
        // Create a path with the rectangle in it.
        var path = CGPathCreateMutable()
    
        let radius : CGFloat = 50.0
        let xOffset : CGFloat = 10
        let yOffset : CGFloat = 10
    
        CGPathAddArc(path, nil, overlayView.frame.width - radius/2 - xOffset, yOffset, radius, 0.0, 2 * 3.14, false)
        CGPathAddRect(path, nil, CGRectMake(0, 0, overlayView.frame.width, overlayView.frame.height))
    
    
        maskLayer.backgroundColor = UIColor.blackColor().CGColor
    
        maskLayer.path = path;
        maskLayer.fillRule = kCAFillRuleEvenOdd
    
        // Release the path since it's not covered by ARC.
        overlayView.layer.mask = maskLayer
        overlayView.clipsToBounds = true
    }
    

    Adjust the radius and xOffset and yOffset to change the radius and position of the circle.

    0 讨论(0)
  • 2020-12-12 21:11

    The above solution works great.

    Say if you are looking for mask with rectangle area here is the snippet below

    let fWidth = self.frame.size.width
    let fHeight = self.frame.size.height
    let squareWidth = fWidth/2
    let topLeft = CGPoint(x: fWidth/2-squareWidth/2, y: fHeight/2-squareWidth/2)
    let topRight = CGPoint(x: fWidth/2+squareWidth/2, y: fHeight/2-squareWidth/2)
    let bottomLeft = CGPoint(x: fWidth/2-squareWidth/2, y: fHeight/2+squareWidth/2)
    let bottomRight = CGPoint(x: fWidth/2+squareWidth/2, y: fHeight/2+squareWidth/2)
    let cornerWidth = squareWidth/4
    
    
    // Step 2
    let path = CGMutablePath()
    path.addRoundedRect(in: CGRect(x: topLeft.x, y: topLeft.y,
      width: topRight.x - topLeft.x, height: bottomLeft.y - topLeft.y),
     cornerWidth: 20, cornerHeight: 20)
    
    
    path.addRect(CGRect(origin: .zero, size: self.frame.size))
    // Step 3
    let maskLayer = CAShapeLayer()
    maskLayer.backgroundColor = UIColor.clear.cgColor
    maskLayer.path = path
    // For Swift 4.0
    maskLayer.fillRule = kCAFillRuleEvenOdd
    // For Swift 4.2
    //maskLayer.fillRule = .evenOdd
    // Step 4
    self.layer.mask = maskLayer
    self.clipsToBounds = true
    

    rectangle mask looks like this

    0 讨论(0)
  • 2020-12-12 21:13

    Updated again for Swift 4 & removed a few items to make the code tighter.

    Please note that maskLayer.fillRule is set differently between Swift 4 and Swift 4.2.

    func createOverlay(frame: CGRect,
                       xOffset: CGFloat,
                       yOffset: CGFloat,
                       radius: CGFloat) -> UIView {
        // Step 1
        let overlayView = UIView(frame: frame)
        overlayView.backgroundColor = UIColor.black.withAlphaComponent(0.6)
        // Step 2
        let path = CGMutablePath()
        path.addArc(center: CGPoint(x: xOffset, y: yOffset),
                    radius: radius,
                    startAngle: 0.0,
                    endAngle: 2.0 * .pi,
                    clockwise: false)
        path.addRect(CGRect(origin: .zero, size: overlayView.frame.size))
        // Step 3
        let maskLayer = CAShapeLayer()
        maskLayer.backgroundColor = UIColor.black.cgColor
        maskLayer.path = path
        // For Swift 4.0
        maskLayer.fillRule = kCAFillRuleEvenOdd
        // For Swift 4.2
        maskLayer.fillRule = .evenOdd
        // Step 4
        overlayView.layer.mask = maskLayer
        overlayView.clipsToBounds = true
    
        return overlayView
    }
    

    A rough breakdown on what is happening:

    1. Create a view sized to the specified frame, with a black background set to 60% opacity
    2. Create the path for drawing the circle using the provided starting point and radius
    3. Create the mask for the area to remove
    4. Apply the mask & clip to bounds

    The following code snippet will call this and place a circle in the middle of the screen with radius of 50:

    let overlay = createOverlay(frame: view.frame,
                                xOffset: view.frame.midX,
                                yOffset: view.frame.midY,
                                radius: 50.0)
    view.addSubview(overlay)
    

    Which looks like this:

    0 讨论(0)
提交回复
热议问题