Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Corner radius image Swift

I'm trying to make this corner radius image...it's not exactly the same shape of the image..any easy answer instead of trying random numbers of width and height ? thanks alot

enter image description here

 let rectShape = CAShapeLayer()
 rectShape.bounds = self.mainImg.frame
 rectShape.position = self.mainImg.center
 rectShape.path = UIBezierPath(roundedRect: self.mainImg.bounds, byRoundingCorners: [.bottomLeft , .bottomRight ], cornerRadii: CGSize(width: 50, height: 4)).cgPath
like image 978
ema so Avatar asked Nov 24 '25 11:11

ema so


1 Answers

You can use QuadCurve to get the design you want.

Here is a Swift @IBDesignable class that lets you specify the image and the "height" of the rounding in Storyboard / Interface Builder:

@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.scaleAspectFill
        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 + roundingValue
        
        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
    
    }

}

Result with 300 x 200 image view, rounding set to 40:

enter image description here


Edit - (3.5 years later)...

To answer @MiteshDobareeya comment, we can switch the rounded edge from Bottom to Top by transforming the bezier path:

let c = CGAffineTransform(scaleX: 1, y: -1).concatenating(CGAffineTransform(translationX: 0, y: bounds.size.height))
        myBezier.apply(c)

It's been quite a while since this answer was originally posted, so a few changes:

  • subclass UIImageView directly - no need to make it a UIView with an embedded UIImageView
  • add a Bool roundTop var
    • if set to False (the default), we round the Bottom
    • if set to True, we round the Top
  • re-order and "name" our path points for clarity

So, the basic principle:

enter image description here

We create a UIBezierPath and:

  • move to pt1
  • add a line to pt2
  • add a line to pt3
  • add a quad-curve to pt4 with controlPoint
  • close the path
  • use that path for a CAShapeLayer mask

the result:

enter image description here

If we want to round the Top, after closing the path we can apply apply a scale transform using -1 as the y value to vertically mirror it. Because that transform mirror it at "y-zero" we also apply a translate transform to move it back down into place.

That gives us:

enter image description here

Here's the updated class:

@IBDesignable
class RoundedTopBottomImageView: UIImageView {
    
    @IBInspectable var roundingValue: CGFloat = 0.0 {
        didSet {
            self.setNeedsLayout()
        }
    }
    
    @IBInspectable var roundTop: Bool = false {
        didSet {
            self.setNeedsLayout()
        }
    }
    
    override func layoutSubviews() {
        super.layoutSubviews()
        
        let r = bounds
        
        let myBezier = UIBezierPath()
        
        let pt1: CGPoint = CGPoint(x: r.minX, y: r.minY)
        let pt2: CGPoint = CGPoint(x: r.maxX, y: r.minY)
        let pt3: CGPoint = CGPoint(x: r.maxX, y: r.maxY - roundingValue)
        let pt4: CGPoint = CGPoint(x: r.minX, y: r.maxY - roundingValue)
        
        let controlPoint: CGPoint = CGPoint(x: r.midX, y: r.maxY + roundingValue)
        
        myBezier.move(to: pt1)
        myBezier.addLine(to: pt2)
        myBezier.addLine(to: pt3)
        myBezier.addQuadCurve(to: pt4, controlPoint: controlPoint)
        myBezier.close()
        
        if roundTop {
            // if we want to round the Top instead of the bottom,
            //  flip the path vertically
            let c = CGAffineTransform(scaleX: 1, y: -1) //.concatenating(CGAffineTransform(translationX: 0, y: bounds.size.height))
            myBezier.apply(c)
        }
        
        let maskForPath = CAShapeLayer()
        maskForPath.path = myBezier.cgPath
        layer.mask = maskForPath
    }
    
}
like image 82
DonMag Avatar answered Nov 27 '25 01:11

DonMag



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!