5

I'm programming in Swift. I want to mask an image using CALayer and UIImage. I'm creating my mask image programmatically. The created mask image is a UIImage and works fine when I view it on its own. But when I use it as a mask the whole screen becomes white. I suspect that my problem is in configuring the CALayer object. I would appreciate your help. Thanks!

class ViewController: UIViewController {

    @IBOutlet weak var imageView: UIImageView!

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        var maskImageSize = CGSizeMake(self.imageView.frame.width, self.imageView.frame.height)
        UIGraphicsBeginImageContextWithOptions(maskImageSize, false, 0.0)

        var color = UIColor(white: 1.0, alpha: 1.0)
        color.setFill()
        var rect = CGRectMake(0, 0, self.imageView.frame.width, self.imageView.frame.height)
        UIRectFill(rect)

        color = UIColor(white: 0.0, alpha: 1.0)
        color.setFill()
        rect = CGRectMake((self.imageView.frame.width/2)-100, (self.imageView.frame.height/2)-100, 200, 200)
        UIRectFill(rect)

        var maskImage = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()

        var maskLayer = CALayer()
        maskLayer.contents = maskImage
        maskLayer.contentsRect = CGRectMake(0, 0, self.imageView.bounds.width, self.imageView.bounds.height)


        self.imageView.image = UIImage(named: "pictobemasked.png")

        self.imageView.layer.mask = maskLayer;
    }

}
matt
  • 515,959
  • 87
  • 875
  • 1,141
user2732722
  • 615
  • 1
  • 10
  • 22

1 Answers1

13

Unfortunately you've asked your question rather badly - you have not said what it is that you are actually trying to do! It looks, however, as if you might be trying to punch a rectangular hole in your image view using a mask. If so, your code has at least three huge flaws.

  • One reason your code is not working is that a mask is based on transparency, not on color. You are using an opaque white and an opaque black, which are both opaque, so there is no difference there. You need your two colors to be like this:

     var color = UIColor(white: 1.0, alpha: 1.0)
    // ... and then, later ...
    color = UIColor(white: 1.0, alpha: 0.0)
    
  • The second problem is that your layer has no size. You need to give it one:

    var maskLayer = CALayer()
    maskLayer.frame = CGRectMake(
        0, 0, self.imageView.bounds.width, self.imageView.bounds.height)
    
  • The third and biggest problem is that your mask image is never getting into your mask layer, because you have forgotten to extract its CGImage:

    maskLayer.contents = maskImage.CGImage
    

That last one is really the killer, because if you set the contents to a UIImage without extracting its CGImage, the image fails silently to get into the layer. There is no error message, no crash - and no image.

Making those three corrections in your code, I was able to make the mask punch a rectangular hole in an image. So if that's your purpose, those changes will achieve it.

matt
  • 515,959
  • 87
  • 875
  • 1,141
  • Thanks for your reply. All three points are correct. I still have problem which must be related to the sizing and positioning of the CALayer object. I'm new to this subject. I will post another comment elaborating the problem and the possible fix or more questions. Thanks! – user2732722 Dec 30 '14 at 21:46
  • Ask a new question! If you blip me with a comment here, I'll try to look at it. - Please note that you would not have made any of the above mistakes if you had read the chapter of my book on layers: http://www.apeth.com/iOSBook/ch16.html All three points are explicitly and emphatically mentioned. – matt Dec 30 '14 at 22:04
  • Thanks for the link to you book chapter. I admit that I need to study thoroughly first. I'm almost in time pressure and that's why I've started hacking. My problem is that I don't know how to align the mask onto the original image. I want to show arbitrary size image. I generate the contents of the mask programmatically so that it's the same size as the image. This hole in the middle of the mask is just a test. After I get this running I want to have a more sophisticated mask that needs to cover the whole image. I would appreciate any help to get this alignment right. Thanks! – user2732722 Dec 30 '14 at 23:08
  • This really needs to be a new question! – matt Dec 30 '14 at 23:44
  • I posted a new question here : http://stackoverflow.com/questions/27714562/how-to-align-a-mask-onto-an-image. Thanks for your help. – user2732722 Dec 31 '14 at 00:26
  • I did the above, and it worked. However I wanted a circle. Using UIBezierPath to draw the circle was easy, but there is a difference between UIRectFill(..) and the bezier object .fill(). The difference is that .fill() with a fully transparent color doesn't draw anything. Invisible ink! So remember to use CGContextSetBlendMode(UIGraphicsGetCurrentContext(), .Copy) to say you want to replace what's underneath pixel for pixel. – specimen Nov 28 '15 at 19:58
  • @specimen I think what you're missing is the UIBezierPath command `fillWithBlendMode`. Use a blend mode of `.Clear` (and it doesn't matter what the alpha is) and now you've erased the circle region. It doesn't matter what the fill color is! – matt Nov 28 '15 at 22:47
  • @matt I'll try that, but the CGContextSetBlendMode works for me, but it will be nice to remove that line though. – specimen Nov 28 '15 at 23:12
  • @specimen The point I'm making is that `fillWithBlendMode` calls `CGContextSetBlendMode` _for you_, so you never need to get the current graphics context — you can do the whole thing with the UIBezierPath (and UIColor). – matt Nov 29 '15 at 00:30
  • @matt Yes, I understood that much :) Thanks for helping me out though, I'm still learning. – specimen Nov 30 '15 at 08:50