iOS 中的 3d 文字效果
Posted
技术标签:
【中文标题】iOS 中的 3d 文字效果【英文标题】:3d text effect in iOS 【发布时间】:2012-12-07 15:42:08 【问题描述】:我想在我的一个屏幕上呈现一些具有 3dish 外观的文本。我正在使用 UIKit 和标准视图控制器等。
效果将如下所示:
这可以通过 UIKit 和 ios 以某种方式完成吗?通常我只会使用静态 png,但是文本是动态的,并且会根据用户数据进行更新
【问题讨论】:
阴影效果很基本,但我也想知道如何像这样转换文本。也许自定义字体可以自己做? 是的,阴影是直截了当的,但在这种情况下它不是我想要的。我不认为自定义字体能够指定这样的渲染细节。 你试过阴影属性和阴影偏移吗? 一种方法是在 Photoshop 或类似程序中创建它并将其插入为UIImage
。
你读过癫痫这个问题吗?我想知道是否有办法在不使用静态 png 的情况下做到这一点。
【参考方案1】:
嗯,这是一个基本示例。
我们的想法是您使用 x/y 1 偏移量一遍又一遍地绘制相同文本的图层以创建“深度”外观。
我创建了一个 UIImage 类别,称为 UIImage+3d,您可以对其进行测试:
这是头文件(.h)
//
// UIImage+3D.h
//
// Created by Lefteris Haritou on 12/10/12.
// Feel Free to use this code, but please keep the credits
//
#import <UIKit/UIKit.h>
@interface UIImage (Extensions)
+ (UIImage *)create3DImageWithText:(NSString *)_text Font:(UIFont*)_font ForegroundColor:(UIColor*)_foregroundColor ShadowColor:(UIColor*)_shadowColor outlineColor:(UIColor*)_outlineColor depth:(int)_depth useShine:(BOOL)_shine;
@end
这是实现 (.m) 文件
//
// UIImage+3D.m
//
// Created by Lefteris Haritou on 12/10/12.
// Feel Free to use this code, but please keep the credits
//
#import "UIImage+3D.h"
#import <CoreText/CoreText.h>
#import <QuartzCore/QuartzCore.h>
@implementation UIImage (Extensions)
+ (UIImage *)create3DImageWithText:(NSString *)_text Font:(UIFont*)_font ForegroundColor:(UIColor*)_foregroundColor ShadowColor:(UIColor*)_shadowColor outlineColor:(UIColor*)_outlineColor depth:(int)_depth useShine:(BOOL)_shine
//calculate the size we will need for our text
CGSize expectedSize = [_text sizeWithFont:_font constrainedToSize:CGSizeMake(MAXFLOAT, MAXFLOAT)];
//increase our size, as we will draw in 3d, so we need extra space for 3d depth + shadow with blur
expectedSize.height+=_depth+5;
expectedSize.width+=_depth+5;
UIColor *_newColor;
UIGraphicsBeginImageContextWithOptions(expectedSize, NO, [[UIScreen mainScreen] scale]);
CGContextRef context = UIGraphicsGetCurrentContext();
//because we want to do a 3d depth effect, we are going to slightly decrease the color as we move back
//so here we are going to create a color array that we will use with required depth levels
NSMutableArray *_colorsArray = [[NSMutableArray alloc] initWithCapacity:_depth];
CGFloat *components = (CGFloat *)CGColorGetComponents(_foregroundColor.CGColor);
//add as a first color in our array the original color
[_colorsArray insertObject:_foregroundColor atIndex:0];
//create a gradient of our color (darkening in the depth)
int _colorStepSize = floor(100/_depth);
for (int i=0; i<_depth; i++)
for (int k=0; k<3; k++)
if (components[k]>(_colorStepSize/255.f))
components[k]-=(_colorStepSize/255.f);
_newColor = [UIColor colorWithRed:components[0] green:components[1] blue:components[2] alpha:CGColorGetAlpha(_foregroundColor.CGColor)];
//we are inserting always at first index as we want this array of colors to be reversed (darkest color being the last)
[_colorsArray insertObject:_newColor atIndex:0];
//we will draw repeated copies of our text, with the outline color and foreground color, starting from the deepest
for (int i=0; i<_depth; i++)
//change color
_newColor = (UIColor*)[_colorsArray objectAtIndex:i];
//draw the text
CGContextSaveGState(context);
CGContextSetShouldAntialias(context, YES);
//draw outline if this is the last layer (front one)
if (i+1==_depth)
CGContextSetLineWidth(context, 1);
CGContextSetLineJoin(context, kCGLineJoinRound);
CGContextSetTextDrawingMode(context, kCGTextStroke);
[_outlineColor set];
[_text drawAtPoint:CGPointMake(i, i) withFont:_font];
//draw filling
[_newColor set];
CGContextSetTextDrawingMode(context, kCGTextFill);
//if this is the last layer (first one we draw), add the drop shadow too and the outline
if (i==0)
CGContextSetShadowWithColor(context, CGSizeMake(-2, -2), 4.0f, _shadowColor.CGColor);
else if (i+1!=_depth)
//add glow like blur
CGContextSetShadowWithColor(context, CGSizeMake(-1, -1), 3.0f, _newColor.CGColor);
[_text drawAtPoint:CGPointMake(i, i) withFont:_font];
CGContextRestoreGState(context);
//if we need to apply the shine
if (_shine)
//create an alpha mask from the top most layer of the image, so we can add a shine effect over it
CGColorSpaceRef genericRGBColorspace = CGColorSpaceCreateDeviceRGB();
CGContextRef imageContext = CGBitmapContextCreate(NULL, (int)expectedSize.width, (int)expectedSize.height, 8, (int)expectedSize.width * 4, genericRGBColorspace, kCGBitmapByteOrder32Little | kCGImageAlphaPremultipliedFirst);
UIGraphicsPushContext(imageContext);
CGContextSetTextDrawingMode(imageContext, kCGTextFill);
[_text drawAtPoint:CGPointMake(_depth-1, _depth-1) withFont:_font];
CGImageRef alphaMask = CGBitmapContextCreateImage(imageContext);
CGContextRelease(imageContext);
UIGraphicsPopContext();
//draw shine effect
//clip context to the mask we created
CGRect drawRect = CGRectZero;
drawRect.size = expectedSize;
CGContextSaveGState(context);
CGContextClipToMask(context, drawRect, alphaMask);
CGContextSetBlendMode(context, kCGBlendModeLuminosity);
size_t num_locations = 4;
CGFloat locations[4] = 0.0, 0.4, 0.6, 1;
CGFloat gradientComponents[16] =
0.0, 0.0, 0.0, 1.0,
0.6, 0.6, 0.6, 1.0,
0.8, 0.8, 0.8, 1.0,
0.0, 0.0, 0.0, 1.0
;
CGGradientRef glossGradient = CGGradientCreateWithColorComponents(genericRGBColorspace, gradientComponents, locations, num_locations);
CGPoint start = CGPointMake(0, 0);
CGPoint end = CGPointMake(0, expectedSize.height);
CGContextDrawLinearGradient(context, glossGradient, start, end, 0);
CGColorSpaceRelease(genericRGBColorspace);
CGGradientRelease(glossGradient);
CGImageRelease(alphaMask);
CGContextRestoreGState(context);
UIImage *finalImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return finalImage;
@end
要使用它,只需导入类别扩展,然后按如下方式使用它:
UIImage *my3dImage = [UIImage create3DImageWithText:@"3" Font:[UIFont systemFontOfSize:250] ForegroundColor:[UIColor colorWithRed:(200/255.f) green:(200/255.f) blue:(200/255.f) alpha:1.0] ShadowColor:[UIColor blackColor] outlineColor:[UIColor colorWithRed:(225/255.f) green:(225/255.f) blue:(225/255.f) alpha:1.0] depth:8 useShine:NO];
UIImageView *imgView = [[UIImageView alloc] initWithImage:my3dImage];
[self.view addSubview: imgView];
另一个例子是这样的:
UIImage *my3dImage = [UIImage create3DImageWithText:@"3D" Font:[UIFont fontWithName:@"MarkerFelt-Wide" size:180] ForegroundColor:[UIColor colorWithRed:(222/255.f) green:(100/255.f) blue:(100/255.f) alpha:1.0] ShadowColor:[UIColor blackColor] outlineColor:[UIColor colorWithRed:(216/255.f) green:(120/255.f) blue:(120/255.f) alpha:1.0] depth:6 useShine:NO];
UIImageView *imgView = [[UIImageView alloc] initWithImage:my3dImage];
imgView.center = self.view.center;
[self.view addSubview: imgView];
结果如下所示:
我编辑了代码以在图像上添加光泽效果,我相信这会让它看起来更酷
UIImage *my3dImage = [UIImage create3DImageWithText:@"3D" Font:[UIFont fontWithName:@"MarkerFelt-Wide" size:180] ForegroundColor:[UIColor colorWithRed:(222/255.f) green:(100/255.f) blue:(100/255.f) alpha:1.0] ShadowColor:[UIColor blackColor] outlineColor:[UIColor colorWithRed:(216/255.f) green:(120/255.f) blue:(120/255.f) alpha:1.0] depth:6 useShine:YES];
UIImageView *imgView = [[UIImageView alloc] initWithImage:my3dImage];
imgView.center = self.view.center;
[self.view addSubview: imgView];
【讨论】:
这看起来不错,干得好! @Lefteris:刚刚有了这个想法:也许你应该赋予选择 3D 的“方向”的能力。例如,仅“向下”或向左和向上等。明白我的意思吗?举个更好的例子,在问题中,3D 效果只会下降,而在您的答案中,它会下降并且正确。我认为将其包含在您的代码中并不难(让用户在方法调用中使用枚举将其作为参数传递,就像您使用 UIViewAnimationCurveEaseInOut 时一样),它可能会非常好!另外我认为您绝对应该将其发送到 cocoacontrols.com! @rdurand 如果我有时间,我会从这里创建一个 git 项目,它有更多选项,比如你建议的一些选项,当然还有更好的结果。以上是我快速完成的一个 30 分钟的事情来证明这一点。至于 CocoaControls,这实际上并不符合自定义控件的条件:-) 太棒了!至于 CocoaControls,我个人希望在 CC 上找到类似的东西。它不是“真正的”控件,但该站点的 About 部分说:“Cocoa Controls 列出自定义控件 和视图适用于 iOS 和 Mac OS X,帮助您以尽可能少的工作量提高 Cocoa 应用程序的质量。”我会说你是对的就可以了,还是我错了? ;) @Lefteris - 当然,它符合条件:)【参考方案2】:以下代码可能并不完美,但应该是一个很好的起点。
基本上你画了两次字体,稍微改变了大小和偏移量。根据您要处理的字体和大小,您可能需要使用fontSize
、fontSizeDelta
和fontOffset
。
结果看起来有点像这样:
- (UIImage *)imageWith3dString:(NSString *)text
CGFloat fontSize = 150.0;
CGFloat fontSizeDelta = 3.0;
CGFloat fontOffset = 5.0;
NSString *fontName = @"Bebas";
UIFont *font = [UIFont fontWithName:fontName size:fontSize];
CGSize textSize = [text sizeWithFont:font
constrainedToSize:CGSizeMake(CGFLOAT_MAX, CGFLOAT_MAX)];
CGSize size = CGSizeMake(textSize.width, fontSize);
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGContextRef ctx = CGBitmapContextCreate(NULL,
(int)size.width,
(int)size.height,
8,
(int)(4 * size.width),
colorSpace,
kCGImageAlphaPremultipliedLast);
// Draw with shadow
CGContextSetShadowWithColor(ctx, CGSizeMake(0, 0), 10.0, [UIColor colorWithWhite:0.0 alpha:0.6].CGColor);
CGContextSetRGBStrokeColor(ctx, 1.0, 1.0, 1.0, 0.6);
CGContextSetAllowsAntialiasing(ctx, YES);
CGContextSetLineWidth(ctx, 2.0);
CGContextSetTextDrawingMode(ctx, kCGTextFillStroke);
CGContextSetRGBFillColor(ctx, 222 / 255.0, 222 / 255.0, 222 / 255.0, 1.0);
CGContextSetCharacterSpacing(ctx, 2.6);
CGContextSelectFont(ctx, [fontName UTF8String], fontSize - fontSizeDelta, kCGEncodingMacRoman);
CGContextShowTextAtPoint(ctx, 0.0, 3.0 + fontOffset, [text UTF8String], text.length);
CGContextSetShadowWithColor(ctx, CGSizeZero, 0.0, NULL); // disable shadow
CGContextSetCharacterSpacing(ctx, 1.0);
CGContextSelectFont(ctx, [fontName UTF8String], fontSize, kCGEncodingMacRoman);
CGContextShowTextAtPoint(ctx, 0.0, 3.0, [text UTF8String], text.length);
CGImageRef imageRef = CGBitmapContextCreateImage(ctx);
UIImage *image = [UIImage imageWithCGImage:imageRef];
CGColorSpaceRelease(colorSpace);
CGImageRelease(imageRef);
CGContextRelease(ctx);
return image;
- (void)viewDidLoad
[super viewDidLoad];
UIImageView *imageView = [[UIImageView alloc] initWithImage:[self imageWith3dString:@"3"]];
[self.view addSubview:imageView];
YMMV
【讨论】:
嗨 Tomas,那非常接近,我认为这可能是最接近的。 当我尝试这个时,我的日志会变得很糟糕:我收到像Dec 12 17:28:01 imac-1 3D Text[1380] <Error>: CGContextSetStyle: invalid context 0x0
和 Dec 12 17:28:01 imac-1 3D Text[1380] <Error>: CGContextSetRGBStrokeColor: invalid context 0x0
这样的错误有什么帮助吗?
我猜你没有安装 Bebas 字体。尝试将字体名称更改为不同的名称,例如“Helvetica”以上是关于iOS 中的 3d 文字效果的主要内容,如果未能解决你的问题,请参考以下文章