【问题标题】:Speech Bubble in iOS SDK using Objective-C使用 Objective-C 的 iOS SDK 中的语音气泡
【发布时间】:2012-06-27 02:36:19
【问题描述】:

我正在开发一款儿童读物应用程序,并希望为角色对话动态填充气泡,如所附屏幕截图所示。屏幕截图来自我猜是使用 Cocos2D(或 3D)开发的 Cinderella 应用程序。但是,我想使用 Objective-c 和 iOS sdk 来实现这个功能。

一种方法是:

计算给定字体和大小的文本大小(使用 NSString 方法 sizeWithFont:constrainedToSize:),然后绘制一个圆角矩形(使用 CALayer)来包围该大小。

为了绘制圆角矩形,设置一个具有背景颜色、边框宽度、边框颜色和颜色半径的 CALayer,并将其添加到 UIView 对象。这将很容易产生气泡效果。

现在,我怎样才能得到一个指向角色嘴角的角?我如何从角色的嘴里弹出和弹出CALayer,我将如何实现这个动画?我的主要场景(插图)将是一个 UIImageview,这个对话弹出窗口应该以动画方式从角色的嘴里出现,几秒钟后它应该消失,就好像它回到角色的嘴里一样。您的建议将不胜感激。

如果您在某个地方知道一些示例代码/文章,请相应地路由我。

这是该应用程序的视频链接,它显示了角色对话如何以对话气泡的形式弹出和弹出:http://www.youtube.com/watch?v=umfNJLyrrNg

【问题讨论】:

    标签: objective-c ios


    【解决方案1】:

    这是我通过修改这个苹果教程实现的:

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

    这会导致...

    【讨论】:

    • 这看起来不错,但你能添加关于使用的评论吗?即,您如何从想要将一些文本放入气泡中,到实际将其放入气泡中
    • @user2011985 您将创建一个 UILabel 或 UITextField 的子类并添加 drawRect 方法。我正在稍微修改代码以使其更加明显。
    • 你能快速提供演示吗? @米莎
    • @Misha 。您能否将这个 UILabel 子类与您也使用过的属性共享?评论和配色方案?还是演示?将不胜感激。
    • 我发现,至少在 IOS 11 中,当我尝试这样做时,如果左对齐的文本不在对话气泡的边缘,所以我不得不使用覆盖的方法缩进它 - (void )drawTextInRect:(CGRect)rect { UIEdgeInsets insets = {0, 10, 0, 10}; [超级drawTextInRect:UIEdgeInsetsInsetRect(rect, insets)]; }
    【解决方案2】:

    另一种方法是使用带有帽子插图的图像。看看 UIImage 方法:

    resizableImageWithCapInsets:

    基本上,您可以创建一个最小尺寸的气泡图像,并让它无限延伸,同时保持“气泡”边框的外观。

    下面的代码指定了一个图像,在拉伸/调整图像大小时保留顶部、左侧、底部和右侧 12 个像素:

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

    动画 UIImageView 的大小变化是免费的:

    [UIView animateWithDuration:0.3
                     animations:^(void) {
                         imgView.frame = CGRectMake(50, 50, 200, 100);
                     } completion:^(BOOL finished) {
    
                     }];
    

    【讨论】:

    • 我最近经常看到这个。 -stretchableImageWithLeftCapWidth:topCapHeight: 是否已弃用?
    • 谢谢。我如何使用 resizableImageWithCapInsets: 说出指向角色嘴巴的气泡角?以及如何为对话泡泡的打开和关闭设置动画?
    • 我想尖角将是我要拉伸的图像的一部分。如何为对话泡泡的打开和关闭设置动画(点击或自动)?
    • 是的,Alex,尖角将成为图像的一部分。请参阅我上面的说明。
    • 非常感谢。我将如何以及在哪里打印实际的对话文本?我可以使用 NSString 和/或 UILabel 吗?对话应打印在对话气泡内。
    【解决方案3】:

    three20 framework(实际上是 Three20Style)有一个名为 TTSpeechBubbleShape 的形状窗口类,基本上就是你要找的。​​p>

    现在,在我看来,您有两个选择:

    1. 要么使用 Three20;或

    2. 分析 TTSpeechBubbleShape 类以查看它是如何实现的,并在您的应用中执行相同的操作。

    【讨论】:

    • 谢谢。如果我使用 TTSpeechBubbleShape 实施,我希望我的应用程序被苹果接受不会有任何问题?我将如何为气泡的打开和关闭设置动画?
    • 我认为你不会有批准的问题;我的一个使用语音气泡的应用程序前段时间获得了批准...尝试为帧大小设置动画,就可以了。
    • @sergio 嘿,我正在尝试实现这个,但是 SpeechBubble 类是从 TTShape 派生的,TTShape 是从 NSObject 派生的。我如何把这个 NSObject 变成一个视图
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-10-26
    • 2021-09-18
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多