Using an Uiview as a Mask in Another Uiview on Swift

Using an UIView as a Mask in another UIView on Swift

You can set the alpha property from your mask view and add in front of the other view, for instance:

let maskView = UIView()
maskView.backgroundColor = UIColor(white: 0, alpha: 0.5) //you can modify this to whatever you need
maskView.frame = CGRect(x: 0, y: 0, width: imageView.frame.width, height: imageView.frame.height)

yourView.addSubview(maskView)

EDIT: Now that you edited your question with an image, now I see what you need, so here is how you can accomplish that.

func setMask(with hole: CGRect, in view: UIView){

// Create a mutable path and add a rectangle that will be h
let mutablePath = CGMutablePath()
mutablePath.addRect(view.bounds)
mutablePath.addRect(hole)

// Create a shape layer and cut out the intersection
let mask = CAShapeLayer()
mask.path = mutablePath
mask.fillRule = kCAFillRuleEvenOdd

// Add the mask to the view
view.layer.mask = mask

}

With this function, all you need is to have a view and create a shape that it's going to be a hole in that view, for instance:

// Create the view (you can also use a view created in the storyboard)
let newView = UIView(frame: CGRect(x: 0, y: 0, width: view.frame.width, height: view.frame.height))
newView.backgroundColor = UIColor(white: 0, alpha: 1)

// You can play with these values and find one that fills your need
let rectangularHole = CGRect(x: view.bounds.width*0.3, y: view.bounds.height*0.3, width: view.bounds.width*0.5, height: view.bounds.height*0.5)

// Set the mask in the created view
setMask(with: rectangularHole, in: newView)

How can we set a UIView as a mask of another UIView

UIImageView and UILabel both have intrinsic content size, but not UIView. Mask of a view has to have a frame set properly. Make sure that the containerView.frame is set:

containerView.frame = gradientView.bounds

Also, make sure that containerView.backgroundColor is set to UIColor.clear.

How to apply multiple masks to UIView

Based on @Sharad's answer, I realised that re-adding the view's rect would enable me to combine the original and new mask into one.

Here is my solution:

func cutCircle(inView view: UIView, withRect rect: CGRect) {

// Create new path and mask
let newMask = CAShapeLayer()
let newPath = UIBezierPath(ovalIn: rect)

// Create path to clip
let newClipPath = UIBezierPath(rect: view.bounds)
newClipPath.append(newPath)

// If view already has a mask
if let originalMask = view.layer.mask,
let originalShape = originalMask as? CAShapeLayer,
let originalPath = originalShape.path {

// Create bezierpath from original mask's path
let originalBezierPath = UIBezierPath(cgPath: originalPath)

// Append view's bounds to "reset" the mask path before we re-apply the original
newClipPath.append(UIBezierPath(rect: view.bounds))

// Combine new and original paths
newClipPath.append(originalBezierPath)

}

// Apply new mask
newMask.path = newClipPath.cgPath
newMask.fillRule = kCAFillRuleEvenOdd
view.layer.mask = newMask
}

Masking multiple iOS UIViews with a single UIView

Looks like the answer is no, you can't use the same UIView instance to mask multiple views: apparently the masking view's layer is being integrated into the layer hierarchy of the view that is being masked.

The best way would be to create a new UIView mask instance for each of the views you are trying to mask. Alternatively you could copy the existing view via NSKeyedArchiver:

let archivedData = NSKeyedArchiver.archivedDataWithRootObject(viewMask)
let viewMaskCopy = NSKeyedUnarchiver.unarchiveObjectWithData(archivedData) as! UIView

Although I would suggest simply creating new masking views instances the way you instantiated viewMask in the first place.

You may also group the masked views in a containing UIView in storyboard, and apply the mask to that view instead.

Masking a UIView and Auto Layout

As Zev explained, the mask view lives outside of the ordinary view hierarchy and so can't be used together with Auto Layout. I got around this by placing it manually in my view controller's viewDidLayoutSubviews:

-(void)viewDidLayoutSubviews{
[super viewDidLayoutSubviews];
CGRect viewToMaskRect = self.viewToMask.bounds;
CGRect maskRect = CGRectMake(viewToMaskRect.origin.x + 50.0, viewToMaskRect.origin.y + 50.0, 100.0, 100.0);
[self.theMask setFrame:maskRect];
[self.viewToMask setMaskView:self.theMask];
}

proper masking

Masking A UIView With UILabel Using AutoLayout

Masks work in an opposite fashion, when the color on your mask is not transparent then it will show the content in that pixels position and when the pixels are transparent then it will hide the content.

Since you can't achieve this using UILabel you need to use something else as a mask, such as a CALayer.

If you lookup "UILabel see through text" you should find results similar to this which basically also applies to you (with some changes).

Instantiate your CircleView, then instantiate a CATextLayer and use this as a mask for your UIView



Related Topics



Leave a reply



Submit