Растеризация
Что, вероятно, здесь происходит, так это то, что он решает, что должен отображать textLayer в пикселях. Обратите внимание на предупреждение для shouldRasterize в справочнике по классам CALayer:
Когда значение этого свойства равно NO, слой по возможности компонуется непосредственно в место назначения. Слой все еще может быть растрирован до композитинга, если этого требуют определенные функции модели композитинга (например, включение фильтров).
Итак, CATextLayer может внезапно решить растрировать. Он решает растрировать, если это подслой повернутого слоя. Так что не делайте этого.
Односторонние слои
Это возвращает вас к вашему решению, которое вызывает перевернутый текст. Вы можете предотвратить это, отключив doubleSided
на текстовых слоях. Ваши знаки теперь будут пустыми на дальней стороне, поэтому добавьте второй текстовый слой, повернутый на 180 градусов относительно первого.
Объявите два текстовых слоя:
@property (retain) CAShapeLayer *pathLayer;
@property (retain) CATextLayer *textLayerFront;
@property (retain) CATextLayer *textLayerBack;
Затем инициализируйте их как односторонние, повернув задний слой на 180 градусов:
CAShapeLayer *pathLayer = [CAShapeLayer layer];
// Also need to store a UIBezierPath in the pathLayer.
CATextLayer *textLayerFront = [CATextLayer layer];
textLayerFront.doubleSided = NO;
textLayerFront.string = @"Front";
textLayerFront.contentsScale = [[UIScreen mainScreen] scale];
CATextLayer *textLayerBack = [CATextLayer layer];
textLayerBack.doubleSided = NO;
// Eventually both sides will have the same text, but for demonstration purposes we will label them differently.
textLayerBack.string = @"Back";
// Rotate the back layer 180 degrees relative to the front layer.
textLayerBack.transform = CATransform3DRotate(textLayerBack.transform, M_PI, 0, 1, 0);
textLayerBack.contentsScale = [[UIScreen mainScreen] scale];
// Make all the layers siblings. These means they must all be rotated independently of each other.
// The layers can flicker if their Z position is close to the background, so move them forward.
// This will not work if the main layer has a perspective transform on it.
textLayerFront.zPosition = 256;
textLayerBack.zPosition = 256;
// It would make sense to make the text layers siblings of the path layer, but this seems to mean they get pre-rendered, blurring them.
[self.layer addSublayer:pathLayer];
[self.layer addSublayer:textLayerBack];
[self.layer addSublayer:textLayerFront];
// Store the layers constructed at this time for later use.
[self setTextLayerFront:textLayerFront];
[self setTextLayerBack:textLayerBack];
[self setPathLayer:pathLayer];
Затем вы можете вращать слои. Они будут отображаться правильно, если вы всегда поворачиваете их на одну и ту же величину.
CGFloat angle = M_PI;
self.pathLayer.transform = CATransform3DRotate(self.pathLayer.transform, angle, 0, 1, 0);
self.textLayerFront.transform = CATransform3DRotate(self.textLayerFront.transform, angle, 0, 1, 0);
self.textLayerBack.transform = CATransform3DRotate(self.textLayerBack.transform, angle, 0, 1, 0);
Затем вы обнаружите, что можете поворачивать свой знак под любым углом, в то время как текст остается четким.
Текст в путь
Существует альтернатива, если вам действительно нужно манипулировать отображением текста таким образом, чтобы CATextLayer растеризировался: преобразовать текст в представление UIBezierPath
. Затем его можно поместить в CAShapeLayer. Для этого необходимо глубоко погрузиться в основной текст, но результаты будут впечатляющими. Например, вы можете анимировать рисуемый текст а>.
// - (UIBezierPath*) bezierPathWithString:(NSString*) string font:(UIFont*) font inRect:(CGRect) rect;
// Requires CoreText.framework
// This creates a graphical version of the input screen, line wrapped to the input rect.
// Core Text involves a whole hierarchy of objects, all requiring manual management.
- (UIBezierPath*) bezierPathWithString:(NSString*) string font:(UIFont*) font inRect:(CGRect) rect;
{
UIBezierPath *combinedGlyphsPath = nil;
CGMutablePathRef combinedGlyphsPathRef = CGPathCreateMutable();
if (combinedGlyphsPathRef)
{
// It would be easy to wrap the text into a different shape, including arbitrary bezier paths, if needed.
UIBezierPath *frameShape = [UIBezierPath bezierPathWithRect:rect];
// If the font name wasn't found while creating the font object, the result is a crash.
// Avoid this by falling back to the system font.
CTFontRef fontRef;
if ([font fontName])
fontRef = CTFontCreateWithName((__bridge CFStringRef) [font fontName], [font pointSize], NULL);
else if (font)
fontRef = CTFontCreateUIFontForLanguage(kCTFontUserFontType, [font pointSize], NULL);
else
fontRef = CTFontCreateUIFontForLanguage(kCTFontUserFontType, [UIFont systemFontSize], NULL);
if (fontRef)
{
CGPoint basePoint = CGPointMake(0, CTFontGetAscent(fontRef));
CFStringRef keys[] = { kCTFontAttributeName };
CFTypeRef values[] = { fontRef };
CFDictionaryRef attributesRef = CFDictionaryCreate(NULL, (const void **)&keys, (const void **)&values,
sizeof(keys) / sizeof(keys[0]), &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
if (attributesRef)
{
CFAttributedStringRef attributedStringRef = CFAttributedStringCreate(NULL, (__bridge CFStringRef) string, attributesRef);
if (attributedStringRef)
{
CTFramesetterRef frameSetterRef = CTFramesetterCreateWithAttributedString(attributedStringRef);
if (frameSetterRef)
{
CTFrameRef frameRef = CTFramesetterCreateFrame(frameSetterRef, CFRangeMake(0,0), [frameShape CGPath], NULL);
if (frameRef)
{
CFArrayRef lines = CTFrameGetLines(frameRef);
CFIndex lineCount = CFArrayGetCount(lines);
CGPoint lineOrigins[lineCount];
CTFrameGetLineOrigins(frameRef, CFRangeMake(0, lineCount), lineOrigins);
for (CFIndex lineIndex = 0; lineIndex<lineCount; lineIndex++)
{
CTLineRef lineRef = CFArrayGetValueAtIndex(lines, lineIndex);
CGPoint lineOrigin = lineOrigins[lineIndex];
CFArrayRef runs = CTLineGetGlyphRuns(lineRef);
CFIndex runCount = CFArrayGetCount(runs);
for (CFIndex runIndex = 0; runIndex<runCount; runIndex++)
{
CTRunRef runRef = CFArrayGetValueAtIndex(runs, runIndex);
CFIndex glyphCount = CTRunGetGlyphCount(runRef);
CGGlyph glyphs[glyphCount];
CGSize glyphAdvances[glyphCount];
CGPoint glyphPositions[glyphCount];
CFRange runRange = CFRangeMake(0, glyphCount);
CTRunGetGlyphs(runRef, CFRangeMake(0, glyphCount), glyphs);
CTRunGetPositions(runRef, runRange, glyphPositions);
CTFontGetAdvancesForGlyphs(fontRef, kCTFontDefaultOrientation, glyphs, glyphAdvances, glyphCount);
for (CFIndex glyphIndex = 0; glyphIndex<glyphCount; glyphIndex++)
{
CGGlyph glyph = glyphs[glyphIndex];
// For regular UIBezierPath drawing, we need to invert around the y axis.
CGAffineTransform glyphTransform = CGAffineTransformMakeTranslation(lineOrigin.x+glyphPositions[glyphIndex].x, rect.size.height-lineOrigin.y-glyphPositions[glyphIndex].y);
glyphTransform = CGAffineTransformScale(glyphTransform, 1, -1);
CGPathRef glyphPathRef = CTFontCreatePathForGlyph(fontRef, glyph, &glyphTransform);
if (glyphPathRef)
{
// Finally carry out the appending.
CGPathAddPath(combinedGlyphsPathRef, NULL, glyphPathRef);
CFRelease(glyphPathRef);
}
basePoint.x += glyphAdvances[glyphIndex].width;
basePoint.y += glyphAdvances[glyphIndex].height;
}
}
basePoint.x = 0;
basePoint.y += CTFontGetAscent(fontRef) + CTFontGetDescent(fontRef) + CTFontGetLeading(fontRef);
}
CFRelease(frameRef);
}
CFRelease(frameSetterRef);
}
CFRelease(attributedStringRef);
}
CFRelease(attributesRef);
}
CFRelease(fontRef);
}
// Casting a CGMutablePathRef to a CGPathRef seems to be the only way to convert what was just built into a UIBezierPath.
combinedGlyphsPath = [UIBezierPath bezierPathWithCGPath:(CGPathRef) combinedGlyphsPathRef];
CGPathRelease(combinedGlyphsPathRef);
}
return combinedGlyphsPath;
}
Вот вращающийся контурный текст, созданный описанным выше методом. Также можно было добавить перспективу, не становясь очевидными позиции z текстовых слоев.
person
Dondragmer
schedule
15.04.2012
textLayer_
является подслоемpathLayer_
, нужно ли вращать их оба? Должен лиtextLayer_
просто вращаться по пути, к которому он прикреплен? - person Dondragmer   schedule 14.04.2012