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


人（キャラクター）が喋っている感じを表現しようと、吹き出しを描いた際のメモ。
Xcode7.2で動作確認しています。

方針としては、グラフィックスコンテキストを作成して、吹き出しぽい形を描画していくのが良さそう。
グラフィックスコンテキストを利用した描画に関しての説明は、以下の記事などが詳しい。

[SwiftでCoreGraphics画像作成（基本）【メモ】 - Qiita](http://qiita.com/moonkids/items/4f89701dbc397107a867)


## 実装
StoryBoardに置いた`ViewController.swift`に、吹き出しを`addSubview(_:)`する。
吹き出しは、`BalloonView`という`UIView`のサブクラスとして宣言し、幅280px, 高さ100pxで描画。

```swift: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(_:)`で塗りつぶし、吹き出しが完成。

```swift: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](https://qiita-image-store.s3.amazonaws.com/0/1795/5562a433-6bc9-24bb-317a-183c7aca9171.png "スクリーンショット 2015-12-27 20.18.18.png")

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

## 蛇足
せっかくなので、セリフと喋ってるキャラクター（というか猫）の写真だけ入れてみた。
セリフは`UILabel`、写真は`UIImageView`で実装して`addSubView`してるだけです。

```swift: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](https://qiita-image-store.s3.amazonaws.com/0/1795/25e220d9-2218-6d3b-7dad-a1fba072bfd2.png "スクリーンショット 2015-12-27 20.21.21.png")