Search code examples
iosxamarin.ioscore-animation

Mask of layer mask. Core animation


I deep into the Core Animation and have some problem with layer masking

I try to make a complex UI layer with transparent things for study reasons

This view a have and put into the ViewController

public class TestView : UIView
    {
        public TestView()
        {
            Layer.Delegate = this;
            Frame = new CGRect(100f,200f, 100f, 100f);
        }

        public override void LayoutSublayersOfLayer(CALayer layer)
        {
            base.LayoutSublayersOfLayer(layer);

            var bounds = Bounds;
            var circleFrame = new CGRect(20, 20, 60, 60);
            var imageFrame = new CGRect(30, 30, 40, 40);

            Layer.BorderWidth = 5;
            Layer.BorderColor = UIColor.Red.CGColor;
            Layer.CornerRadius = 4;

            var gradientLayer = new CAGradientLayer();
            gradientLayer.Colors = new[] { UIColor.Green.CGColor, UIColor.Red.CGColor };
            gradientLayer.Frame = bounds;
            Layer.AddSublayer(gradientLayer);

            var maskOfGradientLayer = new CAShapeLayer();
            var path = UIBezierPath.FromRoundedRect(circleFrame, 30);
            path.UsesEvenOddFillRule = true;
            maskOfGradientLayer.Path = path.CGPath;
            maskOfGradientLayer.FillRule = CAShapeLayer.FillRuleEvenOdd;
            maskOfGradientLayer.FillColor = UIColor.Black.CGColor;
            maskOfGradientLayer.Frame = bounds;
            gradientLayer.Mask = maskOfGradientLayer;

            var maskOfMaskLayer = new CAShapeLayer();
            maskOfMaskLayer.Frame = imageFrame;
            maskOfMaskLayer.FillColor = UIColor.Black.CGColor;
            maskOfMaskLayer.Contents = Bundles.ImageOff.CGImage;

            Layer.AddSublayer(maskOfMaskLayer);
        }
    }

And that I have and this is exactly what I wanted.

enter image description here

But I also want can to make transparent in circle instead black color.

I tried to make like this maskOfGradientLayer.Mask = maskOfMaskLayer. But this way maskOfMaskLayer do nothing! It's not working like mask must do.
What I should to do?


Solution

  • All needed to do it is reverse image colors via Core Graphics, any color to transparent, transparent to any color. In this case, black color is enough

    public static UIImage GetRevertImage(UIImage image, CGRect rect, CGRect cropFrame)
    {
        UIGraphics.BeginImageContextWithOptions(rect.Size, false, 1);
        UIColor.Black.SetColor();
        UIGraphics.RectFill(rect);
        image.Draw(cropFrame, CGBlendMode.DestinationOut, 1);
    
        var newImage = UIGraphics.GetImageFromCurrentImageContext();
        UIGraphics.EndImageContext();
        return newImage;
    }
    

    And set mask of mask layer to parent layer

    Layer.Mask = maskOfMaskLayer;

    And final version of code:

    public class TestView : UIView
    {
        public TestView()
        {
            Layer.Delegate = this;
            Frame = new CGRect(50f, 50f, 100f, 100f);
        }
    
        public override void LayoutSublayersOfLayer(CALayer layer)
        {
            base.LayoutSublayersOfLayer(layer);
    
            var bounds = Bounds;
            var circleFrame = new CGRect(20, 20, 60, 60);
            var imageFrame = new CGRect(30, 30, 40, 40);
    
            Layer.BorderWidth = 5;
            Layer.BorderColor = UIColor.Red.CGColor;
            Layer.CornerRadius = 4;
            Layer.MasksToBounds = true;
    
            var gradientLayer = new CAGradientLayer();
            gradientLayer.Colors = new[] {UIColor.Green.CGColor, UIColor.Red.CGColor};
            gradientLayer.Frame = bounds;
            Layer.AddSublayer(gradientLayer);
    
            var maskOfGradientLayer = new CAShapeLayer();
            var path = UIBezierPath.FromRoundedRect(circleFrame, 30);
            path.UsesEvenOddFillRule = true;
            maskOfGradientLayer.Path = path.CGPath;
            maskOfGradientLayer.FillRule = CAShapeLayer.FillRuleEvenOdd;
            maskOfGradientLayer.FillColor = UIColor.Black.CGColor;
            maskOfGradientLayer.Frame = bounds;
            gradientLayer.Mask = maskOfGradientLayer;
    
            var maskOfMaskLayer = new CALayer();
            maskOfMaskLayer.Frame = bounds;
            var image = UIImage.FromBundle("Turn");
            var reversedImage = GetReverseImage(image, bounds, imageFrame);
            maskOfMaskLayer.Contents = reversedImage.CGImage;
    
            Layer.Mask = maskOfMaskLayer;
        }
    
        private static UIImage GetReverseImage(UIImage image, CGRect rect, CGRect cropFrame)
        {
            UIGraphics.BeginImageContextWithOptions(rect.Size, false, 1);
            UIColor.Black.SetColor();
            UIGraphics.RectFill(rect);
            image.Draw(cropFrame, CGBlendMode.DestinationOut, 1);
            var newImage = UIGraphics.GetImageFromCurrentImageContext();
            UIGraphics.EndImageContext();
    
            return newImage;
        }
    }
    

    Final version