0

I have created a UIView subclass which uses a CAGradientLayer for its layer. This is working fine. Now I want to draw a line at the end of the gradient (used as separator). I thought I can use the drawRect method for this:

public class GradientView : UIView
{
    // accessors
    private CAGradientLayer gradientLayer {
        // read-only
        get { return (CAGradientLayer)this.Layer; }
    }

    public CGColor[] Colors {
        // set the colors of the gradient layer
        get { return this.gradientLayer.Colors; }
        set { this.gradientLayer.Colors = value; }
    }

    [Export ("layerClass")]
    public static Class LayerClass ()
    {
        // use a different Core Animation layer for its backing store
        // normally a CALayer is used for a UIView
        return new Class (typeof(CAGradientLayer));
    }

    public override void Draw (RectangleF rect)
    {

        PointF pointA = new PointF (0, rect.Height);
        PointF pointB = new PointF (rect.Width, rect.Height);
        CAShapeLayer line = new CAShapeLayer ();
        UIBezierPath linePath = new UIBezierPath ();
        linePath.MoveTo (pointA);
        linePath.AddLineTo(pointB);
        line.Path = linePath.CGPath;
        line.FillColor = null;
        line.Opacity = 1.0f;
        line.StrokeColor = UIColor.Black.CGColor;

        this.gradientLayer.AddSublayer (line);

        base.Draw (rect);
    }
}

But I get a black background. Can I use drawRect for that? Or how do I add a line on it?

I'm using C# as language but you can provide your solution in Objective-C as well. Auto Layout determines the size of the GradientView and my old solution also adapted to orientation changes.

Edit:

I tried what Cyrille said:

public class GradientView : UIView
{
    // accessors
    private CAShapeLayer line;

    private CAGradientLayer gradientLayer {
        // read-only
        get { return (CAGradientLayer)this.Layer; }
    }

    public CGColor[] Colors {
        // set the colors of the gradient layer
        get { return this.gradientLayer.Colors; }
        set { this.gradientLayer.Colors = value; }
    }

    public GradientView()
    {
        PointF pointA = new PointF (0, this.Bounds.Height);
        PointF pointB = new PointF (this.Bounds.Width, this.Bounds.Height);
        line = new CAShapeLayer ();
        UIBezierPath linePath = new UIBezierPath ();
        linePath.MoveTo (pointA);
        linePath.AddLineTo(pointB);
        line.Path = linePath.CGPath;
        line.FillColor = null;
        line.Opacity = 1.0f;
        line.StrokeColor = UIColor.Black.CGColor;
        line.LineWidth = 1.0f;

        this.gradientLayer.AddSublayer (line);
    }

    [Export ("layerClass")]
    public static Class LayerClass ()
    {
        // use a different Core Animation layer for its backing store
        // normally a CALayer is used for a UIView
        return new Class (typeof(CAGradientLayer));
    }

    public override void LayoutSubviews ()
    {
        PointF pointA = new PointF (0, this.Bounds.Height-2);
        PointF pointB = new PointF (this.Bounds.Width, this.Bounds.Height-2);
        UIBezierPath linePath = new UIBezierPath ();
        linePath.MoveTo (pointA);
        linePath.AddLineTo(pointB);

        line.Path = linePath.CGPath;

        base.LayoutSubviews ();
    }
}

This seems to do what I want except that the line is not a thin hairline. Is CAShapeLayer inappropriate for this?

Solution:

Of course it would be possible to use CAShapeLayer, but if I have to setup things in drawRect, then I completely draw my line there. Now the solution for me does look like the following:

public class GradientView : UIView
{
    // accessors
    private CAShapeLayer line;

    private CAGradientLayer gradientLayer {
        // read-only
        get { return (CAGradientLayer)this.Layer; }
    }

    public CGColor[] Colors {
        // set the colors of the gradient layer
        get { return this.gradientLayer.Colors; }
        set { this.gradientLayer.Colors = value; }
    }

    public GradientView()
    {
        this.BackgroundColor = UIColor.Clear;
    }

    [Export ("layerClass")]
    public static Class LayerClass ()
    {
        // use a different Core Animation layer for its backing store
        // normally a CALayer is used for a UIView
        return new Class (typeof(CAGradientLayer));
    }

    public override void Draw (RectangleF rect)
    {
        base.Draw (rect);

        // get graphics context
        CGContext context = UIGraphics.GetCurrentContext ();

        // start point
        context.MoveTo (0, rect.Height);

        // end point
        context.AddLineToPoint (rect.Width, rect.Height);

        context.SetLineWidth (1.0f);

        context.SetShouldAntialias(false);

        // draw the path
        context.DrawPath (CGPathDrawingMode.Stroke);
    }
testing
  • 19,681
  • 50
  • 236
  • 417

1 Answers1

1

Using a CAGradientLayer as the backing layer of your view does not prevent you from adding another CAShapeLayer inside your view upon initialization. Then you can use this shape layer to draw your straight line, by setting its path property accordingly (and recalculate it any time the bounds change, in -layoutSubviews, to react correctly to orientation/layout changes).

Cyrille
  • 25,014
  • 12
  • 67
  • 90
  • Thanks for your response. I tried what you said and it works. One question though: How can I draw a thin harline with `CAShapeLayer`? – testing Dec 09 '14 at 12:28
  • Many questions on SO already address this issue: http://stackoverflow.com/questions/21515451/draw-a-line-with-a-calayer , http://stackoverflow.com/questions/21515451/draw-a-line-with-a-calayer … – Cyrille Dec 09 '14 at 13:14
  • Another question: Why does the `UIView` get a black background when I implement `drawRect`? Seems that I can't turn off anti-aliasing without getting the current graphics context (e.g. [disable anti-aliasing for UIBezierPath](http://stackoverflow.com/questions/15417410/disable-anti-aliasing-for-uibezierpath)). But as I know the current graphics context is only available in `drawRect`, which I again can't implement. – testing Dec 09 '14 at 13:18
  • 1
    Again, this is another question: http://stackoverflow.com/questions/19555889/black-background-in-uiview – Cyrille Dec 09 '14 at 13:20
  • Setting a clear background color and disabling anti-aliasing seems to work. Thank you again. – testing Dec 09 '14 at 13:39