Speech Bubble in iOS SDK using Objective-C

后端 未结 3 671
暖寄归人
暖寄归人 2020-12-08 12:26

I am working on a children\'s book app and would like to dynamically populate speech bubble(s) for character dialogues as shown in attached screenshots. Screenshots are from

相关标签:
3条回答
  • 2020-12-08 12:47

    here is my implementation by modifying this apple tutorial:

    https://developer.apple.com/library/ios/samplecode/quartzdemo/Listings/QuartzDemo_QuartzCurves_m.html

    - (void)drawRect:(CGRect)rect
    {
        CGContextRef context=UIGraphicsGetCurrentContext();
        CGContextSetLineWidth(context, .5f);
        CGContextSetStrokeColorWithColor(context, [UIColor lightGrayColor].CGColor);
        CGContextSetRGBFillColor(context, 1, 1, 1, 1);
    
        rect.origin.y++;
        CGFloat radius = cornerRadius;
    
        CGFloat minx = CGRectGetMinX(rect), midx = CGRectGetMidX(rect), maxx = CGRectGetMaxX(rect);
        CGFloat miny = CGRectGetMinY(rect), midy = CGRectGetMidY(rect), maxy = CGRectGetMaxY(rect);
    
        CGMutablePathRef outlinePath = CGPathCreateMutable();
    
        if (![User isCurrentUser:message.user])
        {
            minx += 5;
        
            CGPathMoveToPoint(outlinePath, nil, midx, miny);
            CGPathAddArcToPoint(outlinePath, nil, maxx, miny, maxx, midy, radius);
            CGPathAddArcToPoint(outlinePath, nil, maxx, maxy, midx, maxy, radius);
            CGPathAddArcToPoint(outlinePath, nil, minx, maxy, minx, midy, radius);
            CGPathAddLineToPoint(outlinePath, nil, minx, miny + 20);
            CGPathAddLineToPoint(outlinePath, nil, minx - 5, miny + 15);
            CGPathAddLineToPoint(outlinePath, nil, minx, miny + 10);
            CGPathAddArcToPoint(outlinePath, nil, minx, miny, midx, miny, radius);
            CGPathCloseSubpath(outlinePath);
        
            CGContextSetShadowWithColor(context, CGSizeMake(0,1), 1, [UIColor lightGrayColor].CGColor);
            CGContextAddPath(context, outlinePath);
            CGContextFillPath(context);
        
            CGContextAddPath(context, outlinePath);
            CGContextClip(context);
            CGPoint start = CGPointMake(rect.origin.x, rect.origin.y);
            CGPoint end = CGPointMake(rect.origin.x, rect.size.height);
            CGContextDrawLinearGradient(context, [self normalGradient], start, end, 0);
        }
        else
        {
            maxx-=5;
            CGPathMoveToPoint(outlinePath, nil, midx, miny);
            CGPathAddArcToPoint(outlinePath, nil, minx, miny, minx, midy, radius);
            CGPathAddArcToPoint(outlinePath, nil, minx, maxy, midx, maxy, radius);
            CGPathAddArcToPoint(outlinePath, nil, maxx, maxy, maxx, midy, radius);
            CGPathAddLineToPoint(outlinePath, nil, maxx, miny + 20);
            CGPathAddLineToPoint(outlinePath, nil, maxx + 5, miny + 15);
            CGPathAddLineToPoint(outlinePath, nil, maxx, miny + 10);
            CGPathAddArcToPoint(outlinePath, nil, maxx, miny, midx, miny, radius);
            CGPathCloseSubpath(outlinePath);
        
            CGContextSetShadowWithColor(context, CGSizeMake(0,1), 1, [UIColor lightGrayColor].CGColor);
            CGContextAddPath(context, outlinePath);
            CGContextFillPath(context);
        
            CGContextAddPath(context, outlinePath);
            CGContextClip(context);
            CGPoint start = CGPointMake(rect.origin.x, rect.origin.y);
            CGPoint end = CGPointMake(rect.origin.x, rect.size.height);
            CGContextDrawLinearGradient(context, [self greenGradient], start, end, 0);
        }
    
    
        CGContextDrawPath(context, kCGPathFillStroke);
    
    }
    
    - (CGGradientRef)normalGradient
    {
    
        NSMutableArray *normalGradientLocations = [NSMutableArray arrayWithObjects:
                                                   [NSNumber numberWithFloat:0.0f],
                                                   [NSNumber numberWithFloat:1.0f],
                                                   nil];
    
    
        NSMutableArray *colors = [NSMutableArray arrayWithCapacity:2];
    
        UIColor *color = [UIColor whiteColor];
        [colors addObject:(id)[color CGColor]];
        color = [StyleView lightColorFromColor:[UIColor cloudsColor]];
        [colors addObject:(id)[color CGColor]];
        NSMutableArray  *normalGradientColors = colors;
    
        int locCount = [normalGradientLocations count];
        CGFloat locations[locCount];
        for (int i = 0; i < [normalGradientLocations count]; i++)
        {
            NSNumber *location = [normalGradientLocations objectAtIndex:i];
            locations[i] = [location floatValue];
        }
        CGColorSpaceRef space = CGColorSpaceCreateDeviceRGB();
    
        CGGradientRef normalGradient = CGGradientCreateWithColors(space, (__bridge CFArrayRef)normalGradientColors, locations);
        CGColorSpaceRelease(space);
    
        return normalGradient;
    }
    

    Which results in this... enter image description here

    0 讨论(0)
  • 2020-12-08 13:08

    Another way of doing this is through the use of images with cap insets. Take a look at the UIImage method:

    resizableImageWithCapInsets:

    Basically you can create an image of a minimum sized bubble, and have it stretch indefinitely while maintaining the look of the 'bubble' borders.

    The code below specifies an image where 12 pixels on the top, left, bottom and right be preserved when stretching/resizing the image:

    UIImage *bubble = [[UIImage imageNamed:@"bubble.png"] 
                                resizableImageWithCapInsets:UIEdgeInsetsMake(12, 12, 12, 12)];
    UIImageView *imgView = [[[UIImageView alloc] initWithImage:bubble] autorelease];
    imgView.frame = CGRectZero;
    

    Animating the size change of a UIImageView comes for free:

    [UIView animateWithDuration:0.3
                     animations:^(void) {
                         imgView.frame = CGRectMake(50, 50, 200, 100);
                     } completion:^(BOOL finished) {
    
                     }];
    
    0 讨论(0)
  • 2020-12-08 13:12

    The three20 framework (actually, Three20Style) has got a shaped window class called TTSpeechBubbleShape, which is basically what you are looking for.

    Now, you have two options, as I see it:

    1. either use Three20; or

    2. analyze the TTSpeechBubbleShape class to see how this is implemented and do the same in your app.

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