Adding border to mask layer
Asked Answered
R

1

3

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.

Robey answered 24/12, 2017 at 10:45 Comment(4)
I would recommend you to use a resizable image with insetsParrot
I you want draw something, you shouldn't add that drawing layer to the mask layer. You should add this layer to the layer of the view. The mask layer is for describing the mask, and never for drawing.Durkheim
@Durkheim could you please clarify moreRobey
You can't draw a border with the mask layer or its sublayers. If you want to draw the border with a layer you should add that layer to button.layer as sublayer.Durkheim
R
3

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)
Robey answered 25/12, 2017 at 13:52 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.