3d text effect in iOS

后端 未结 2 628
太阳男子
太阳男子 2021-01-30 02:35

I want to render some text on one of my screens that has a 3dish look to it. I am using UIKit and standard views controllers etc.

The effect will look something like thi

相关标签:
2条回答
  • 2021-01-30 03:19

    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.

    The result looks somewhat like this:

    enter image description here

    - (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

    0 讨论(0)
  • 2021-01-30 03:30

    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.

    I have create an UIImage Category, called UIImage+3d, which you can test:

    This is the header (.h) file

    //
    //  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

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

    enter image description here

    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:

    enter image description here

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

    enter image description here

    0 讨论(0)
提交回复
热议问题