简体   繁体   English

iOS中的3d文字效果

[英]3d text effect in iOS

I want to render some text on one of my screens that has a 3dish look to it. 我想在我的一个具有3dish外观的屏幕上呈现一些文本。 I am using UIKit and standard views controllers etc. 我正在使用UIKit和标准视图控制器等。

The effect will look something like this: 效果看起来像这样:

在此输入图像描述

Can this be done somehow with UIKit & iOS? 这可以通过UIKit和iOS以某种方式完成吗? Ordinarily I would just use a static png however, the text is dynamic and updates based on user data 通常我只会使用静态png,但文本是动态的,并根据用户数据进行更新

Well, here is a basic sample of this. 嗯,这是一个基本的例子。

The idea is you draw layers of the same text over and over with an x/y 1 offset to create the "depth" look. 我们的想法是用x / y 1偏移一遍又一遍地绘制相同文本的图层,以创建“深度”外观。

I have create an UIImage Category, called UIImage+3d, which you can test: 我创建了一个名为UIImage + 3d的UIImage类别,你可以测试它:

This is the header (.h) file 这是标题(.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

Here is the implementation (.m) file 这是实现(.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

And to use this , simply import the category extension then use it as follows: 要使用它,只需导入类别扩展,然后按如下方式使用它:

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];

在此输入图像描述

Another example is this: 另一个例子是:

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];

And the result looks like this: 结果如下:

在此输入图像描述

I edited the code to add a shine effect over the image, which I believe makes it look cooler 我编辑了代码,在图像上添加了光泽效果,我相信它看起来更酷

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];

在此输入图像描述

The following code might not be perfect, but it should be a good starting point. 以下代码可能不完美,但它应该是一个很好的起点。

Basically you draw the font twice, slightly changing the size and the offset. 基本上你画两次字体,稍微改变大小和偏移量。 Depending on the font and size you're dealing with you're probably have to play a bit with fontSize , fontSizeDelta and fontOffset . 根据您正在处理的字体和大小,您可能需要使用fontSizefontSizeDeltafontOffset

The result looks somewhat like this: 结果看起来有点像这样:

在此输入图像描述

- (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 因人而异

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM