-1

I have created below masked image and I have masked image like this

image imag2

Here is my code

//
//  Collage_Layout_1.swift
//  Snapcial
//
//  Created by Jecky Modi on 15/02/25.
//


class Collage_Layout_1: CollageLayoutBaseView {
    var maskedImage: String = ""
    var originalImage = UIImage()
    private let maskImageView = UIImageView()
    var viewFrame : CGRect = .zero
    //    private var originalImageCenter: CGPoint?
    
    init(frame: CGRect, maskedImage: String, originalImage: UIImage) {
        super.init(frame: frame)
        self.maskedImage = maskedImage
        self.originalImage = originalImage
        self.viewFrame = frame
    }
    
    override func setUpCollage() {
        let maskedContainer = self.arrViews[0].containerView
        maskedContainer.tag = 100
                
        let maskImage = UIImage(named: maskedImage)?.withRenderingMode(.alwaysTemplate)
        maskImageView.translatesAutoresizingMaskIntoConstraints = false
        maskImageView.image = maskImage
        maskImageView.contentMode = .scaleAspectFit
        maskImageView.tintColor = .clear
        addSubview(maskImageView)       // Keep the mask image visible
        
        // Create a mask layer from the black shape
        let maskLayer = CALayer()
        maskLayer.contents = maskImage?.cgImage
        maskLayer.frame = self.viewFrame
        maskLayer.contentsGravity = .resizeAspect
        
        maskedContainer.layer.mask = maskLayer
        maskedContainer.clipsToBounds = true
        maskedContainer.backgroundColor = .clear
        self.backgroundColor = .clear
        
        addSubview(maskedContainer)     // Add container with mask
        
        
        NSLayoutConstraint.activate([
            maskImageView.leadingAnchor.constraint(equalTo: self.leadingAnchor),
            maskImageView.topAnchor.constraint(equalTo: self.topAnchor),
            maskImageView.trailingAnchor.constraint(equalTo: self.trailingAnchor),
            maskImageView.bottomAnchor.constraint(equalTo: self.bottomAnchor),

            maskedContainer.leadingAnchor.constraint(equalTo: self.leadingAnchor),
            maskedContainer.topAnchor.constraint(equalTo: self.topAnchor),
            maskedContainer.trailingAnchor.constraint(equalTo: self.trailingAnchor),
            maskedContainer.bottomAnchor.constraint(equalTo: self.bottomAnchor),
        ])
        

        self.layoutIfNeeded()
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

I want to add borders to my masked image that border should take shape of mask.

Any help would be appreciated.

5
  • May be add shadow Commented Apr 17 at 13:51
  • Use a vector drawing program to trace your "black / mask" image, and use the resulting path as the mask, and as the path for a stroked shape layer. Or, you may find this useful: stackoverflow.com/a/74197544/6257435 Commented Apr 17 at 13:52
  • To draw a proper border, you want a “path” (a CGPath or UIBezierPath). So, you could (a) create a B&W rendition of the mask image; (b) perform “edge detection” (stackoverflow.com/questions/78359266/78360887#78360887) to get a path from this B&W rendition of the mask image; (c) create a view with a CAShapeLayer to render this path; and (d) place this view above the image view. It’s a lot of work (both programmatically & computationally), so ask whether you really want to do that, or whether you can just start with a path, and eliminate this “get path from image alpha” overhead. Commented Apr 17 at 15:48
  • 2
    Jitendra -- with a few edits, the code linked to by Rob works nicely. Here's an example result: i.sstatic.net/rU1H2aNk.png Commented Apr 17 at 20:35
  • can you please make changes in my sample code and what edits you make in that? I am not able to achieve this @DonMag Commented Apr 18 at 7:08

1 Answer 1

1

Based on this answer: How do I get a Path from an Image( )?

Code stripped-down a bit so we can see how we're using a UIImage (the black leaf with transparent background), instead of a generating a SF Symbol image.

enum ContourError: Error {
    case cgImageFailed
    case noContour
}

extension CGPath {
    static func imgContourPath(image: UIImage) throws -> CGPath {
        guard let cgImage = image.cgImage else { throw ContourError.cgImageFailed }
        return try cgImage.contourPath(size: image.size)
    }
}

extension CGImage {
    func contourPath(size: CGSize? = nil, contrastAdjustment: Float = 2, maximumImageDimension: Int? = nil) throws -> CGPath {
        let size = size ?? CGSize(width: width, height: height)
        
        let contourRequest = VNDetectContoursRequest()
        contourRequest.maximumImageDimension = maximumImageDimension ?? Int(max(size.width, size.height))
        contourRequest.contrastAdjustment = contrastAdjustment
        let requestHandler = VNImageRequestHandler(cgImage: self, options: [:])
        try requestHandler.perform([contourRequest])
        var transform = CGAffineTransform(translationX: 0, y: CGFloat(size.height))
            .scaledBy(x: CGFloat(size.width), y: -CGFloat(size.height))
        guard let path = contourRequest.results?.first?.normalizedPath.mutableCopy(using: &transform) else {
            throw ContourError.noContour
        }
        return path
    }
}

extension UIImage {
    func withBackground(_ background: UIColor) -> UIImage? {
        let format = UIGraphicsImageRendererFormat()
        format.scale = scale
        let rect = CGRect(origin: .zero, size: size)
        return UIGraphicsImageRenderer(bounds: rect, format: format).image { _ in
            background.setFill()
            UIBezierPath(rect: rect).fill()
            draw(in: rect)
        }
    }
}

Using this 1200x1200 "image to mask":

and this 1200x1200 "mask image" (with transparent background):

We can generate a Path to use as both the Mask and the Border / Outline:

class ExampleVC: UIViewController {
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        view.backgroundColor = UIColor(white: 0.95, alpha: 1.0)
        
        guard let img = UIImage(named: "maskImage"),
              let origImage = UIImage(named: "imageToMask")
        else { fatalError("Could not load images") }
        
        // let's use a 300x300 image view
        let r: CGRect = .init(origin: .zero, size: .init(width: 300, height: 300)).offsetBy(dx: 20, dy: 50)
        
        let imgView = UIImageView(image: origImage)
        imgView.frame = r
        view.addSubview(imgView)
        
        let szOrig = img.size
        
        // for Contour detection, we want a black shape on a white background
        //  NOT transparent background
        guard let wImage = img.withBackground(.white) else { fatalError("Failed to create white background image") }
        
        if let cp = try? CGPath.imgContourPath(image: wImage) {

            // the Contour Path matches the image size...
            //  we want to scale the path to fit the view size
            let bz = UIBezierPath(cgPath: cp)
            let scaleX = r.width / szOrig.width
            let scaleY = r.height / szOrig.height
            bz.apply(CGAffineTransform(scaleX: scaleX, y: scaleY))

            // pathed shape layer to mask the image view
            let mskLayer = CAShapeLayer()
            mskLayer.path = bz.cgPath

            // pathed shape layer for the border / outline
            let sl = CAShapeLayer()
            sl.strokeColor = UIColor.red.cgColor
            sl.fillColor = nil
            sl.lineWidth = 3
            sl.path = bz.cgPath

            // mask the image view
            imgView.layer.mask = mskLayer
            
            // add the border / outline layer
            imgView.layer.addSublayer(sl)
        }
        
    }
    
}

Giving us this output:

output image

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.