iOS
Swift5

Swift 5で進化するNSAttributedStringの扱い方

Swift5がリリースされましたね。

Swift5にはABIの安定化やResult型のStandard Library追加など様々な追加・変更がありました。

次のURLにまとまった神Playgroundがあるので、みたことない人はおさらいしましょう。

https://github.com/twostraws/whats-new-in-swift-5-0


String Interpolationを使ってNSAttributedStringをラップする

この中でString Interpolationという機能があります。

String Interpolationの使い方は上記Playgroundを参照してください。

このString Interpolationを使うとめちゃめちゃNSAttributedStringが便利になります。

下記にNSAttributedStringをラップしたものを作りました。


AttributedString.swift

import UIKit

public struct AttributedString: ExpressibleByStringLiteral, ExpressibleByStringInterpolation, CustomStringConvertible {
public struct StringInterpolation: StringInterpolationProtocol {
public var attributedString: NSMutableAttributedString
public init(literalCapacity: Int, interpolationCount: Int) {
attributedString = NSMutableAttributedString()
}

public func appendLiteral(_ literal: String) {
attributedString.append(NSAttributedString(string: literal))
}

public func appendInterpolation(_ linkText: String? = nil, URL url: URL) {
let text = linkText ?? url.absoluteString
attributedString.append(NSAttributedString(string: text, attributes: [.link: url]))
}

public func appendInterpolation(_ image: UIImage, bounds: CGRect? = nil) {
let textAttachment = NSTextAttachment()
textAttachment.image = image
if let bounds = bounds {
textAttachment.bounds = bounds
}
attributedString.append(NSAttributedString(attachment: textAttachment))
}

func appendInterpolation(_ text: String, attributes: [NSAttributedString.Key: Any]) {
attributedString.append(NSAttributedString(string: text, attributes: attributes))
}
}
public var attributedString: NSAttributedString
public init(stringLiteral value: String) {
attributedString = NSAttributedString(string: value)
}

public init(stringInterpolation: StringInterpolation) {
attributedString = NSAttributedString(attributedString: stringInterpolation.attributedString)
}

public init(attributedString: NSAttributedString) {
self.attributedString = attributedString
}

public var description: String {
return attributedString.description
}

}


このようにするとほとんど文字列と同じようにAttributedStringを作ることができます。

let attrStr: AttributedString = "新規登録は\("こちら", URL: URL(string: "http://example.com/signin")!)"

print(attrStr)

新規登録は{

}こちら{
NSLink = "http://example.com/signin";
}

Textにimageを追加するのも簡単です!

let image = UIImage(named: "sun")!

let attrStr: AttributedString = "今日はいい天気だ\(image)"

今日はいい天気だ{

}{
NSAttachment = "<NSTextAttachment: 0x60000050d490>";
}

もし自前で表現を追加したい場合はAttributedString.StringInterpolationを拡張してappendInterpolation methodを追加してください。

extension AttributedString.StringInterpolation {

// 任意のサイズのスペースを追加
public func appendInterpolation(space: CGFloat) {
let textAttachment = NSTextAttachment()
textAttachment.bounds.size.width = space
attributedString.append(NSAttributedString(attachment: textAttachment))
}
}

let attrStr: AttributedString = "ここから\(space: 100)ここまで"

UIで表示したい場合はattributedStringプロパティがあるのでそれを利用すれば表示できます。

let label = UILabel()

label.attributedText = attrStr.attributedString
label.sizeToFit()


まとめ

最初Swift5でこの機能を見たときは、ちょっと便利そうだけど使う機会あるかなと思っていました。

しかし使ってみるとかなりの神機能でNSAttributedStringをここまで進化させてくれるものとは思いませんでした。

上記AttributedString.swiftはgistにもおいておくので色々試してみてください!

https://gist.github.com/fmtonakai/4e3f6011a4a85498478047a0e742f3ec