iOSのUILabelのサイズそのままでHiragino SansのqyjpやÉの上下が見切れないようにする

# 課題

動作環境: iOS10〜12

UILabelのHiragino Sansを直接指定して【英語のみ】とかの場合、下記のようにqyjpやÉの上下が見切れたりします。

<img width="372" alt="スクリーンショット 2018-12-03 18.42.45.png" src="https://qiita-image-store.s3.amazonaws.com/0/308423/cad3097e-8dbf-59d3-de3c-af470e893665.png">

```swift
label.font = UIFont(name: "HiraginoSans-W3", size: fontSize)!
label.text = "Font TestgpÉ"
```

【AutoLayoutやsizeThatFits()上のサイズを変更することなく】、描画領域を広げて下記みたいにしたい

<img width="377" alt="スクリーンショット 2018-12-03 18.41.51.png" src="https://qiita-image-store.s3.amazonaws.com/0/308423/239fc39f-e7bb-bf84-84a2-d9289746e180.png">

# 対応方法

上下にgutter(スペース)を確保して、描画領域を広げることで対応。
AutoLayout上のサイズを変更しないためには```alignmentRectInsets```を使用する

```swift
import UIKit

class VerticalGutterLabel: UILabel {

  /// フォントサイズの1/6のgutterを計算
  private var verticalGutter: CGFloat {
    guard let font = self.font else {
      return 4.0 //font未指定の時の値。だいたい4.0確保しておけばOKなのでそうしておく
    }
    return ceil(font.pointSize / 6.0)
  }

  /// サイズ(描画領域)を大きくする
  override var intrinsicContentSize: CGSize {
    if #available(iOS 17, *) {
      // iOS17以降だとalignmentRectInsetsが変わると自動的にintrinsicContentSizeも変わるようなので、iOS17以降はintrinsicContentSizeを大きくしない
      return super.intrinsicContentSize
    }
    var size = super.intrinsicContentSize
    size.height += verticalGutter * 2
    return size
  }

  /// 描画領域を大きくした分、描画位置を調整
  override func drawText(in rect: CGRect) {
    super.drawText(in: UIEdgeInsetsInsetRect(rect, UIEdgeInsets(top: verticalGutter,
                                                                left: 0.0,
                                                                bottom: verticalGutter,
                                                                right: 0.0)))
  }

  /// 描画領域を大きくした分の部分をAutoLayout上で加算されないように相殺する処理
  override var alignmentRectInsets: UIEdgeInsets {
    let alignmentRectInsets = super.alignmentRectInsets
    return UIEdgeInsets(top: alignmentRectInsets.top + verticalGutter,
                        left: alignmentRectInsets.left,
                        bottom: alignmentRectInsets.bottom + verticalGutter,
                        right: alignmentRectInsets.right)
  }

  /// alignmentRectInsetsの影響でsizeThatFits(sizeToFit含む)が小さくなってしまうのを相殺する処理
  override func sizeThatFits(_ size: CGSize) -> CGSize {
    var size = super.sizeThatFits(size)
    size.height += verticalGutter * 2
    return size
  }
}
```

修正内容の説明は```VerticalGutterLabel```のコメント参照

参考にした情報
https://stackoverflow.com/questions/20985085/uilabel-clipping-italic-oblique-text-at-left-and-right-edges-of-content-ios

# 対応時の注意！！

描画領域を広げているので```backgroundColor```などの描画領域も広がっているので注意！！

<img width="433" alt="スクリーンショット 2018-12-03 15.52.09.png" src="https://qiita-image-store.s3.amazonaws.com/0/308423/232b7f9a-5522-da6a-0080-1c9fe0935b0a.png">

# "HiraginoSans"で直接指定する理由

Zeplinというツールをデザイン指示に使用しているのですが、
Zeplinで指定するフォントサイズをそのまま設定しても
システムフォント(```systemFont(ofSize:)```や```boldSystemFont(ofSize:)```)だと小さくなってしまうという問題があったためです。

Zeplinでのフォントサイズと"HiraginoSans"直接指定の時のフォントサイズが一致していたため"HiraginoSans"直接指定しています。

よくわかっていないですが、システムフォント指定の場合は"HiraginoSans"の本来のフォントサイズより小さく表示しているのかな。。と思っています。

# 次の記事は

明日、食べログAdventCalendar11日目は @itume さんからの「POROと大人になった僕」です。