Tarek hemdan
Tarek hemdan

Reputation: 874

Adding border to mask layer

I'm trying to make a custom shape UIButton using mask layers and I was successful

extension UIButton { 

  func mask(withImage image : UIImage , frame : CGRect ){

        let maskingLayer = CAShapeLayer()
        maskingLayer.frame = frame
        maskingLayer.contents = image.cgImage
        self.layer.mask = maskingLayer
    }
}

But I want to add a border (stroke) to the mask layer to indicate that this button was chosen.

I tried adding a sub layer to the mask layer

button.mask?.layer.borderColor = UIColor.black.cgColor
button.mask?.layer.borderWidth = 4

but didn't work since the button shape is still rectangle.

I know I can use CGMutablePath to define the shape

func mask(withPath path: CGMutablePath , frame : CGRect , color : UIColor) {

    let mask = CAShapeLayer()
    mask.frame = frame
    mask.path = path
    self.layer.mask = mask

    let shape = CAShapeLayer()
    shape.frame = self.bounds
    shape.path = path
    shape.lineWidth = 3.0
    shape.strokeColor = UIColor.black.cgColor
    shape.fillColor = color.cgColor

    self.layer.insertSublayer(shape, at: 0)
   //  self.layer.sublayers![0].masksToBounds = true
}

but drawing such complex shape using paths is extremly hard

enter image description here

Any help would be appreciated.

Upvotes: 3

Views: 2312

Answers (1)

Tarek hemdan
Tarek hemdan

Reputation: 874

i was able to get CGPath from an image(.svg) using PocketSVG

but i faced another problem that the scale of the path is equal to the original SVG so i managed to scale the path to fit in frame , here is the full code :-

extension UIView {
    func mask(withSvgName ImageName : String , frame : CGRect , color : UIColor){

            let svgutils = SvgUtils()
            let paths = svgutils.getLayerFromSVG(withImageName: ImageName)
            let mask = CAShapeLayer()
            mask.frame = frame
            let newPath = svgutils.resizepath(Fitin: frame, path: paths[0].cgPath)
            mask.path = newPath
            self.layer.mask = mask

            let shape = CAShapeLayer()
            shape.frame = self.bounds
            shape.path = newPath
            shape.lineWidth = 2.0
            shape.strokeColor = UIColor.black.cgColor
            shape.fillColor = color.cgColor
            self.layer.insertSublayer(shape, at: 0)

        }
}

and the utilities class

import Foundation
import PocketSVG

class SvgUtils{


    func getLayerFromSVG(withImageName ImageName : String ) -> [SVGBezierPath]{

        let url = Bundle.main.url(forResource: ImageName, withExtension: "svg")!

        var paths = [SVGBezierPath]()

        for path in SVGBezierPath.pathsFromSVG(at: url) {

            paths.append(path)
        }

        return paths
    }

    func resizepath(Fitin frame : CGRect , path : CGPath) -> CGPath{


        let boundingBox = path.boundingBox
        let boundingBoxAspectRatio = boundingBox.width / boundingBox.height
        let viewAspectRatio = frame.width  / frame.height
        var scaleFactor : CGFloat = 1.0
        if (boundingBoxAspectRatio > viewAspectRatio) {
            // Width is limiting factor

            scaleFactor = frame.width / boundingBox.width
        } else {
            // Height is limiting factor
            scaleFactor = frame.height / boundingBox.height
        }


        var scaleTransform = CGAffineTransform.identity
        scaleTransform = scaleTransform.scaledBy(x: scaleFactor, y: scaleFactor)
        scaleTransform.translatedBy(x: -boundingBox.minX, y: -boundingBox.minY)

        let scaledSize = boundingBox.size.applying(CGAffineTransform (scaleX: scaleFactor, y: scaleFactor))
       let centerOffset = CGSize(width: (frame.width - scaledSize.width ) / scaleFactor * 2.0, height: (frame.height - scaledSize.height) /  scaleFactor * 2.0 )
        scaleTransform = scaleTransform.translatedBy(x: centerOffset.width, y: centerOffset.height)
        //CGPathCreateCopyByTransformingPath(path, &scaleTransform)
        let  scaledPath = path.copy(using: &scaleTransform)


        return scaledPath!
    }

}

and simply use it like this

button.mask(withSvgName: "your_svg_fileName", frame: button.bounds, color: UIColor.green)

Upvotes: 3

Related Questions