How to mask UIViews in iOS

I've been working on this problem for a couple of hours and have a solution that I think will do what you want. First, create your masking image using whatever means you see fit. Note that we only need the alpha values here, all other colours will be ignored, so make certain that the method you use supports alpha values. In this example I'm loading from a .png file, but don't try it with .jpg files as they don't have alpha values.

Next, create a new layer, assign your mask to its contents and set this new layer to your UIView's own layer, like so: you should find that this masks the UIView and all its attached subviews:

UIImage *_maskingImage = [UIImage imageNamed:@"mask"];
CALayer *_maskingLayer = [CALayer layer];
_maskingLayer.frame = theView.bounds;
[_maskingLayer setContents:(id)[_maskingImage CGImage]];
[theView.layer setMask:_maskingLayer];

With this done, you can set the UIView's background colour to whatever you like and the mask will be used to create a coloured filter.

EDIT: As of iOS8 you can now mask a view simply by assigning another view to its maskView property. The general rules stay the same in that the maskView's alpha layer is used to determine the opacity of the view it is applied to.


For apps targeting iOS 8.0+ this worked well (in this case, using a gradient as the mask) It avoids any need to resize or position the mask.

// Add gradient mask to view
func addGradientMask(targetView: UIView)
{
    let gradientMask = CAGradientLayer()

    gradientMask.frame = targetView.bounds
    gradientMask.colors = [UIColor.blackColor().CGColor, UIColor.clearColor().CGColor]
    gradientMask.locations = [0.8, 1.0]

    let maskView: UIView = UIView()
    maskView.layer.addSublayer(gradientMask)

    targetView.maskView = maskView 
}

In my case, I want to remove the mask once the user starts scrolling. This is done with:

func scrollViewWillBeginDragging(scrollView: UIScrollView) {

    exerDetailsTableView.maskView = nil        
}

where the view is defined as an @IBOutlet:

@IBOutlet weak var exerDetailsTableView: UITableView!

Result:

example screenshot


I don't know the exact code off the top of my head but the basic idea is to have two UIViews. One UIView would have it's image property set to be the grey scale image and the other UIView would be set as usual the only difference is that you would position the initial UIView directly on top of the UIView containing the "normal" image.

I hope that is enough to push your idea a step further.