当前,在我的SpriteKit应用程序中,我将一个新的NSLabel节点添加到另一个具有弧形的节点中,例如:
name:"InternalServerErrorException"
requestId:"be9d8ffb-e0f0-11e8-891d-e5fc10d60fa0"
retryable:true
stack:"InternalServerErrorException: Internal server error\n at Object.extractError (/Users/develop/projects/nodejs/notifier-service/node_modules/aws-sdk/lib/protocol/json.js:48:27)\n at Request.extractError (/Users/develop/projects/nodejs/notifier-service/node_modules/aws-sdk/lib/protocol/rest_json.js:52:8)\n at Request.callListeners (/Users/develop/projects/nodejs/notifier-service/node_modules/aws-sdk/lib/sequential_executor.js:106:20)\n at Request.emit (/Users/develop/projects/nodejs/notifier-service/node_modules/aws-sdk/lib/sequential_executor.js:78:10)\n at Request.emit (/Users/develop/projects/nodejs/notifier-service/node_modules/aws-sdk/lib/request.js:683:14)\n at Request.transition (/Users/develop/projects/nodejs/notifier-service/node_modules/aws-sdk/lib/request.js:22:10)\n at AcceptorStateMachine.runTo (/Users/develop/projects/nodejs/notifier-service/node_modules/aws-sdk/lib/state_machine.js:14:12)\n at /Users/develop/p...
statusCode:500
但是添加后看起来像这样
有没有办法让我可以将NSLabel放置在圆弧内,使其呈圆弧状放置在圆弧内? 经过几个小时的搜索,我真的找不到任何有用的东西,因此希望您能向我展示路径
PS我要的是MacOS,而不是iOS。
答案 0 :(得分:2)
Apple在2013年发布了Objective-C示例代码:
Drawing Along a Path Using Core Text with Cocoa
CoreTextArcCocoa演示了如何使用Core Text在Cocoa应用程序中沿弧线绘制文本。它还说明了如何使用Cocoa字体面板来接收可供Core Text用来选择用于绘图的字体的字体设置。
您也许可以使它适应您的需求。请记住,可以通过在桥接头文件中包含头文件来从Swift调用Objective-C代码。
但是,如果标签文本在运行时没有更改(或只有几个可能的值),则仅使用图像编辑器创建弯曲文本的图像可能会更容易。
如果到示例项目的链接断开,这是头文件“ APLCoreTextArcView.h”的相关部分:
#import <Cocoa/Cocoa.h>
@interface APLCoreTextArcView : NSView
@property (nonatomic) NSFont *font;
@property (nonatomic) NSString *string;
@property (readonly, nonatomic) NSAttributedString *attributedString;
@property (nonatomic) CGFloat radius;
@property (nonatomic) BOOL showsGlyphBounds;
@property (nonatomic) BOOL showsLineMetrics;
@property (nonatomic) BOOL dimsSubstitutedGlyphs;
@end
这是实现文件“ APLCoreTextArcView.m”的相关部分:
#import "APLCoreTextArcView.h"
#import <AssertMacros.h>
#define ARCVIEW_DEFAULT_FONT_NAME @"Didot"
#define ARCVIEW_DEFAULT_FONT_SIZE 64.0
#define ARCVIEW_DEFAULT_RADIUS 150.0
typedef struct GlyphArcInfo {
CGFloat width;
CGFloat angle; // in radians
} GlyphArcInfo;
static void PrepareGlyphArcInfo(CTLineRef line, CFIndex glyphCount, GlyphArcInfo *glyphArcInfo)
{
NSArray *runArray = (__bridge NSArray *)CTLineGetGlyphRuns(line);
// Examine each run in the line, updating glyphOffset to track how far along the run is in terms of glyphCount.
CFIndex glyphOffset = 0;
for (id run in runArray) {
CFIndex runGlyphCount = CTRunGetGlyphCount((__bridge CTRunRef)run);
// Ask for the width of each glyph in turn.
CFIndex runGlyphIndex = 0;
for (; runGlyphIndex < runGlyphCount; runGlyphIndex++) {
glyphArcInfo[runGlyphIndex + glyphOffset].width = CTRunGetTypographicBounds((__bridge CTRunRef)run, CFRangeMake(runGlyphIndex, 1), NULL, NULL, NULL);
}
glyphOffset += runGlyphCount;
}
double lineLength = CTLineGetTypographicBounds(line, NULL, NULL, NULL);
CGFloat prevHalfWidth = glyphArcInfo[0].width / 2.0;
glyphArcInfo[0].angle = (prevHalfWidth / lineLength) * M_PI;
// Divide the arc into slices such that each one covers the distance from one glyph's center to the next.
CFIndex lineGlyphIndex = 1;
for (; lineGlyphIndex < glyphCount; lineGlyphIndex++) {
CGFloat halfWidth = glyphArcInfo[lineGlyphIndex].width / 2.0;
CGFloat prevCenterToCenter = prevHalfWidth + halfWidth;
glyphArcInfo[lineGlyphIndex].angle = (prevCenterToCenter / lineLength) * M_PI;
prevHalfWidth = halfWidth;
}
}
@implementation APLCoreTextArcView
- (id)initWithFrame:(NSRect)frame {
self = [super initWithFrame:frame];
if (self) {
_font = [NSFont fontWithName:ARCVIEW_DEFAULT_FONT_NAME size:ARCVIEW_DEFAULT_FONT_SIZE];
_string = @"Curvaceous Type";
_radius = ARCVIEW_DEFAULT_RADIUS;
_showsGlyphBounds = NO;
_showsLineMetrics = NO;
_dimsSubstitutedGlyphs = NO;
}
return self;
}
- (void)drawRect:(NSRect)rect {
// Don't draw if we don't have a font or string
if (self.font == NULL || self.string == NULL)
return;
// Initialize the text matrix to a known value
CGContextRef context = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
CGContextSetTextMatrix(context, CGAffineTransformIdentity);
// Draw a white background
[[NSColor whiteColor] set];
NSRectFill(rect);
CTLineRef line = CTLineCreateWithAttributedString((__bridge CFAttributedStringRef)self.attributedString);
assert(line != NULL);
CFIndex glyphCount = CTLineGetGlyphCount(line);
if (glyphCount == 0) {
CFRelease(line);
return;
}
GlyphArcInfo * glyphArcInfo = (GlyphArcInfo*)calloc(glyphCount, sizeof(GlyphArcInfo));
PrepareGlyphArcInfo(line, glyphCount, glyphArcInfo);
// Move the origin from the lower left of the view nearer to its center.
CGContextSaveGState(context);
CGContextTranslateCTM(context, CGRectGetMidX(NSRectToCGRect(rect)), CGRectGetMidY(NSRectToCGRect(rect)) - self.radius / 2.0);
// Stroke the arc in red for verification.
CGContextBeginPath(context);
CGContextAddArc(context, 0.0, 0.0, self.radius, M_PI, 0.0, 1);
CGContextSetRGBStrokeColor(context, 1.0, 0.0, 0.0, 1.0);
CGContextStrokePath(context);
// Rotate the context 90 degrees counterclockwise.
CGContextRotateCTM(context, M_PI_2);
/*
Now for the actual drawing. The angle offset for each glyph relative to the previous glyph has already been calculated; with that information in hand, draw those glyphs overstruck and centered over one another, making sure to rotate the context after each glyph so the glyphs are spread along a semicircular path.
*/
CGPoint textPosition = CGPointMake(0.0, self.radius);
CGContextSetTextPosition(context, textPosition.x, textPosition.y);
CFArrayRef runArray = CTLineGetGlyphRuns(line);
CFIndex runCount = CFArrayGetCount(runArray);
CFIndex glyphOffset = 0;
CFIndex runIndex = 0;
for (; runIndex < runCount; runIndex++) {
CTRunRef run = (CTRunRef)CFArrayGetValueAtIndex(runArray, runIndex);
CFIndex runGlyphCount = CTRunGetGlyphCount(run);
Boolean drawSubstitutedGlyphsManually = false;
CTFontRef runFont = CFDictionaryGetValue(CTRunGetAttributes(run), kCTFontAttributeName);
/*
Determine if we need to draw substituted glyphs manually. Do so if the runFont is not the same as the overall font.
*/
if (self.dimsSubstitutedGlyphs && ![self.font isEqual:(__bridge NSFont *)runFont]) {
drawSubstitutedGlyphsManually = true;
}
CFIndex runGlyphIndex = 0;
for (; runGlyphIndex < runGlyphCount; runGlyphIndex++) {
CFRange glyphRange = CFRangeMake(runGlyphIndex, 1);
CGContextRotateCTM(context, -(glyphArcInfo[runGlyphIndex + glyphOffset].angle));
// Center this glyph by moving left by half its width.
CGFloat glyphWidth = glyphArcInfo[runGlyphIndex + glyphOffset].width;
CGFloat halfGlyphWidth = glyphWidth / 2.0;
CGPoint positionForThisGlyph = CGPointMake(textPosition.x - halfGlyphWidth, textPosition.y);
// Glyphs are positioned relative to the text position for the line, so offset text position leftwards by this glyph's width in preparation for the next glyph.
textPosition.x -= glyphWidth;
CGAffineTransform textMatrix = CTRunGetTextMatrix(run);
textMatrix.tx = positionForThisGlyph.x;
textMatrix.ty = positionForThisGlyph.y;
CGContextSetTextMatrix(context, textMatrix);
if (!drawSubstitutedGlyphsManually) {
CTRunDraw(run, context, glyphRange);
}
else {
/*
We need to draw the glyphs manually in this case because we are effectively applying a graphics operation by setting the context fill color. Normally we would use kCTForegroundColorAttributeName, but this does not apply as we don't know the ranges for the colors in advance, and we wanted demonstrate how to manually draw.
*/
CGFontRef cgFont = CTFontCopyGraphicsFont(runFont, NULL);
CGGlyph glyph;
CGPoint position;
CTRunGetGlyphs(run, glyphRange, &glyph);
CTRunGetPositions(run, glyphRange, &position);
CGContextSetFont(context, cgFont);
CGContextSetFontSize(context, CTFontGetSize(runFont));
CGContextSetRGBFillColor(context, 0.25, 0.25, 0.25, 0.5);
CGContextShowGlyphsAtPositions(context, &glyph, &position, 1);
CFRelease(cgFont);
}
// Draw the glyph bounds
if ((self.showsGlyphBounds) != 0) {
CGRect glyphBounds = CTRunGetImageBounds(run, context, glyphRange);
CGContextSetRGBStrokeColor(context, 0.0, 0.0, 1.0, 1.0);
CGContextStrokeRect(context, glyphBounds);
}
// Draw the bounding boxes defined by the line metrics
if ((self.showsLineMetrics) != 0) {
CGRect lineMetrics;
CGFloat ascent, descent;
CTRunGetTypographicBounds(run, glyphRange, &ascent, &descent, NULL);
// The glyph is centered around the y-axis
lineMetrics.origin.x = -halfGlyphWidth;
lineMetrics.origin.y = positionForThisGlyph.y - descent;
lineMetrics.size.width = glyphWidth;
lineMetrics.size.height = ascent + descent;
CGContextSetRGBStrokeColor(context, 0.0, 1.0, 0.0, 1.0);
CGContextStrokeRect(context, lineMetrics);
}
}
glyphOffset += runGlyphCount;
}
CGContextRestoreGState(context);
free(glyphArcInfo);
CFRelease(line);
}
- (NSAttributedString *)attributedString {
// Create an attributed string with the current font and string.
assert(self.font != nil);
assert(self.string != nil);
// Create our attributes.
NSDictionary *attributes = @{NSFontAttributeName: self.font, NSLigatureAttributeName: @0};
assert(attributes != nil);
// Create the attributed string.
NSAttributedString *attrString = [[NSAttributedString alloc] initWithString:self.string attributes:attributes];
return attrString;
}
@end