Кривой текст на существующем круге - PullRequest
54 голосов
/ 01 октября 2010

Для приложения, которое я строю, я нарисовал 2 круга. Один немного больше другого. Я хочу изогнуть текст между этими строками для создания кругового меню.

Я прочитал большую часть материала о изгибе текста, в котором вам нужно разделить текст на символы и нарисовать каждый символ отдельно с правильным углом (вращая контекст, на котором вы рисуете).

Я просто не могу обернуться, чтобы узнать правильные углы и положения для моих персонажей.

Я включил скриншот того, как выглядит меню на данный момент. Только тексты, которые я добавил, загружаются из изображения в UIImageView.

alt text

Я надеюсь, что кто-то может подсказать мне, как я могу рисовать текст в белом круге в определенных точках.

EDIT: Хорошо, я сейчас нахожусь в этой точке:

alt text

Я использую следующий код:

- (UIImage*) createMenuRingWithFrame:(CGRect)frame
{
    CGRect imageSize = CGRectMake(0,0,300,300);
    float perSectionDegrees = 360 / [sections count];
    float totalRotation = 90;
    char* fontName = (char*)[self.menuItemsFont.fontName cStringUsingEncoding:NSASCIIStringEncoding];

    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
    CGContextRef context = CGBitmapContextCreate(NULL, imageSize.size.width, imageSize.size.height, 8, 4 * imageSize.size.width, colorSpace, kCGImageAlphaPremultipliedFirst);

    CGContextSetTextMatrix(context, CGAffineTransformIdentity);
    CGContextSelectFont(context, fontName, 18, kCGEncodingMacRoman);

    CGContextSetRGBFillColor(context, 0, 0, 0, 1);

    CGPoint centerPoint = CGPointMake(imageSize.size.width / 2, imageSize.size.height / 2);
    double radius = (frame.size.width / 2);

    CGContextStrokeEllipseInRect(context, CGRectMake(centerPoint.x - (frame.size.width / 2), centerPoint.y - (frame.size.height / 2), frame.size.width, frame.size.height));

    for (int index = 0; index < [sections count]; index++)
    {
        NSString* menuItemText = [sections objectAtIndex:index];
        CGSize textSize = [menuItemText sizeWithFont:self.menuItemsFont];
        char* menuItemTextChar = (char*)[menuItemText cStringUsingEncoding:NSASCIIStringEncoding];

        float x = centerPoint.x + radius * cos(degreesToRadians(totalRotation));
        float y = centerPoint.y + radius * sin(degreesToRadians(totalRotation));

        CGContextSaveGState(context);

        CGContextTranslateCTM(context, x, y);
        CGContextRotateCTM(context, degreesToRadians(totalRotation - 90));
        CGContextShowTextAtPoint(context, 0 - (textSize.width / 2), 0 - (textSize.height / 2), menuItemTextChar, strlen(menuItemTextChar));

        CGContextRestoreGState(context);

        totalRotation += perSectionDegrees;
    }

    CGImageRef contextImage = CGBitmapContextCreateImage(context);

    CGContextRelease(context);
    CGColorSpaceRelease(colorSpace);

    return [UIImage imageWithCGImage:contextImage];
}

Вот переменные, которые я там использую:

NSArray* sections = [[NSArray alloc] initWithObjects:@"settings", @"test", @"stats", @"nog iets", @"woei", @"woei2", nil];
self.menuItemsFont = [UIFont fontWithName:@"VAGRounded-Bold" size:18];

Вращение слов кажется правильным, размещение тоже. Теперь мне нужно как-то выяснить, при каком повороте должны быть буквы (и их координаты). Я мог бы помочь с этим.

Редактировать: Исправлено! Проверьте следующий код!

- (void) drawStringAtContext:(CGContextRef) context string:(NSString*) text atAngle:(float) angle withRadius:(float) radius
{
    CGSize textSize = [text sizeWithFont:self.menuItemsFont];

    float perimeter = 2 * M_PI * radius;
    float textAngle = textSize.width / perimeter * 2 * M_PI;

    angle += textAngle / 2;

    for (int index = 0; index < [text length]; index++)
    {
        NSRange range = {index, 1};
        NSString* letter = [text substringWithRange:range];     
        char* c = (char*)[letter cStringUsingEncoding:NSASCIIStringEncoding];
        CGSize charSize = [letter sizeWithFont:self.menuItemsFont];

        NSLog(@"Char %@ with size: %f x %f", letter, charSize.width, charSize.height);

        float x = radius * cos(angle);
        float y = radius * sin(angle);

        float letterAngle = (charSize.width / perimeter * -2 * M_PI);

        CGContextSaveGState(context);
        CGContextTranslateCTM(context, x, y);
        CGContextRotateCTM(context, (angle - 0.5 * M_PI));
        CGContextShowTextAtPoint(context, 0, 0, c, strlen(c));
        CGContextRestoreGState(context);

        angle += letterAngle;
    }
}

- (UIImage*) createMenuRingWithFrame:(CGRect)frame
{
    CGPoint centerPoint = CGPointMake(frame.size.width / 2, frame.size.height / 2);
    char* fontName = (char*)[self.menuItemsFont.fontName cStringUsingEncoding:NSASCIIStringEncoding];

    CGFloat* ringColorComponents = (float*)CGColorGetComponents(ringColor.CGColor);
    CGFloat* textColorComponents = (float*)CGColorGetComponents(textColor.CGColor);

    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
    CGContextRef context = CGBitmapContextCreate(NULL, frame.size.width, frame.size.height, 8, 4 * frame.size.width, colorSpace, kCGImageAlphaPremultipliedFirst);

    CGContextSetTextMatrix(context, CGAffineTransformIdentity);

    CGContextSelectFont(context, fontName, 18, kCGEncodingMacRoman);
    CGContextSetRGBStrokeColor(context, ringColorComponents[0], ringColorComponents[1], ringColorComponents[2], ringAlpha);
    CGContextSetLineWidth(context, ringWidth);  

    CGContextStrokeEllipseInRect(context, CGRectMake(ringWidth, ringWidth, frame.size.width - (ringWidth * 2), frame.size.height - (ringWidth * 2)));
    CGContextSetRGBFillColor(context, textColorComponents[0], textColorComponents[1], textColorComponents[2], textAlpha);

    CGContextSaveGState(context);
    CGContextTranslateCTM(context, centerPoint.x, centerPoint.y);

    float angleStep = 2 * M_PI / [sections count];
    float angle = degreesToRadians(90);

    textRadius = textRadius - 12;

    for (NSString* text in sections)
    {
        [self drawStringAtContext:context string:text atAngle:angle withRadius:textRadius];
        angle -= angleStep;
    }

    CGContextRestoreGState(context);

    CGImageRef contextImage = CGBitmapContextCreateImage(context);

    CGContextRelease(context);
    CGColorSpaceRelease(colorSpace);

    [self saveImage:[UIImage imageWithCGImage:contextImage] withName:@"test.png"];
    return [UIImage imageWithCGImage:contextImage];

}

Ответы [ 12 ]

0 голосов
/ 18 сентября 2015

enter image description here

#import <Cocoa/Cocoa.h>

@interface CircleTextCell : NSCell {

}

@end

#import "CircleTextCell.h"

#define PI (3.141592653589793)

@implementation CircleTextCell

- (void)drawWithFrame: (NSRect)cellFrame inView: (NSView*)controlView
{
    NSAttributedString *str = [self attributedStringValue];
    NSSize stringSize = [str size];
    NSUInteger chars = [[str string] length];
    CGFloat radius = (stringSize.width + 5 * chars) / (2 * PI);
    CGFloat diameter = 2*radius;
    NSPoint scale = {1,1};
    if (diameter > cellFrame.size.width)
    {
        scale.x = cellFrame.size.width / diameter;
    }
    if (diameter > cellFrame.size.height)
    {
        scale.y = cellFrame.size.height / diameter;
    }
    NSAffineTransform *transform = [NSAffineTransform transform];
    NSAffineTransformStruct identity = [transform transformStruct];
    [transform scaleXBy: scale.x yBy: scale.y];
    [transform translateXBy: radius yBy: 0];
    [NSGraphicsContext saveGraphicsState];

    [transform concat];

    NSPoint origin = {0,0};
    CGFloat angleScale = 360 / (stringSize.width + (5 * chars));
    for (NSUInteger i=0 ; i<chars ; i++)
    {
        NSAttributedString *substr = 
            [str attributedSubstringFromRange: NSMakeRange(i, 1)];
        [substr drawAtPoint: origin];
        [transform setTransformStruct: identity];
        CGFloat displacement = [substr size].width + 5;
        [transform translateXBy: displacement yBy: 0];
        [transform rotateByDegrees: angleScale * displacement];
        [transform concat];
    }
    [NSGraphicsContext restoreGraphicsState];
}
@end

#import <Cocoa/Cocoa.h>

@class CircleTextCell;
@interface CircleTextView : NSView {
    CircleTextCell *cell;
}

@end

#import "CircleTextView.h"
#import "CircleTextCell.h"

@implementation CircleTextView
- (void)awakeFromNib
{
    NSDictionary *attributes = 
        [NSDictionary dictionaryWithObject: [NSFont fontWithName: @"Zapfino"
                                                            size:32]
                                    forKey: NSFontAttributeName];
    NSAttributedString *str =
        [[NSAttributedString alloc] initWithString: @"Hello World!  This is a very long text string that will be wrapped into a circle by a cell drawn in a custom view"
                                        attributes: attributes];
    cell = [[CircleTextCell alloc] init];
    [cell setAttributedStringValue: str];
}
- (void)drawRect:(NSRect)rect 
{
    [[NSColor whiteColor] setFill];
    [NSBezierPath fillRect: rect];
    [cell drawWithFrame: [self bounds] inView: self];
}

@end
0 голосов
/ 18 декабря 2013

Ссылаясь на Али Сейман ответ :

Вы можете загрузить пример проекта, который использует CoreTextArcView: https://github.com/javenisme/CurvaView

Добавьте этот метод, чтобы уменьшить размер рамки просмотра, как и UILabel.

- (void)sizeToFit{
[super sizeToFit];

CGFloat width = ceilf( fabsf((self.radius*2)) + self.font.lineHeight) + 3.0;
CGRect f = self.frame;
f.size = CGSizeMake(width,width);
self.frame = f;
[self setNeedsDisplay];
}

Если кто-то может улучшить и высоту, добро пожаловать на добавление.

...