Drawing Circles In Xamarin.iOS (Xamarin Monotouch)

2019-03-26 01:51发布

问题:

As I'm very new to Xamarin World and new to its controls. I would like to add Circles to Show Work Progress in my mono touch app. For showing Progress I have to mark an Arc in the circle. And if possible any one can help with me a sample code. Awaiting an answer, Thanks a lot in advance.

回答1:

using System;
    using UIKit;
    using CoreGraphics;

    namespace CircleTest.Touch
    {
        public class CircleGraph : UIView
        {
            int _radius = 10;
            int _lineWidth = 10;
            nfloat _degrees = 0.0f;
            UIColor _backColor = UIColor.FromRGB(46, 60, 76);
            UIColor _frontColor = UIColor.FromRGB(234, 105, 92);
            //FromRGB (234, 105, 92);

        public CircleGraph (CGRect frame, int lineWidth, nfloat degrees)
        {
            _lineWidth = lineWidth; 
            _degrees = degrees; 
            this.Frame = new CGRect(frame.X, frame.Y, frame.Width, frame.Height);
            this.BackgroundColor = UIColor.Clear; 

        }

        public CircleGraph (CGRect frame, int lineWidth, UIColor backColor, UIColor frontColor)
        {
            _lineWidth = lineWidth;
            this.Frame = new CGRect(frame.X, frame.Y, frame.Width, frame.Height);
            this.BackgroundColor = UIColor.Clear;

        }

        public override void Draw (CoreGraphics.CGRect rect)
        {
            base.Draw (rect);

            using (CGContext g = UIGraphics.GetCurrentContext ()) {
                _radius = (int)( (this.Bounds.Width) / 3) - 8;
                DrawGraph(g, this.Bounds.GetMidX(), this.Bounds.GetMidY());
            };
        }

        public void DrawGraph(CGContext g,nfloat x0,nfloat y0) {
            g.SetLineWidth (_lineWidth);

            // Draw background circle
            CGPath path = new CGPath ();
            _backColor.SetStroke ();
            path.AddArc (x0, y0, _radius, 0, 2.0f * (float)Math.PI, true);
            g.AddPath (path);
            g.DrawPath (CGPathDrawingMode.Stroke);

            // Draw overlay circle
            var pathStatus = new CGPath ();
            _frontColor.SetStroke ();
            pathStatus.AddArc(x0, y0, _radius, 0, _degrees * (float)Math.PI, false);
            g.AddPath (pathStatus);
            g.DrawPath (CGPathDrawingMode.Stroke);
        }
    }
}

Actually this is what iam actually supposed to do. Its working for me

This is how it look like. U can create it like a class file and simply u can assign to a UIView. For more reference you can use this sample project Pi Graph

[EDIT]: The Draw method originally passed the View.Frame x,y to the DrawGraph method. This should be View.Bounds (modified above to reflect this). Remember that the frame x,y is in reference to the containing superview and bounds is referenced to the current view. This would have worked if the view was added at 0,0 but once you start moving around the UI it disappears. When the arcs are drawn the values for x,y passed to AddArc need to be in reference to the current view not the parent superview.



回答2:

Drawing a circle on a GLContext isn't that hard to do and is the same as you would do in Objective-C or Swift.

I assume you want to create your own view which you can reuse. To do so, simply inherit from UIView:

public class CircleView : UIView
{
}

Now to draw anything in your new custom view you want to override the Draw method:

public override void Draw(RectangleF rect)
{
    base.Draw(rect);
    // draw stuff in here
}

To draw stuff you need to get hold of the current context from UIGraphics, which can be done like so:

using (var gctx = UIGraphics.GetCurrentContext())
{
    // use gctx to draw stuff
}

The CGContext you get back, is very similar to Canvas on Android for instance. It has helper methods to draw arcs, circles, rectangles, points and much more.

So to draw a simple circle in that context, you do:

gctx.SetFillColor(UIColor.Cyan.CGColor);
gctx.AddEllipseInRect(rect);

So combine everything you get:

public class CircleView : UIView
{
    public override Draw(RectangleF rect)
    {
        base.Draw(rect);
        using (var gctx = UIGraphics.GetCurrentContext())
        {
            gctx.SetFillColor(UIColor.Cyan.CGColor);
            gctx.AddEllipseInRect(rect);
        }
    }
}

That is it! Well, not exactly, this is where you need to start think of how you want to draw your progress indicator. What I think would probably work is:

  1. Draw the back ground
  2. Draw the borders
  3. Calculate the degrees from the progress in percent
  4. Use the degrees to create an arc using gctx.AddArc(), which can take an angle and draw an arc.
  5. Draw the percentage as a string in the middle

To draw a string you will need to convert your string to a NSAttributedString then use CTLine to draw the text like:

using(var line = new CTLine(nsAttrString))
    line.Draw(gctx);


回答3:

Minor alterations to the answer provided by @SARATH as copying and pasting did not yield the desired result.

Changed _degrees to _percentComplete

Fixed overload constructor for changing colors by adding a param for percentComplete and added missing member variable assignments for _backColor and _frontColor

Added constant float value for drawing a full circle (FULL_CIRCLE)

Multiply _percentComplete by FULL_CIRCLE to get the end angle for both arcs (with different directions)

Calculation of the radius

  public class CircleGraph : UIView
  {
      const float FULL_CIRCLE = 2 * (float)Math.PI;
      int _radius = 10;
      int _lineWidth = 10;
      nfloat _percentComplete = 0.0f;
      UIColor _backColor = UIColor.LightGray; //UIColor.FromRGB(46, 60, 76);
      UIColor _frontColor = UIColor.Green; //UIColor.FromRGB(234, 105, 92);

      public CircleGraph(CGRect frame, int lineWidth, nfloat percentComplete)
      {
         _lineWidth = lineWidth;
         _percentComplete = percentComplete;
         this.Frame = new CGRect(frame.X, frame.Y, frame.Width, frame.Height);
         this.BackgroundColor = UIColor.Clear;

      }

      public CircleGraph(CGRect frame, int lineWidth, nfloat percentComplete, UIColor backColor, UIColor frontColor)
      {
         _lineWidth = lineWidth;
         _percentComplete = percentComplete;
         this.Frame = new CGRect(frame.X, frame.Y, frame.Width, frame.Height);
         this.BackgroundColor = UIColor.Clear;
         _backColor = backColor;
         _frontColor = frontColor;
      }

      public override void Draw(CoreGraphics.CGRect rect)
      {
         base.Draw(rect);

         using (CGContext g = UIGraphics.GetCurrentContext())
         {
            var diameter = Math.Min(this.Bounds.Width, this.Bounds.Height);
            _radius = (int)(diameter / 2) - _lineWidth;

            DrawGraph(g, this.Bounds.GetMidX(), this.Bounds.GetMidY());
         };
      }

      public void DrawGraph(CGContext g, nfloat x, nfloat y)
      {
         g.SetLineWidth(_lineWidth);

         // Draw background circle
         CGPath path = new CGPath();
         _backColor.SetStroke();
         path.AddArc(x, y, _radius, 0, _percentComplete * FULL_CIRCLE, true);
         g.AddPath(path);
         g.DrawPath(CGPathDrawingMode.Stroke);

         // Draw overlay circle
         var pathStatus = new CGPath();
         _frontColor.SetStroke();

         // Same Arc params except direction so colors don't overlap
         pathStatus.AddArc(x, y, _radius, 0, _percentComplete * FULL_CIRCLE, false);
         g.AddPath(pathStatus);
         g.DrawPath(CGPathDrawingMode.Stroke);
      }
   }

Example

var circleGraph = new CircleGraph(circleGraphView.Frame, 20, 0.75f);

CircleGraph at 75%