Swiftui Add Inverted Mask

SwiftUI add inverted mask

Here is a demo of possible approach of creating inverted mask, by SwiftUI only, (on example to make a hole in view)

SwiftUI hole mask, reverse mask

func HoleShapeMask(in rect: CGRect) -> Path {
var shape = Rectangle().path(in: rect)
shape.addPath(Circle().path(in: rect))
return shape
}

struct TestInvertedMask: View {

let rect = CGRect(x: 0, y: 0, width: 300, height: 100)
var body: some View {
Rectangle()
.fill(Color.blue)
.frame(width: rect.width, height: rect.height)
.mask(HoleShapeMask(in: rect).fill(style: FillStyle(eoFill: true)))
}
}

Inverted mask swiftui with system image

This answer by Vlad Lego works when applied here.

You create your mask using .black for the parts you want cut out, and .white for the parts you want preserved.

(assuming the background is red, this is what it would look like)

Sample Image

Rectangle()
.foregroundColor(Color.white)
.mask(
ZStack {
Circle()
.fill(Color.white)
.frame(width: 50, height: 50)
Image(systemName: "play.fill")
.font(.system(size: 24))
.foregroundColor(Color.black)
}
.compositingGroup()
.luminanceToAlpha()
)

How to apply an inverse text mask in Swift?

Whilst not exactly the same, please refer to the answer provided here by Rob who answered a similar question:

How do I style a button to have transparent text?

This should get you started at the very least...

Using Swift and CAShapeLayer() with masking, how can I avoid inverting the mask when masked regions intersect?

You asked:

how can I avoid inverting the mask when masked regions intersect?

In short, do not use the .evenOdd fill rule.


You have specified a fillRule of .evenOdd. That results in intersections of paths to invert. Here is a red colored view with a mask consisting of a path with two overlapping circular arcs with the .evenOdd rule:

Sample Image

If you use .nonZero (which, coincidentally, is the default fill rule for shape layers), they will not invert each other:

Sample Image


E.g.

class ViewController: UIViewController {
@IBOutlet weak var imageView: UIImageView!

var maskLayer: CAShapeLayer = {
let shapeLayer = CAShapeLayer()
shapeLayer.fillColor = UIColor.white.cgColor
return shapeLayer
}()

var points: [CGPoint] = [] // this isn't strictly necessary, but just in case you want an array of the points that were tapped
var path = UIBezierPath()

override func viewDidLoad() {
super.viewDidLoad()
imageView.layer.mask = maskLayer
}

@IBAction func handleTapGesture(_ gesture: UITapGestureRecognizer) {
let point = gesture.location(in: gesture.view)
points.append(point)

path.move(to: point)
path.addArc(withCenter: point, radius: 40, startAngle: 0, endAngle: .pi * 2, clockwise: true)

maskLayer.path = path.cgPath
}
}

Resulting in:

Sample Image

SwiftUI Mask a rectangle inside a rounded rectangle

If I correctly understood your goal, here is a solution - the only needed clip in right place is after internal content (two rectangles in this case) is constructed. So clipping with RoundedRectangle gives rounded corners around entire card. (As well as shadow most probably is needed to entire card, so placed at the end).

UPDATE: re-tested with Xcode 13.3 / iOS 15.4

demo

ZStack (alignment: .topLeading) {
Rectangle()
.foregroundColor(.white)

Rectangle()
.fill(Color.pink)
.frame(minWidth: 0, maxWidth: .infinity, maxHeight: 150, alignment: .top)
}
.clipShape(RoundedRectangle(cornerRadius: 16)) // << here !!
.frame(width: 300, height: 450, alignment: .center)
.shadow(radius: 10)

How can I mask a UIImageView?

There's an easier way.

#import <QuartzCore/QuartzCore.h>
// remember to include Framework as well

CALayer *mask = [CALayer layer];
mask.contents = (id)[[UIImage imageNamed:@"mask.png"] CGImage];
mask.frame = CGRectMake(0, 0, <img_width>, <img_height>);
yourImageView.layer.mask = mask;
yourImageView.layer.masksToBounds = YES;

For Swift 4 and plus follow code below

let mask = CALayer()
mask.contents = [ UIImage(named: "right_challenge_bg")?.cgImage] as Any
mask.frame = CGRect(x: 0, y: 0, width: leftBGImage.frame.size.width, height: leftBGImage.frame.size.height)
leftBGImage.layer.mask = mask
leftBGImage.layer.masksToBounds = true


Related Topics



Leave a reply



Submit