C#
Unity

【Unity】範囲内に収まらないテキストを省略表示する

Unityでは標準機能で"テキストの長さに応じて表示範囲を広げる"ということはできるのですが、デフォルトのUI表示を崩したくないこともしばしばあります。
そこで表示範囲はそのままに、表示範囲を超える長いテキストが入っているときには"テキストが省略されている"ということを伝えられる機能を作りました。

screenshot.gif

ソースコード

【GitHub】TextEllipsis

using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;

public class UITextEllipsis : UIBehaviour
{
    private Text m_Text;

    protected override void Awake()
    {
        if (m_Text != null)
        {
            return;
        }

        m_Text = GetComponent<Text>();
        m_Text.RegisterDirtyLayoutCallback(DoEllipsis);
    }

    protected override void OnDestroy()
    {
        m_Text.UnregisterDirtyLayoutCallback(DoEllipsis);
    }

    private static readonly string ELLIPSIS = "...";

    private void DoEllipsis()
    {
        if (!IsActive() || m_Text == null)
        {
            return;
        }

        if (!NeedsEllipsis(m_Text))
        {
            return;
        }

        TextGenerator generator = m_Text.cachedTextGenerator;
        TextGenerationSettings settings = m_Text.GetGenerationSettings(m_Text.rectTransform.rect.size);
        generator.Populate(m_Text.text, settings);
        string result = string.Empty;
        for (int i = 0; i < generator.characterCount; ++i)
        {
            string current = m_Text.text.Substring(i, 1);
            string next = string.Empty;
            if (i + 1 <= generator.characterCount)
            {
                next = m_Text.text.Substring(i + 1, 1);
            }

            var preferredWidth = GetPreferredWidth(result + current + next);
            if (IsOverSize(m_Text.rectTransform.rect.size.x, preferredWidth))
            {
                result += ELLIPSIS;
                break;
            }

            result += current;
        }

        m_Text.text = result;
    }

    private bool NeedsEllipsis(Text text)
    {
        return IsOverSize(text.rectTransform.rect.size.x, text.preferredWidth);
    }

    private bool IsOverSize(float textBoxWidth, float preferredWidth)
    {
        return textBoxWidth < preferredWidth;
    }

    private float GetPreferredWidth(string str)
    {
        var settings = m_Text.GetGenerationSettings(Vector2.zero);
        return m_Text.cachedTextGeneratorForLayout.GetPreferredWidth(str, settings) / m_Text.pixelsPerUnit;
    }
}

使い方

Textコンポーネントと一緒にアタッチしておくだけ。

screenshot.png

実装の解説

アタッチするだけでリアルタイムに処理ができるのはGraphic.UnregisterDirtyLayoutCallbackのおかげです。こいつにイベントを登録すると、Text.textのsetが呼ばれたタイミングでコールバックを受け取ることができます。便利。

範囲を超えたら〜の部分は、
1. Textの変数としても用意されているTextGeneratorを利用して文字数を取得
2. 文字数に達するまで1文字目から順番にテキストを形成していく
3. 必要表示範囲を超えたら、1つ前の文字を省略記号(...)に変えて表示させる
となっています。