Search code examples
iosxcodeuiimagenine-patchcashapelayer

Can I use more slicing lines with UIImage?


I want to change rectangle proportions dynamically but preserving semicircle's proportions. Semicircle should be aligned to the bottom and centered horizontally:

enter image description here

How can I add more slicing lines to UIImage in Xcode?

I have made what I want in Nine Patch editor for android, but it isn't compatible with iOS.

enter image description here


Solution

  • There is no way to stretch UIImage like you want, but you can do it programmatically. I've implemented a custom UIView with CAShapeLayer inside:

    import UIKit
    
    class CustomShapeView: UIView {
    
        private let shapeLayer = CAShapeLayer()
        var radius = CGFloat(0)
        var shadowRadius = CGFloat(5)
    
        override func draw(_ rect: CGRect) {
            backgroundColor = UIColor.clear
    
            shapeLayer.frame = self.bounds
            shapeLayer.fillColor = UIColor.white.cgColor
            shapeLayer.path = createShapePath().cgPath
    
            shapeLayer.shadowPath = shapeLayer.path
            shapeLayer.shadowColor = UIColor.black.cgColor
            shapeLayer.shadowOffset = .zero
            shapeLayer.shadowRadius = shadowRadius
            shapeLayer.shadowOpacity = 0.5
    
            layer.addSublayer(shapeLayer)
        }
    
        func createShapePath () -> UIBezierPath {
            let path = UIBezierPath()
    
            let w = self.bounds.size.width
            let h = self.bounds.size.height
            let circleLever: CGFloat = radius * 0.552
    
            path.move(to: CGPoint(x: w/2 - radius, y: h))
            path.addLine(to: CGPoint(x: 0, y: h))
            path.addLine(to: CGPoint(x: 0, y: 0))
            path.addLine(to: CGPoint(x: w, y: 0))
            path.addLine(to: CGPoint(x: w, y: h))
            path.addLine(to: CGPoint(x: w/2 + radius, y: h))
    
            path.addCurve(to: CGPoint(x: w/2, y: h - radius), controlPoint1:CGPoint(x: w/2 + radius, y: h - circleLever), controlPoint2:CGPoint(x: w/2 + circleLever, y: h - radius))
            path.addCurve(to: CGPoint(x: w/2 - radius, y: h), controlPoint1:CGPoint(x: w/2 - circleLever, y: h - radius), controlPoint2:CGPoint(x: w/2 - radius, y: h - circleLever))
            path.close()
            path.move(to: CGPoint(x: w/2 - radius, y: h))
    
            return path
        }        
    }
    

    You can set whatever size and any radius you want, circle's center will always be on the bottom edge of the view. You also can change shadowRadius if you wish.

    Setup in Interface Builder:

    enter image description here

    Result:

    enter image description here