Nininea
Nininea

Reputation: 2729

Draw text along circular path in xamarin ios

I have found the answer in following link : Draw text along circular path in Swift for iOS

It seems fine for me. Now , I'm just trying to convert the same code in xamarin ios, but haven't succeeded yet.

 public class CircularText : UIView
{
    public override void Draw(CoreGraphics.CGRect rect)
    {
        base.Draw(rect);
        this.BackgroundColor = UIColor.Clear;
        var context = UIGraphics.GetCurrentContext();
        var size = this.Bounds.Size;

        context.TranslateCTM(size.Width / 2, size.Height / 2);
        context.ScaleCTM(1, -1);


        CentreArcPerpendicular(str: "Hello round world", context: context, radius: 100, angle: 0, color: UIColor.Red, font: UIFont.SystemFontOfSize(16), clockwise: true);
        CentreArcPerpendicular(str: "Anticlockwise", context: context, radius: 100, angle: (System.nfloat)(-Math.PI / 2), color: UIColor.Red, font: UIFont.SystemFontOfSize(16), clockwise: false);
        centre(str: "Hello flat world", context: context, radius: 0, angle: 0, color: UIColor.Yellow, font: UIFont.SystemFontOfSize(16), slantAngle: (System.nfloat)(Math.PI / 4));
    }

    void CentreArcPerpendicular(String str, CGContext context, nfloat radius, nfloat angle, UIColor color, UIFont font, bool clockwise)
    {
        var l = str.Length;
        char[] characters = str.ToCharArray();
        nfloat[] arcs = new nfloat[l];
        nfloat totalArc = 0;

        for (int i = 0; i < l; i++)
        {
            arcs[i] = chordToArc(1, radius);  
        }

        var direction = clockwise ? -1 : 1;
        var slantCorrection = clockwise ? -Math.PI / 2 : Math.PI;

        var thetaI = angle - direction * totalArc / 2;


        for (int i = 0; i < l; i++)
        {
            thetaI += direction * arcs[i] / 2;
            centre(str: characters[i].ToString(), context: context, radius: radius, angle: angle, color: color, font: font, slantAngle: (System.nfloat)(angle + slantCorrection));
            thetaI += direction * arcs[i] / 2;
        }
    }

    nfloat chordToArc(nfloat chord, nfloat radius)
    {
        return (nfloat)(2 * Math.Asin(chord / (2 * radius)));
    }

    void centre(String str, CGContext context, nfloat radius, nfloat angle, UIColor color, UIFont font, nfloat slantAngle)
    {
        //var attributes = [NSForegroundColorAttributeName: color,
        //NSFontAttributeName: font]

        context.SaveState();
        //    // Undo the inversion of the Y-axis (or the text goes backwards!)
        context.ScaleCTM(1, -1);
        // Move the origin to the centre of the text (negating the y-axis manually)
        context.TranslateCTM((System.nfloat)(radius * Math.Cos(angle)), (System.nfloat)(-(radius * Math.Sin(angle))));
        // Rotate the coordinate system
        context.RotateCTM(-slantAngle);
        // Calculate the width of the text
        var offset = str.Length;
        // Move the origin by half the size of the text

        //CGContextTranslateCTM(context, -offset.width / 2, -offset.height / 2)
        context.TranslateCTM(-20 / 2, 20 / 2); // Move the origin to the centre of the text (negating the y-axis manually)
        // Draw the text
        str.DrawString(new CGPoint(x: 0, y: 0), font);
        // Restore the context
        context.RestoreState();
    }
}

Some comments shows original lines, what it maybe not translated correctly... It shows just black view for me

Upvotes: 1

Views: 576

Answers (1)

Kevin Li
Kevin Li

Reputation: 2258

There're several mistakes when you convert the swift code to C#, I have corrected them in the below code snippet. The reason why it's black is that you didn't set the color for the text. You can use NSString.DrawString instead to achieve that.

It should like this:

        public override void Draw(CoreGraphics.CGRect rect)
        {
            base.Draw(rect);
            this.BackgroundColor = UIColor.Clear;
            var context = UIGraphics.GetCurrentContext();
            var size = this.Bounds.Size;

            context.TranslateCTM(size.Width / 2, size.Height / 2);
            context.ScaleCTM(1, -1);


            CentreArcPerpendicular(str: "Hello round world", context: context, radius: 100, angle: 0, color: UIColor.Red, font: UIFont.SystemFontOfSize(16), clockwise: true);
            CentreArcPerpendicular(str: "Anticlockwise", context: context, radius: 100, angle: (System.nfloat)(-Math.PI / 2), color: UIColor.Red, font: UIFont.SystemFontOfSize(16), clockwise: false);
            centre(str: "Hello flat world", context: context, radius: 0, angle: 0, color: UIColor.Yellow, font: UIFont.SystemFontOfSize(16), slantAngle: (System.nfloat)(Math.PI / 4));
        }

        void CentreArcPerpendicular(String str, CGContext context, nfloat radius, nfloat angle, UIColor color, UIFont font, bool clockwise)
        {
            var l = str.Length;
            char[] characters = str.ToCharArray();
            nfloat[] arcs = new nfloat[l];
            nfloat totalArc = 0;

            for (int i = 0; i < l; i++)
            {

                //Get the Size of the Char(Transfer to string to get the width)
                NSString s = new NSString(Char.ToString(characters[i]));
                CGSize size = s.GetSizeUsingAttributes(new UIStringAttributes(new NSDictionary(UIStringAttributeKey.Font, font)));

                arcs[i] = chordToArc(size.Width, radius);

                totalArc += arcs[i];

            }

            var direction = clockwise ? -1 : 1;
            var slantCorrection = clockwise ? -Math.PI / 2 : Math.PI / 2 ; //You lose the /2

            var thetaI = angle - direction * totalArc / 2;


            for (int i = 0; i < l; i++)
            {
                thetaI += direction * arcs[i] / 2;
                centre(str: characters[i].ToString(), context: context, radius: radius, angle: thetaI, color: color, font: font, slantAngle: (System.nfloat)(thetaI + slantCorrection)); // USe thetaI not angle !
                thetaI += direction * arcs[i] / 2;
            }
        }

        nfloat chordToArc(nfloat chord, nfloat radius)
        {
            return (nfloat)(2 * Math.Asin(chord / (2 * radius)));
        }

        void centre(String str, CGContext context, nfloat radius, nfloat angle, UIColor color, UIFont font, nfloat slantAngle)
        {
            // Set the text attributes
            UIStringAttributes attribute = new UIStringAttributes()
            {
                Font = font,
                ForegroundColor = color
            };

            NSString s = new NSString(str);

            // Save the context
            context.SaveState();
            // Undo the inversion of the Y-axis (or the text goes backwards!)
            context.ScaleCTM(1, -1);
            // Move the origin to the centre of the text (negating the y-axis manually)
            context.TranslateCTM((System.nfloat)(radius * Math.Cos(angle)), (System.nfloat)(-(radius * Math.Sin(angle))));
            // Rotate the coordinate system
            context.RotateCTM(-slantAngle);
            // Calculate the width of the text
            var offset = s.GetSizeUsingAttributes(attribute);
            // Move the origin by half the size of the text
            context.TranslateCTM(-offset.Width / 2, -offset.Height / 2); // Move the origin to the centre of the text (negating the y-axis manually)

            // Draw the text
            s.DrawString(new CGPoint(0, 0), attribute);//use NSString.DrawString, then it can add attribute parameter.
            // Restore the context
            context.RestoreState();
        }

It works fine like this:

enter image description here

Upvotes: 3

Related Questions