Posted at

iOSアプリで、吹き出しっぽいviewを作成

More than 3 years have passed since last update.

人(キャラクター)が喋っている感じを表現しようと、吹き出しを描いた際のメモ。

Xcode7.2で動作確認しています。

方針としては、グラフィックスコンテキストを作成して、吹き出しぽい形を描画していくのが良さそう。

グラフィックスコンテキストを利用した描画に関しての説明は、以下の記事などが詳しい。

SwiftでCoreGraphics画像作成(基本)【メモ】 - Qiita


実装

StoryBoardに置いたViewController.swiftに、吹き出しをaddSubview(_:)する。

吹き出しは、BalloonViewというUIViewのサブクラスとして宣言し、幅280px, 高さ100pxで描画。


ViewController.swift

class ViewController: UIViewController {

override func viewDidLoad() {
super.viewDidLoad()

let balloon = BalloonView(frame: CGRectMake((view.bounds.size.width - 280) / 2, 100, 280, 100))
balloon.backgroundColor = UIColor.whiteColor()
view.addSubview(balloon)
}

}


BalloonViewの中身は以下の通り。

drawRect(_:)メソッドの中で、


  • グラフィックスコンテキストを宣言



    • UIGraphicsGetCurrentContextで呼び出し



  • 吹き出しを塗りつぶす色を指定


    • それぞれCGContextSetFillColorWithColor



  • 吹き出しの塗りつぶし範囲の指定 & 実行



    • contextBalloonPath(_:rect:)メソッドで




塗りつぶしについて。

今回描こうと思っている吹き出しは、長方形部分と三角形部分に分割できる。

contextBalloonPath(_:rect:)メソッドの中では、三角形部分の座標を計算し、それを利用して塗りつぶしを行う。

※三角形の一辺の長さを20px, 高さを17.3pxで、だいたい正三角形



  1. CGContextAddRect(_:_:)で長方形部分のpathを指定


  2. CGContextMoveToPoint(_:_:_:), CGContextAddLineToPoint(_:_:_:)で三角形のpathを指定


    • 算出済みの三角形の各頂点の座標を利用



これらをそれぞれCGContextFillPath(_:)で塗りつぶし、吹き出しが完成。


BalloonView.swift

class BalloonView: UIView {

let triangleSideLength: CGFloat = 20
let triangleHeight: CGFloat = 17.3

override func drawRect(rect: CGRect) {
super.drawRect(rect)

let context = UIGraphicsGetCurrentContext()
CGContextSetFillColorWithColor(context, UIColor.greenColor().CGColor)
contextBalloonPath(context!, rect: rect)
}

func contextBalloonPath(context: CGContextRef, rect: CGRect) {
let triangleRightCorner = (x: (rect.size.width + triangleSideLength) / 2, y: CGRectGetMaxY(rect) - triangleHeight)
let triangleBottomCorner = (x: rect.size.width / 2, y: CGRectGetMaxY(rect))
let triangleLeftCorner = (x: (rect.size.width - triangleSideLength) / 2, y: CGRectGetMaxY(rect) - triangleHeight)

// 塗りつぶし
CGContextAddRect(context, CGRectMake(0, 0, 280, rect.size.height - triangleHeight))
CGContextFillPath(context)
CGContextMoveToPoint(context, triangleLeftCorner.x, triangleLeftCorner.y)
CGContextAddLineToPoint(context, triangleBottomCorner.x, triangleBottomCorner.y)
CGContextAddLineToPoint(context, triangleRightCorner.x, triangleRightCorner.y)
CGContextFillPath(context)
}

}


以下のようなビューになる。

スクリーンショット 2015-12-27 20.18.18.png

吹き出しに枠線をつけたい場合は、CGContextStrokePath(_:)などを使って実装することになるだろう。


蛇足

せっかくなので、セリフと喋ってるキャラクター(というか猫)の写真だけ入れてみた。

セリフはUILabel、写真はUIImageViewで実装してaddSubViewしてるだけです。


ViewController.swift

override func viewDidLoad() {

super.viewDidLoad()

let balloon = BalloonView(frame: CGRectMake((view.bounds.size.width - 280) / 2, 100, 280, 100))
balloon.backgroundColor = UIColor.whiteColor()
view.addSubview(balloon)

let label = UILabel(frame: balloon.bounds)
label.frame.origin.y -= 8.7
label.text = "ニャーオ"
label.textColor = UIColor.whiteColor()
label.font = UIFont.boldSystemFontOfSize(24)
label.textAlignment = .Center
balloon.addSubview(label)

let imageView = UIImageView()
imageView.frame.size = CGSizeMake(100, 100)
imageView.frame.origin.y = CGRectGetMaxY(balloon.frame) + 10
imageView.center.x = balloon.center.x
imageView.layer.cornerRadius = 50
imageView.image = UIImage(named: "Cat")
imageView.contentMode = .ScaleAspectFill
imageView.clipsToBounds = true
view.addSubview(imageView)
}


出来上がり。以上です。

スクリーンショット 2015-12-27 20.21.21.png