NSAttributedStringをかんたんに生成する拡張関数(Swift)

## はじめに

iOSアプリ開発では、複数行に渡る文章の行間（ `lineSpacing` ）など、文字列に複雑な装飾を施す場合、 `NSAttributedString` を生成する必要があります。
生成方法が複雑なので、引数を渡してかんたんに生成する拡張関数を実装しました。

## 環境

- OS：macOS Mojave 10.14.6
- Xcode：11.3.1 (11C504)
- Swift：5.1.3

## 実装

イニシャライザを拡張関数として定義します。

```swift:NSAttributedString+Init.swift
import UIKit

extension NSAttributedString {
    convenience init(font: UIFont, color: UIColor, lineSpacing: CGFloat, alignment: NSTextAlignment, string: String) {
        var attributes: [NSAttributedString.Key: Any] = [
            .font: font,
            .foregroundColor: color
        ]
        let paragraphStyle = NSMutableParagraphStyle()
        paragraphStyle.lineSpacing = lineSpacing
        paragraphStyle.alignment = alignment
        attributes.updateValue(paragraphStyle, forKey: .paragraphStyle)

        self.init(string: string, attributes: attributes)
    }
}
```

## 使い方

拡張関数として定義したイニシャライザを呼び出すのみです。

```swift:after
    @IBOutlet private weak var descriptionLabel: UILabel! {
        willSet {
            newValue.attributedText = NSAttributedString(
                font: .systemFont(ofSize: 16.0),
                color: .blue,
                lineSpacing: 6.0,
                alignment: .left,
                string: "description"
            )
        }
    }
```

拡張関数の定義前と比べると、スッキリ書けることがわかります。

```swift:before
    @IBOutlet private weak var descriptionLabel: UILabel! {
        willSet {
            var attributes: [NSAttributedString.Key: Any] = [
                .font: UIFont.systemFont(ofSize: 16.0),
                .foregroundColor: UIColor.blue
            ]
            let paragraphStyle = NSMutableParagraphStyle()
            paragraphStyle.lineSpacing = 6.0
            paragraphStyle.alignment = .left
            attributes.updateValue(paragraphStyle, forKey: .paragraphStyle)

            newValue.attributedText = NSAttributedString(string: "description", attributes: attributes)
        }
    }
```

## おわりに

今回紹介した拡張関数は、私が必要としている最低限の引数のみ追加しているので、必要に応じてカスタマイズしてください。
`NSMutableAttributedString` として可変に扱う場合にもカスタマイズが必要です。

## おまけ：他の方法

Twitterで他の人がどう `NSAttributedString` を生成しているか伺ったので、紹介します。

### `NSAttributedString` を定数化する

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">よくアプリ内で使うAttributeを定数化ですね...<br>enum Attributeという名前空間作ってその中に static で書いていっちゃう感じです。</p>&mdash; 大渕雄生@未踏2020！ (@obuchi_yuki) <a href="https://twitter.com/obuchi_yuki/status/1276092829245161472?ref_src=twsrc%5Etfw">June 25, 2020</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

行間の大きさが画面間で変わることも少ないので、 `NSAttributedString` ごと定数化するのはありだと思いました。
定数化の実装時に私が紹介した拡張関数を使うと、よりスッキリ書けると思います。

### `String` から生成できるようにする

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">自分は極力楽したくて<br>&quot;文字列&quot;.decode([DecoratableType])<br>で書けるlibを書いた <a href="https://t.co/z3gngLRzcJ">https://t.co/z3gngLRzcJ</a></p>&mdash; もちゅる｜【転職活動中】フルリモートパパエンジニア (@mothule) <a href="https://twitter.com/mothule/status/1276094369301315585?ref_src=twsrc%5Etfw">June 25, 2020</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

`String` に `NSAttributedString` を返す拡張関数を追加する方法です。
こちらもわかりやすくていいと思いました。
ただ文字列をローカライズしている場合、若干読みづらくなるかもしれません。