Swiftで文字を圧縮表示したい!

本投稿は TECOTEC Advent Calendar 2021 の16日目の記事です。

みなさま、おはこんばんちは。
証券フロンティア事業部でiOSエンジニアをやっております赤池です。


皆さん、AttibutedTextの機能は使っていますか?
UILabel等のプロパティのひとつで、文字に下線を引いたり途中でフォントサイズを変えたりす等々、文字に装飾を施すときに使うアレです。
色々と文字に装飾ができて便利なのですが、開発を進めていくと、時には機能にないことを求められたりもします。

今回はそんなAttributedTextの機能にない、文字の圧縮(正しい言い方かどうかは解りませんが)を実装していくお話です。


何をしたいか

そもそも文字の圧縮とはなんぞや?
ということですが、この表現があっているか解らないので実際に画像を例に説明すると、

↑これを
↑こうしたい
ということです。


少し調べてみましたが、AttributedTextの中には該当の機能がなさそう(もしもあったら教えて下さい!)だったので、どうにかして実装してみたいと思います。

実装

では具体的にどうやって実装しようかと考えたところ、
UILabelを画像化して横方向に縮めてしまえばよいのでは?
という考えに至りました。

今のところ他に思いつかなかったのですが、他になにか良さげな方法があれば教えていただければと。


1. Storyboard

ということでまずはおなじみ、Storyboardの設定から。

今回は75%と50%の2種類を表示させようと思うので、
大本の文字列表示用のLabelと加工をした後の文字列を表示するためのUIImageViewをふたつ用意します。

配置に関してはそこまで厳密に設定しなくても大丈夫だと思いますが、念の為、widthだけはすべて同じにしたほうが良いかなともいます。

一応Storyboardの見た目だけ

Storyboard

(さり気なく、圧縮率表示のUILabelにAttributedTextを適用して、そっちもちゃんと使ってますアピール)


2. コーディング

ではお待ちかねのコードを書いていきましょう。

IBOutletで大本の文字列Labelと2つのUIImageViewをViewControllerに紐づけていきます。

名前はそれぞれ、以下の通りとしました。

/// 大本の文字列表示Label
@IBOutlet var originalLabel: UILabel!
/// 75%圧縮の文字列を表示するImageView
@IBOutlet var threeQuarterImageView: UIImageView!
/// 50%圧縮の文字列を表示するImageView
@IBOutlet var halfImageView: UIImageView!

続いてUILabel(UIView)のスクリーンショットを取得するロジックを作成します。

他でも流用できるようにUIViewの拡張として作成しました。

extension UIView {
    /// UIViewのスクリーンショットを取得し、画像化する
    func createImageFromView() -> UIImage?{
        let image: UIImage?  
        UIGraphicsBeginImageContextWithOptions(frame.size, false, 0)
        layer.render(in: UIGraphicsGetCurrentContext()!)
        image = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()  
        return image
    }
}

更に今度は、UIImageのサイズを調整するロジックを作成します。

こちらも同様にUIImageの拡張として作成しています。

extension UIImage {
    /// 画像の縦横のサイズを、指定された割合に縮める
    func resize(widthScale: CGFloat = 1.0, heightScale: CGFloat = 1.0) -> UIImage? {
        let resizedSize = CGSize(width: size.width * widthScale, height: size.height * heightScale)  
        UIGraphicsBeginImageContextWithOptions(resizedSize, false, 0.0)
        draw(in: CGRect(origin: .zero, size: resizedSize))
        let resizedImage = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()  
        return resizedImage
    }
}

ここまでできればもう少し。

あとは実際に圧縮して画面に表示させるだけです。

今回は動的に文字列を変えたりはしないため、viewDidAppear内で表示させれば大丈夫でしょう。

override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        createArchivedText()
    }  
/// 圧縮した文字列を表示する
func createArchivedText() {
        guard let textImage = originalLabel.createImageFromView() else {
            return
        }
        threeQuarterImageView.image = textImage.resize(widthScale: 0.75)
        halfImageView.image = textImage.resize(widthScale: 0.5)
    }

これでコードの実装は終わりです。

意外と簡単に実装できたんじゃないかなと思います

実行結果

それでは、実行結果を見てみましょう。こんな感じで表示されたら大成功です。

実行結果

まとめ

やる前は何じゃそりゃ? でしたが、終わってみればいい感じで圧縮表示されて満足いく結果になりました

今回はやっていませんが、heightScaleを変えれば縦方向の圧縮もできます。

なかなか使う機会は少ないかもしれませんが、
AttributedTextでは設定できない項目でもやり方を変えれば表示できるかも?
というお話でした。

tecotec.co.jp