ScrollViewとは何か?UI Toolkit版ScrollViewの特徴と基本機能を徹底解説!

目次

ScrollViewとは何か?UI Toolkit版ScrollViewの特徴と基本機能を徹底解説!

ScrollViewは、コンテンツをスクロールして表示するためのUIコンテナです。UI ToolkitにおけるScrollViewは、表示領域(ビューポート)よりも大きなUI要素群を内部に持つ場合に、それらをスクロール可能な形で配置できます。長いリストや大量のテキストなどを限られた領域で見せる際に活躍し、ユーザーはスクロールバーやホイール操作で見切れている部分を閲覧できます。

従来のScrollRectコンポーネント)と比較して、UI Toolkit版のScrollViewはビジュアルエレメントとして実装されており、コードやUXMLで直接扱えます。本章では、UI Toolkit版ScrollViewの基本構造と機能、そしてScrollViewを使うメリットについて解説します。

ScrollViewの役割と目的:コンテンツをスクロール表示するUIコンテナ機能の基本概要を解説!

ScrollViewの主な役割は、表示エリアを超えるコンテンツをスクロール可能にすることです。例えば、画面に収まりきらない長いリストや画像一覧を扱う場合、ScrollViewでラップすることで縦方向または横方向にスクロールできるようになります。ユーザーはスクロールバーをドラッグしたりマウスホイールを回転させたりすることで、見切れているコンテンツ部分を閲覧可能です。

このように、ScrollViewは限られた画面領域で大量の情報を表示するためのUIコンテナとして機能します。スクロール機能がない場合、画面上に表示できる情報量は固定されてしまいますが、ScrollViewを利用することで画面領域を拡張し、必要に応じてユーザーに情報を提供できるようになります。

UI Toolkit ScrollViewの基本構造:ViewportとContentContainerの役割

UI Toolkit版のScrollViewは、内部にViewport(ビューポート)とContent Container(コンテンツコンテナ)という2つの主要要素を持つ構造になっています。Viewportは表示領域を表し、実際にユーザーに見える部分です。一方、ContentContainerは全ての子要素を格納する領域で、Viewportより大きくなり得るスクロール対象部分です。

ScrollViewに子要素をAddすると、自動的にContentContainerに追加されます。ContentContainerはスクロールするコンテンツ全体を含んでおり、Viewport内に収まらない部分はスクロールすることで閲覧できます。UI Toolkitでは、ContentContainerはCSSスタイルクラス.unity-scroll-view__content-containerを持ち、デフォルトで縦方向のレイアウトが適用されています。Viewport部分には.unity-scroll-view__content-viewportクラスが適用され、表示領域としてContentContainerをマスク(はみ出し部分を隠す)する役割を果たします。

このような二層構造により、ScrollViewはスクロール位置(オフセット)を管理し、ContentContainer内の要素配置とViewport内の表示を連動させています。開発者は通常、ScrollView自体に子を追加するだけでよく、内部のViewportやContentContainerを直接意識する必要はありません。ただし、後述するレイアウトのカスタマイズ(例えば折り返し配置など)ではContentContainerへのスタイル指定が重要になります。

ScrollViewが提供する主な機能と特徴:スクロールバー表示や自動スクロール挙動の仕組みを解説!

ScrollViewには、スクロール可能な領域を扱うための様々な機能が備わっています。まず、内容がViewportより大きい場合にはスクロールバーが自動的に表示されます。デフォルトでは縦スクロールバーが必要に応じて現れ、横方向のコンテンツがあれば横スクロールバーも表示されます(各スクロールバーの表示モードは後述)。ユーザーはこれらのスクロールバーをドラッグして閲覧範囲を移動できます。

また、マウスホイールやタッチパッドによるスクロール操作にも対応しており、垂直方向のスクロールが可能です。スクロール操作時には、コンテンツがスムーズに流れるように慣性スクロール(モバイルなどで指を離した後もしばらく慣性でスクロールが続く動作)やバウンス効果(行き過ぎたときに少し跳ね返る挙動)もサポートされています。UI ToolkitのScrollViewでは、これらの挙動はElasticity(弾性係数)やScroll Deceleration Rate(減速率)といったプロパティで制御可能です。

さらに、ScrollViewは垂直方向・水平方向の両方に対応しており、モードを切り替えることで縦スクロール専用横スクロール専用両方向スクロールを選択できます(デフォルトは縦スクロールのみ)。スクロール位置をプログラムから設定するためのscrollOffsetプロパティや、特定の子要素まで自動スクロールするScrollToメソッドなど、開発者向けの便利な機能も用意されています。

要約すると、ScrollViewは「見えない部分を見せる」ための土台となるUI要素であり、スクロールバーの自動管理、スクロール挙動の制御、複数方向への対応など多岐にわたる基本機能を持っています。

ScrollViewを使用するメリット:スクロールUIを実現する典型的シーンと利点を徹底解説します!

ScrollViewを利用する最大のメリットは、限られた画面領域でも情報量を犠牲にせずに表示できる点です。例えば、ゲームのインベントリ画面で多数のアイテムを一覧表示したい場合、画面全体に収めようとすると非常に小さなアイコンにするか、ページ切り替えを実装しなければなりません。そこでScrollViewを使えば、ある程度の大きさでアイコンを表示しつつ、画面に収まりきらない分はユーザーがスクロールして確認できるようになります。

また、実装面でも利点があります。ScrollViewはUI Toolkitのレイアウトシステムと統合されており、子要素の配置やサイズ調整はレイアウトエンジンに任せられます。開発者は単にコンテンツを追加していくだけで、スクロールバーの表示やコンテンツ全体のサイズ計算は自動で処理されます。ContentSizeFitterVertical Layout GroupといったuGUI特有のコンポーネントを別途設定する必要もありません。結果として、UIを組む手間が減り、動的なコンテンツにも柔軟に対応できます。

ユーザー体験の面でも、ScrollViewがあることで情報閲覧の自由度が高まります。ユーザーは必要に応じてスクロールし、興味のある部分だけを見ることができます。画面遷移なしに情報を深掘りできるため、UX向上にも寄与します。ただし、多数の要素をScrollViewに詰め込みすぎるとパフォーマンスに影響する場合があります。その場合は次節で触れるListViewの活用なども検討しましょう。

ScrollViewとListViewなど他のUIコンポーネントとの関係:適材適所の使い分けを解説!

UI ToolkitにはScrollViewの他に、リスト表示専用のコンポーネントとしてListViewがあります。ListViewは内部でScrollView相当のスクロール機能を持ちながら、項目の仮想化(表示領域に入る分だけ要素を生成し、スクロールに応じて再利用する)など、大量データの表示に特化した機能を備えています。少数の要素やレイアウト自由度が高いUIであればScrollViewに自前で要素を追加して問題ありませんが、数百〜数千件に及ぶデータ一覧を表示する際にはListViewを使う方が効率的です。

たとえば、チャットログやランキングリストなど多数の項目を含むUIでは、ScrollViewに全要素を生成するとメモリ使用量や生成コストが増大します。ListViewはデータソースとバインドすることで、表示に必要な要素だけを生成し、スクロールに応じて内容を入れ替えるため、パフォーマンス上有利です。一方、レイアウトの柔軟性や複雑な構造を表示したい場合は、自由に要素を配置できるScrollViewの方が適しています。

このように、ScrollViewとListViewは用途に応じて使い分けるのがポイントです。UI Toolkitでは適材適所でこれらを活用し、快適なUIを実現できます(ListViewの詳細な使い方や特徴は後述のセクションで詳しく説明します)。

UI ToolkitでScrollViewを作成する手順:UI BuilderとUXMLの活用完全ガイド

ここでは、UI ToolkitにおけるScrollViewの作成方法を紹介します。Unityにはエディタ上でUIをデザインできるUI Builderというツールがあり、それを用いるとコードを書かずにScrollViewを配置・設定可能です。また、XML形式のレイアウト定義ファイルであるUXMLを記述してUIを構築することもできます。さらに、C#スクリプトから動的にScrollViewを生成・配置する方法も押さえておきましょう。以下では、それぞれの手順とポイントについて順を追って解説します。

UI Builderを使ったScrollViewの作成手順:ビジュアルエディタでUIを構築する流れを解説

UI Builderを使用すると、ドラッグ&ドロップの直感的な操作でScrollViewを含む各種UI要素を配置できます。UI Builder上でのScrollView作成の基本的な流れは次のとおりです。

  • HierarchyビューまたはライブラリからScrollViewを選択し、UIツリー上の適切な場所にドラッグします。
  • ScrollViewが配置されたら、Inspectorパネルで各種プロパティを設定できます。例えば、スクロールモード(Mode)やスクロールバーの表示設定、サイズ(Width/Height)などを指定します。
  • 必要に応じて、ScrollViewの子要素(コンテンツ)を同様にUI Builder上で追加します。ScrollViewに他のUI要素(LabelやButtonなど)をドラッグして入れると、それらは自動的にContentContainerに配置されます。
  • 画面上でScrollView領域を視覚的に確認しながら配置やスタイルを調整します。UI Builder上ではスクロールバーはプレビューされますが、コンテンツ量次第で自動表示されることを念頭に置きます。

以上の手順で、コードを書かずにScrollView付きのUIレイアウトを構築できます。UI BuilderではInspector上でModeプロパティ(Vertical/Horizontal/Both)をドロップダウンから選択できたり、スクロールバー表示モード(後述)を設定できたりするため、視覚的なUI設計が容易です。

UXMLによるScrollViewの記述方法:XMLレイアウトファイルでの定義手順とポイントを紹介します

UXMLを直接記述してScrollViewを作成することもできます。UXMLはXML形式でUI構造を表現するファイルで、エディタからではなくテキストベースでUIを定義したい場合に有用です。ScrollViewをUXMLで記述する際の基本構文は以下のようになります。

<ScrollView mode="Vertical" vertical-scroller-visibility="Auto" height="400" width="250"> <Label text="このようにUXMLで子要素を記述できます" /> </ScrollView>

上記の例では、ScrollViewタグを用いて垂直スクロール可能(mode="Vertical")なScrollViewを定義しています。属性でスクロールバーの可視性(vertical-scroller-visibility="Auto")やサイズ(高さ・幅)も指定可能です。子要素として

UXMLでのポイントは、各種プロパティに対応する属性名を正しく指定することです。例えば、Modeプロパティはmode属性、垂直スクロールバー表示はvertical-scroller-visibility属性、スクロール速度関連ではvertical-page-sizemouse-wheel-scroll-size属性などがあります。UI Builderで設定した内容は裏側ではUXML属性として記述されているため、一度UI Builderで配置してからUXMLを開いて確認すると理解が深まります。

C#スクリプトでScrollViewを生成・配置する方法:コードからUI要素を動的に作成する手順を解説

コード(C#)からScrollViewを生成してUIに配置することも可能です。これは、ランタイムに動的なUIを構築したい場合や、より柔軟にUIを制御したい場合に有用です。基本的な手順は以下のとおりです。

// 1. ScrollViewオブジェクトを生成し、スクロールモードを指定(この例では縦スクロールのみ) var scrollView = new ScrollView(ScrollViewMode.Vertical); // 必要に応じてサイズやスタイルを指定 scrollView.style.width = 250; scrollView.style.height = 400;
// 2. ScrollViewにコンテンツを追加(例として複数のラベルを追加) for (int i = 1; i <= 20; i++) { scrollView.Add(new Label($"アイテム {i}")); }
// 3. 作成したScrollViewをUIツリーに追加(ルートのVisualElementに追加する場合) rootVisualElement.Add(scrollView);

上記のコードでは、新しいScrollViewを生成し、コンストラクタ引数でScrollViewMode.Verticalを指定しています。これにより縦スクロール可能なScrollViewが作られます。続いて、ループでLabelを20個生成してscrollView.Add(...)で追加しています。最後に、既存のUIツリー(ここではrootVisualElement)にAddしてScrollView自体を表示させています。

このようにコードから生成する方法では、実行時の状況に応じてScrollViewを構築できます。UI BuilderやUXMLによる静的配置と組み合わせ、初めに空のScrollViewを配置しておき後からコードで要素を差し込む、といった柔軟な使い方も可能です。

UI BuilderでのScrollView初期設定:スクロール方向やサイズ・スタイルの指定方法を解説

UI Builderを用いてScrollViewを配置した際には、Inspectorで様々な初期設定が可能です。例えば、スクロール方向(Modeプロパティ)はInspector上でVerticalHorizontalVertical And Horizontalから選択できます。また、Vertical Scroller VisibilityHorizontal Scroller Visibilityの項目で、それぞれ縦横スクロールバーの表示モード(Auto/常に表示/非表示)を指定できます。

サイズに関しても、ScrollView自体のWidthやHeightをInspectorで設定できます。固定サイズにする場合は数値を入力し、レイアウトに応じて柔軟に伸縮させたい場合はflex-growの値を調整することで親要素内で余白を埋める動的サイズにもできます。スタイルシート(USS)クラスを割り当ててデザインを調整することも可能で、スクロールバーの見た目や背景色などもカスタマイズできます。

UI Builder上のこれらの設定は、裏でUXML属性やUSSスタイルとして保存されます。例えば、InspectorでModeを「Vertical And Horizontal」に設定すると、UXMLにmode="VerticalAndHorizontal"が追記されます。開発者はUI Builderを使うことで視覚的にこれらのパラメータをいじれるため、ScrollViewの初期状態を直感的にデザイン可能です。

ScrollViewをUIに追加する際の注意点:配置順序や他UI要素との干渉に関するポイントを解説します

ScrollViewをUIに組み込む際にはいくつか注意すべきポイントがあります。まず、UIツリー上での配置順序です。ScrollViewは他のコンテナと同様にVisualElementとして扱われるため、UIツリー内での位置によってレイアウトへの影響が変わります。例えば、ScrollViewを複数並べる場合、それぞれのサイズや配置に応じて隣接要素との干渉(はみ出しなど)がないか確認しましょう。必要に応じてstyle.flex-shrinkoverflowスタイルを調整し、他要素とレイアウトがぶつからないようにします。

また、ScrollView内部にボタンやテキストフィールドなどインタラクティブな要素を配置する場合、その操作とスクロール操作が競合しないかにも注意してください。例えば、ScrollView内のボタンをクリックしたときに、そのままドラッグするとスクロールが発生してしまう場合があります。UI Toolkitではデフォルトでコンテンツのマウスドラッグによるスクロールは無効ですが、タッチ操作環境(モバイル)ではドラッグでスクロールできるため、意図しない挙動にならないかテストが必要です。

最後に、ScrollViewはコンテンツサイズに応じて自らのサイズを変化させません(ContentContainerが内部で大きくなるだけで、ScrollView自体の矩形は固定)。そのため、ScrollViewをレイアウト内に配置するときは、あらかじめ想定するサイズを割り当てておくことが重要です。高さを指定しないとScrollView自体も高さ0になりコンテンツが見えない、といったミスが起こりがちなので注意しましょう。

ScrollViewの縦スクロール・横スクロールの設定方法とModeプロパティの詳細解説(設定例付き)

ScrollViewは縦方向・横方向いずれか、または両方向へのスクロールをサポートしています。どの方向にスクロール可能とするかはModeプロパティによって制御されます。デフォルトでは縦スクロールのみ有効ですが、用途に応じて横スクロールや両方向スクロールに切り替えることができます。本章ではModeプロパティの基本と、縦専用・横専用・両対応それぞれの設定例を紹介します。適切にModeを設定することで、不要なスクロールバーを非表示にしたり、必要な方向のスクロールだけを提供したりできます。

ScrollViewのスクロール方向を制御するModeプロパティ:Vertical/Horizontal設定の基本

UI ToolkitのScrollViewにはScrollViewModeという列挙型があり、これがスクロール可能な方向を表します。主な選択肢は以下の3つです。

  • Vertical(垂直): 縦方向のスクロールのみ許可します(デフォルト)。
  • Horizontal(水平方向): 横方向のスクロールのみ許可します。
  • VerticalAndHorizontal(両方向): 縦横両方向のスクロールを許可します。

Modeプロパティは、ScrollView作成時や設定変更時に指定できます。C#コードであれば、コンストラクタにScrollViewModeを渡すか、後からscrollView.mode = ScrollViewMode.Horizontal;のように設定します。UXMLではmode属性としてこれらの値を文字列で指定できます。UI BuilderではInspector上でドロップダウンから選択可能です。

Modeプロパティを適切に指定することで、不要なスクロールバーが表示されるのを防ぎ、UIの見栄えや使い勝手を向上させられます。例えば横スクロールの必要がないリスト表示であればVerticalに固定することで、誤って横スクロールバーが出現することを防止できます。

縦スクロール専用のScrollView設定例:Verticalモードで縦方向スクロールに特化する方法

垂直方向のスクロールだけを提供する場合、ScrollViewのModeをVerticalに設定します。これはデフォルト状態なので特別な指定をしなくても有効ですが、UXMLで明示したい場合やコードで後から変更する場合はscrollView.mode = ScrollViewMode.Vertical;のように指定します。

設定例: UI BuilderのInspectorでModeを「Vertical」に設定し、高さを例えば400px、幅をコンテンツに合わせて柔軟に伸縮するようstretch(親要素の幅いっぱい)に設定します。このScrollViewにテキストの長文やリスト項目を追加すれば、垂直方向にのみスクロールバーが表示され、ユーザーは上下に内容を閲覧できます。横方向にはスクロールバーが表示されず、コンテンツが横に長すぎる場合は自動的に折り返し(またはクリップ)されます。

縦スクロール専用にすることで、ユーザー体験としては一般的なスクロールリストや文章表示と同じ感覚で操作できます。一方、横方向のコンテンツが見切れる場合には別途対処が必要です(例えば、水平方向にも見せたい場合は次項のHorizontalモードを検討します)。

横スクロール専用のScrollView設定例:Horizontalモードで横方向スクロールを実現する方法

水平方向のスクロールのみを提供したい場合、ModeをHorizontalに設定します。これは例えば、横に長い画像ギャラリーやタイムライン表示などで利用されます。

設定例: Modeを「Horizontal」に設定し、幅を例えば600px、高さを200pxに固定したScrollViewを作成します。中に幅の広いコンテンツ(例えば横に並べた画像サムネイルや、横長のグラフ等)を配置すると、ScrollViewに下部スクロールバーが表示されて左右に内容を閲覧できるようになります。縦方向のスクロールバーは表示されず、コンテンツが縦に大きくはみ出す場合はその部分は切り取られる(または別の工夫が必要)ことに注意してください。

横スクロール専用のUIはあまり一般的ではありませんが、特定のデザインや機能(例えば音楽編集ソフトのシーケンサー画面など)で必要になります。UI ToolkitのScrollViewならModeを変えるだけで簡単に横スクロール対応できるのが利点です。

縦横両対応のScrollView設定例:VerticalAndHorizontalモードの活用と利点

縦にも横にもコンテンツがはみ出す可能性がある場合、ModeをVerticalAndHorizontalに設定することで両方向のスクロールを提供できます。例えば、大きな地図や表を表示するUIでは両方向スクロールが必要になるでしょう。

設定例: Modeを「Vertical And Horizontal」に設定し、ScrollViewのサイズを例えば400x300px程度の枠とします。その中に大きな図やグリッドレイアウトを配置すると、右側と下側の両方にスクロールバーが表示されます。ユーザーは上下左右にドラッグしてコンテンツ全体を閲覧できます。

縦横両対応のScrollViewは便利ですが、二次元方向へのナビゲーションとなるためユーザーにとっては若干扱いが難しい場合もあります。スクロールバーが二本出ることでUIがごちゃつく可能性もあるため、本当に必要な場合にのみ採用すべきです。必要に応じてスクロールバーのスタイルを調整して目立たせない工夫(透過させる、細くする等)をすることもあります。

UI Builderでスクロール方向Modeを設定する方法:InspectorからVertical/Horizontalを選択

UI Builderを使う場合、ScrollViewを選択した状態でInspectorパネルを見ると、Modeという項目があります。ここで「Vertical」「Horizontal」「Vertical And Horizontal」の3つの選択肢から希望のスクロール方向モードを選べます。クリックひとつでModeプロパティを設定できるため、直感的です。

また、Inspectorには関連してVertical Scroller VisibilityHorizontal Scroller Visibilityも表示されています。これは各スクロールバーを「Auto(必要なときのみ)」「Always Visible(常に表示)」「Hidden(常に非表示)」から選択するオプションです。モードと組み合わせて、たとえば「縦横両方向スクロール可能だが横スクロールバーは常に表示しない」といった細かい設定も可能です。

UI Builder上でこれらを設定すると、UXML上には対応する属性が追記されます(mode属性やhorizontal-scroller-visibility属性など)。コードに不慣れな場合でもUI Builderで正しく設定すれば、その内容がそのまま適用されるので安心です。設定後はプレビューでスクロールバーの挙動を確認し、狙い通りの表示になっているかチェックしましょう。

ScrollViewに要素を動的に追加・削除する方法(C#スクリプト)

ScrollViewは静的なUIだけでなく、実行時に動的にコンテンツを追加・削除する用途にも適しています。例えば、チャットログに新しいメッセージをスクロールビューの末尾に追加したり、リストからアイテムを削除したりする操作です。ここでは、C#スクリプトからScrollViewのコンテンツを動的に変更する方法について解説します。具体的には、VisualElementをコードで追加・削除する基本、削除メソッドの使い分け、動的変更時の注意点、実装例のコードなどを紹介します。

ScrollViewに要素を動的に追加する基本手順:VisualElementをAddで子要素として挿入

C#スクリプトからScrollViewに要素を追加するには、基本的にScrollView.Add(VisualElement child)メソッドを呼び出します。ScrollViewはVisualElementの一種であり、他のコンテナと同様にAdd関数で子要素を増やせます。動的追加の手順は次のようになります。

  1. 追加するUI要素(例:LabelButtonなど)を生成します。
  2. その要素をScrollViewのインスタンスに対してAddします。すると、自動的にScrollViewのContentContainerに子要素として組み込まれます。
  3. ScrollViewは新しいコンテンツを含めて内部レイアウトを再計算し、必要であればスクロールバーのサイズなども自動調整します。

例えば、空のScrollViewmyScrollViewに対して、新規のLabelを動的に追加するコードは以下のようになります。

var newLabel = new Label(\"追加された項目\"); myScrollView.Add(newLabel);

この2行で、ScrollView内に新しいラベルが末尾に追加され、画面上にも即座に反映されます。UI Toolkitでは、この追加操作はフレーム内で処理され、次のリフレッシュでスクロールバーの長さなども更新されます。特に何も追加の処理をしなくても、要素の追加数に応じてスクロール範囲が拡大し、ユーザーは新たな要素をスクロールして見られるようになります。

ScrollViewから要素を削除する方法:RemoveとClearを使った削除処理の手順を解説します

ScrollViewから子要素を取り除くには、RemoveもしくはRemoveAt、あるいは全要素を消去する場合はClearメソッドを利用します。用途に応じて使い分けます。

  • Remove(childElement): 指定した子要素をScrollViewから削除します。例えばmyScrollView.Remove(label)のように使用します。
  • RemoveAt(index): インデックスを指定して削除します。どの順番の子を消すか明確な場合に使用します。
  • Clear(): 全ての子要素を一括削除します。内容をリセットしたい場合に便利です。

削除の基本手順は、対象のUI要素(VisualElement)を特定し、Removeメソッドに渡すだけです。例えば、一番上の要素を削除したい場合: var firstItem = myScrollView.Children().First(); myScrollView.Remove(firstItem); のようにします。全削除する場合は単に myScrollView.Clear(); を呼びます。

注意点として、要素を削除した直後はScrollView内の残りの要素のレイアウトが自動で再計算され、スクロールバーの長さも必要に応じて縮小されます。ただし、ScrollViewの現在のスクロール位置が末尾付近だった場合、削除後はその位置がコンテンツ範囲外になることがあります。その際、自動でスクロール位置が調整される(例えばスクロール範囲外に行かないよう最下部に戻される)挙動が起こり得ます。連続してRemoveとAddを行うような場合、スクロール位置が飛ぶ可能性がある点は留意しましょう。

UIElementsでUIを動的変更する際の注意点:変更の即時反映とスクロールバー更新の挙動について

UI ToolkitのUIを動的に変更(要素の追加・削除)した場合、基本的には次のフレームでレイアウト計算が走り、表示が更新されます。つまり、多くの場合AddRemoveを呼んだ直後にUIが切り替わります。ただし、注意すべきはスクロールバーの更新タイミングです。要素を追加/削除した直後、フレームが更新されるまではスクロール範囲(スクロールバーの長さなど)が古い状態のことがあります。

通常はごく短時間(1フレーム内)で再計算・再描画が行われるため問題になりませんが、特定のタイミングでScrollViewのプロパティを参照すると古い値のままの場合があります。例えば、子要素を大量にClear()で削除し、その直後のコードでスクロール位置を末尾に設定する、という処理を1フレーム内で行うと、本来要素が無くなったので末尾=先頭になるはずが古い範囲を参照してズレが生じる、といったケースです。

このような場合には、次章で述べるスクロールバーの再描画(再計算)を強制するテクニックが必要になることがあります。ただ、多くのシナリオではフレーム更新に任せて問題ありません。UIを動的変更する際は、必要に応じてschedule.Execute()で1フレーム遅らせてから処理するなど、安全にUIが更新された後で追加の操作をする工夫も時折用いられます。

実装例:ボタン操作でScrollViewにアイテムを追加・削除するC#コードの詳細を詳しく解説します!

実際のユースケースとして、ボタンを押すとScrollViewに新しい項目を追加し、別のボタンで直近の項目を削除する、といった機能を考えてみましょう。以下に簡単なC#コード例を示します。

// ScrollViewとボタンの参照を取得(UI Builderからパーツを取得している想定) var scrollView = root.Q<ScrollView>(\"chatScrollView\"); var addButton = root.Q<Button>(\"addButton\"); var removeButton = root.Q<Button>(\"removeButton\");
// ボタン押下時のコールバック設定 addButton.clicked += () => { // 新しいメッセージラベルを生成してScrollViewに追加 var message = new Label($\"メッセージ {scrollView.childCount + 1}\"); scrollView.Add(message); };
removeButton.clicked += () => { // ScrollView内の最後の子要素を削除(存在する場合) if (scrollView.childCount > 0) { var lastIndex = scrollView.childCount - 1; var lastItem = scrollView.ElementAt(lastIndex); scrollView.Remove(lastItem); } };

このコードでは、addButtonがクリックされるたびに新しいLabelを作ってScrollViewに追加しています。またremoveButtonでは、ScrollViewが持つ子要素の数(childCount)を調べ、最後の要素を削除するようにしています。このような処理を書くだけで、実行時にボタン操作でScrollView内のコンテンツがどんどん増減していくUIが実現できます。

ポイントは、ScrollViewが現在持っている子要素数をchildCountで取得できること、ElementAt(index)で特定インデックスの子要素にアクセスできることです。UI Toolkitでは子要素はリスト的に管理されているため、末尾要素はchildCount - 1番目として取得できます。削除後は即座にレイアウトが再計算され、必要であればスクロールバーの表示範囲も縮小されます。

この実装例のように、ScrollViewを用いた動的UIは比較的シンプルなコードで構築できます。追加・削除を繰り返しても自動でスクロール可能領域が更新されるため、リアルタイムアプリケーション(チャット、ログビューア等)に適しています。

大量の要素をScrollViewに追加する際のパフォーマンス考慮:ListView活用も含めた対策と工夫

ScrollViewは手軽に要素を追加できますが、大量の要素を直接ScrollViewに追加する場合、パフォーマンス面で注意が必要です。例えば数千件のログメッセージをすべてScrollView内にAddすると、全てのLabelが生成・レイアウトされるため、メモリ使用量や描画コストが増大します。そのようなケースでは、以下の対策や工夫を検討してください。

  • ListViewの活用: 先述したように、大量データ表示には仮想化機能を持つListViewを使うのが効果的です。ListViewは表示領域に収まる要素のみ生成し、スクロールに合わせて内容を更新するため、1000件以上のデータでもスムーズに扱えます。
  • 要素の再利用: ScrollView自体には仮想化機能がありませんが、手動で要素を再利用することも可能です。例えば、ログ表示で古いログは使い回して内容を書き換えるなどの工夫です。ただし実装は複雑になるため、ListViewを使えるならそちらを選ぶ方がよいでしょう。
  • 間引き表示: 全てを表示せず、一部のみを表示する(ページングする)方法もあります。ユーザーが特に必要としない古い情報は省略し、「もっと見る」ボタン等で追加読み込みするUXも検討できます。

このように、ScrollViewは万能ではありますが、大量データには専用の仕組み(ListViewなど)を使った方が結果的に快適になります。シンプルなUIや少数の要素であればScrollViewで十分対応できますが、要件に応じて適切なコンポーネントを選択することが重要です。

ScrollView内の要素をWrap表示する方法(flex-directionとflex-wrap)

通常、ScrollView内のコンテンツは縦方向に一直線に並ぶか、横方向に一直線に並ぶかのレイアウトになります。しかし、例えば画像サムネイルを複数行にわたって並べたい場合など、内容を折り返して(Wrapして)表示したいことがあります。UI ToolkitのScrollViewでは、内部コンテナ(ContentContainer)のレイアウトスタイルを変更することでこれを実現できます。ここでは、flex-directionflex-wrapプロパティを使ってScrollView内の子要素を複数行に配置する方法を説明します。

ScrollView内で子要素を横並びに配置する方法:contentContainerにflex-direction: rowを設定

デフォルトのScrollViewでは、ContentContainerのflex-directionは縦方向(column)になっています。そのため子要素は縦に積み重なるレイアウトです。これを横並びに変更するには、ContentContainerのスタイルに対しflex-direction: rowを設定します。具体的には、USS(スタイルシート)やC#コードでContentContainerにアクセスし、プロパティを変更します。

USSで指定する場合、ScrollViewのContentContainerにはクラス.unity-scroll-view__content-containerが付与されています。従って、USSファイルに次のような記述を追加します。

.unity-scroll-view__content-container { flex-direction: row; }

これにより、全てのScrollViewのコンテンツコンテナが横方向レイアウトになります(個別に適用したい場合は自前のクラス名を付与して指定します)。C#コードで動的に設定する場合は、myScrollView.contentContainer.style.flexDirection = FlexDirection.Row;といった形でプロパティを変更できます。

flex-direction: rowを適用すると、ScrollView内部の子要素は左から右へ横並びに配置されます。子要素がScrollViewの幅を超える場合、横方向にスクロールバーが現れます。縦方向には基本的に子要素一列分の高さしか取りません。これが横並び配置の基本です。

要素を折り返して表示させるflex-wrap設定:コンテナにflex-wrap: wrapを指定する効果

横方向に並べた要素がScrollViewの幅を超えた場合、そのままだと横スクロールバーが出て延々一行で続くだけです。しかし、flex-wrap: wrapを指定すると、行がいっぱいになった時に次の行に折り返して配置することができます。ContentContainerに対してflex-wrap: wrapを有効にすると、複数行のレイアウトが可能になります。

先ほどのUSS例に続けて、.unity-scroll-view__content-containerに対し次の指定を追加します。

.unity-scroll-view__content-container { flex-direction: row; flex-wrap: wrap; }

これで、ScrollView内の子要素は横方向に並び、横幅いっぱいになったら新しい行に折り返されます。C#では、myScrollView.contentContainer.style.flexWrap = Wrap.Wrap;と設定することで同様の結果が得られます。

flex-wrap: wrapの効果により、ScrollView内で自然な折り返しレイアウトが可能となります。例えば画像ギャラリーであれば、横に4枚ずつ画像を並べ、5枚目からは次の行に配置する、といったレイアウトが簡単に実現できます。この場合、ScrollViewの垂直スクロールバーが有効になり、複数行にわたるコンテンツ全体を上下にスクロールして閲覧できるようになります。

UI Builderでflex-wrapを適用する方法:USSスタイルシートでScrollViewのcontentContainerを指定

UI Builder上では、ScrollViewのContentContainer自体を直接選択・編集することはできません。しかし、スタイルシートを利用してContentContainerに対するスタイルを適用できます。UI Builderのスタイルシートエディタで、新規クラスセレクタとして.unity-scroll-view__content-containerを追加し、そのスタイルとしてFlex Direction = RowFlex Wrap = Wrapを設定します。

UI Builderでは実行時の挙動を完全にプレビューできないため、折り返し表示を確認するにはある程度コンテンツを入れてPlayモードでテストする必要があります。それでも、スタイルシートを用いることでビジュアルエディタ上からWrap設定を組み込めるので、手作業でUSSを書くのが難しい場合にはUI Builderの補助を活用すると良いでしょう。適用されたUSSはプロジェクト内のスタイルシートに保存され、ビルド時にも反映されます。

Wrap表示を活用したレイアウト例:複数列グリッドのスクロールビューを実現する手法と応用例を解説します

flex-wrapを活用すると、ScrollView内でグリッド状のレイアウトを実現できます。例えば、商品カタログのサムネイル一覧を4列×N行で表示し、縦にスクロールできるUIを考えてみます。

ContentContainerにflex-direction: rowflex-wrap: wrapを設定し、各サムネイル(例えばVisualElementに背景画像を設定したもの)に固定の大きさ(例えば幅100px・高さ100px、マージン適宜)を与えます。さらに、ScrollView自体の幅を4つのサムネイル+マージンが収まるサイズに設定します。すると、1行に4つのサムネイルが並び、5つ目以降は自動で次の行に折り返されます。

この状態でScrollViewを縦にスクロールすると、複数行にわたる画像グリッドを上下に閲覧できるようになります。横スクロールバーは基本的に表示されず、常に4列で整列されるため、ユーザーは縦方向のスクロールのみ意識すれば済みます。まさにWebページのサムネイルギャラリーのようなレイアウトが実現できるわけです。

この応用により、従来はGrid Layout Groupなどを用いていたようなUIも、UI ToolkitのScrollView+フレックスレイアウトだけで表現可能になります。複数列のリストや写真ギャラリー、カードUIの一覧表示など、flex-wrapを活かせる場面は多くあります。

Wrap表示時の注意点:スクロール方向の組み合わせや要素サイズ調整に関する重要な考慮事項を解説します

ScrollViewで折り返し表示を行う際には、いくつか注意すべき点があります。まず、スクロール方向のモードです。通常、折り返し表示する場合は縦スクロールにして横方向にはスクロールさせない(横は折り返しで全表示)ケースが多いです。そのため、ModeプロパティはVerticalのままとし、横スクロールバーは不要なのでHorizontal Scroller VisibilityをHiddenに設定する、といった配慮が必要です。そうしないと、微妙な横方向の余白によって不要な横スクロールバーが出現してしまうことがあります。

次に、子要素のサイズ設定です。折り返しレイアウトでは各要素が均一なサイズであることが望ましいです。サイズがバラバラだと、思わぬ位置で折り返されたりレイアウトが崩れたりします。UI Toolkitのフレックスレイアウトでは各要素の幅/高さやflex-grow設定により配置が変わるため、グリッド状にする場合は各要素に固定幅(または同じ比率で伸縮する設定)を与えるのが安全です。

さらに、非常に多くの要素を折り返し表示する場合、ListViewを使えない(複数列のListViewは仮想化が効かない)ため、パフォーマンスに注意しましょう。数十程度の項目であれば問題ありませんが、数百を超える場合はページングやフィルタリングで表示数を減らす工夫も必要です。

最後に、UI Toolkitの特性としてContentContainerがデフォルトでは無限サイズ(子要素に応じて大きくなるだけで自身には制限がない)であるため、折り返し表示させる際にはScrollViewの幅を適切に制限することが前提となります。幅が無限大だとそもそも折り返されず一行に並び続けてしまうため、ScrollViewのサイズ設定は必ず行いましょう。以上の考慮事項を押さえれば、Wrap表示によるレイアウトを効果的に活用できます。

スクロールバーの表示/非表示とスクロール速度の調整方法

ScrollViewでは、スクロールバーの表示タイミングや速度に関する挙動をカスタマイズできます。縦横それぞれのスクロールバーを常に表示したり自動表示に任せたり、あるいはまったく非表示にすることも可能です。また、スクロールの速度(ページスクロール量やマウスホイールの感度)もプロパティで調整できます。ここでは、スクロールバーの表示モード設定、UI BuilderおよびUXML/C#での指定方法、さらにスクロール速度をコントロールする各種プロパティについて説明します。

スクロールバーの表示モード設定:Auto・Always Visible・Hidden各オプションの意味

ScrollViewの縦横スクロールバーには、それぞれ表示モードを設定できます。表示モードは以下の3種類です。

  • Auto: デフォルトの挙動です。コンテンツがはみ出している場合にスクロールバーを表示し、収まっている場合は表示しません。
  • Always Visible: 常にスクロールバーを表示します。コンテンツが短くてもスクロールバーのトラック(背景)が表示され、つまみ(ハンドル)は適切な長さになります。
  • Hidden: 常にスクロールバーを表示しません。コンテンツが長くてもスクロールバーを一切描画しません。

必要に応じて、縦と横で別々のモードを指定できます。例えば、縦スクロールバーはAuto、横スクロールバーはHiddenといった組み合わせが可能です。AutoはUIとして自然ですが、スクロール可能かどうか視覚的に分からない欠点もあります。そのため、UXによってはAlways Visibleにしてスクロール可能であることを常に示すほうが良い場合もあります。一方、特定のデザインではスクロールバーが邪魔になるのでHiddenにしてしまい、代わりにコンテンツ内で独自スクロールUIを実装する例もあります。

UI Builderでスクロールバーの表示/非表示を設定する手順:InspectorでのVertical/Horizontal Scroller Visibility

UI BuilderのInspectorでScrollViewを選択すると、Vertical Scroller VisibilityHorizontal Scroller Visibilityという設定項目が表示されます。ここでそれぞれのスクロールバーについてAuto/Always Visible/Hiddenを選択できます。例えば縦スクロールバーを常に表示したい場合、Vertical Scroller VisibilityをAlways Visibleに設定します。逆に横スクロールバーを隠したい場合、Horizontal Scroller VisibilityをHiddenにします。

UI Builderを使う利点は、これらをプレビューしながら調整できる点です。もっとも、実際にはコンテンツがあふれた状態でないとスクロールバーは表示されないため、プレビューではAlways Visibleにしても薄いバーが出るだけかもしれません(UI Builder上はコンテンツ次第でAutoとHiddenの違いが分かりづらいことがあります)。そのため、最終的な挙動確認は実行時になりますが、設定自体はUI Builderで完了できます。

UXMLやC#コードでスクロールバー可視性を指定する方法:horizontal-scroller-visibility属性など

コードまたはUXMLでスクロールバー表示モードを設定する場合、以下のプロパティ/属性を使用します。

  • UXML属性: vertical-scroller-visibility, horizontal-scroller-visibility。値として Auto, AlwaysVisible, Hidden を指定します。
  • C#プロパティ: scrollView.verticalScrollerVisibility, scrollView.horizontalScrollerVisibility。値はScrollerVisibility列挙型で、同様にAuto/AlwaysVisible/Hiddenがあります。

例えば、UXMLで縦スクロールバーのみ常時表示にするには <ScrollView vertical-scroller-visibility="AlwaysVisible" horizontal-scroller-visibility="Auto"> のように記述します。コードでは myScrollView.verticalScrollerVisibility = ScrollerVisibility.AlwaysVisible; のように設定します。

これらの設定により、UIのスクロールバー表示ルールを細かく制御できます。デフォルト(Auto)で問題ないケースがほとんどですが、UIデザインや要件に応じて変更したいときに役立ちます。

スクロール速度を調整するプロパティ:vertical-page-size・horizontal-page-sizeの設定方法

ScrollViewでは、スクロールの速度やステップ量を調整するためのプロパティが用意されています。特にキーボード操作やスクロールバーの矢印ボタンによるスクロール量をコントロールするのがページサイズプロパティです。

  • vertical-page-size: キーボードやスクロールバーの矢印ボタンで縦スクロールする際の1ステップ量を制御します。値は既定では0(自動計算)ですが、大きくするほど速く(たくさん)スクロールします。
  • horizontal-page-size: 横スクロールの場合の1ステップ量を制御します。考え方はvertical-page-sizeと同様です。

UI BuilderのInspector上でも「Vertical Page Size」「Horizontal Page Size」としてこれらを設定できますし、UXML属性やC#プロパティでも指定可能です。例えば、縦方向のスクロールを素早くしたい場合、vertical-page-sizeをデフォルトより大きめの値(例えば2や3)にします。値を2にすると、1回のキー押下で見える範囲の2ページ分進む計算になり、結果としてスクロール速度が2倍になります。

逆に値を小さくするとゆっくり細かくスクロールするようになります。ただし、通常はデフォルトの0(UI Toolkit側で適切に計算)で問題ないケースがほとんどです。特定の用途で微調整したい場合にのみ変更を検討すると良いでしょう。

マウスホイール速度とタッチ操作の慣性を制御:mouse-wheel-scroll-sizeやscrollDecelerationRateの活用

マウスホイールでスクロールする際の速度も調整可能です。mouse-wheel-scroll-sizeプロパティは、ホイール1ノッチあたりにスクロールする量を設定します。デフォルトでは18px相当の1行分ずつスクロールする設定になっていますが、この値を大きくするとホイール1回転で進む距離が増え、スクロールが速く感じられます。例えばmouse-wheel-scroll-sizeを36に設定すると、ホイール操作で2行分ずつ進むイメージになります。

タッチ操作(フリック)時の慣性スクロールに関しては、scrollDecelerationRateプロパティで制御できます。この値はスクロールの減速率を表し、0に近づけると減速が遅くなり長く慣性でスクロールし続け、値を大きくすると早めに止まります。デフォルト値は0.135(1秒間でだいたい1/8になる減速)程度に設定されています。scrollDecelerationRateを0にすると指を離した瞬間ピタッと止まる挙動、0.5にすると減速が緩やかでしばらく滑るような挙動になります。

さらに、elasticityプロパティもあります。これはスクロール範囲の端を超えようとしたときのバウンス(跳ね返り)効果の強さです。値を大きくするとゴムのようによく伸びて跳ね返りも大きくなり、0にすると境界でピタッと止まります。タッチUIでは適度な弾性がUX向上につながりますが、デザイン次第では0にしてバウンスしないようにすることも可能です。

これらのプロパティはUI Builderでは「Scroll Deceleration Rate」や「Elasticity」としてInspectorに表示される場合があります。また、UXML属性(scroll-deceleration-rate, elasticityなど)やC#コードでも設定できます。スクロール速度や挙動を環境や好みに合わせてチューニングしたい場合には非常に便利な機能です。

ScrollViewとListViewをバインドして一覧を表示する

UI Toolkitで大量のデータ一覧を表示する場合、ScrollView単体で実装するよりもListViewコンポーネントを使った方が効率的です。ListViewはScrollViewを拡張したリスト表示専用のUI要素で、データソースとバインドして項目を表示したり、要素の再利用(仮想化)によってパフォーマンス高く動作します。本章ではListViewとは何か、ScrollViewとの違い、基本的な使い方、そしてScrollViewとListViewの使い分けについて説明します。

ListViewとは何か:ScrollViewを拡張した効率的なリスト表示コンポーネントの概要を解説

ListViewは、UI Toolkitに用意されているリストデータ表示用のコンポーネントです。一見するとScrollViewと似ていますが、内部的にはScrollViewをベースに、リスト項目の管理ロジックが追加されています。ListViewはデータの配列やリスト(IListなど)に対してUIをバインドし、項目を効率よく表示・非表示切り替えする機能を持ちます。

ListViewの利点は、多くの項目を表示してもパフォーマンスが良好な点です。ScrollViewでは1000個の要素を追加すれば1000個すべてのVisualElementが生成・配置されますが、ListViewでは画面に見える範囲の要素数+αしかUI要素を生成しません。スクロールに応じて要素を再利用(Recycle)する仕組みがあるため、メモリとCPU負荷を大幅に抑えられます。

ListView自体はScrollViewの子要素としてスクロール可能エリアを持っているため、縦方向のスクロールUIとして扱われます。ListViewを使う場合、開発者は「データソース」と「表示テンプレート」を用意するだけで、あとのスクロール処理や項目管理はListView側に任せることができます。

ListViewでデータ一覧を表示する仕組み:itemsSourceやmakeItem・bindItemの基本概念

ListViewはデータソースとUIをつなぐデータバインディングの仕組みを持っています。主に以下のプロパティやコールバックで構成されます。

  • itemsSource: データの配列やリスト(Listなど)をセットします。ListViewはこのコレクションの要素数をもとに項目数を認識します。
  • makeItem: 新しい表示要素(VisualElement)を生成するための関数を指定します。ListViewが新規に項目のUIを必要とした際、この関数が呼ばれてテンプレートとなるVisualElementが返されます。
  • bindItem: 生成済みのVisualElementにデータをバインドして表示を更新するための関数です。引数で与えられるインデックスに対応するデータを取り出し、VisualElement(例えば中のLabelテキストなど)に反映します。

ListViewは、表示領域内に入るインデックスのデータに対してのみmakeItem->bindItemを行い、要素を配置します。スクロールすると、見えなくなった要素を別のデータに使い回すためにbindItemを呼び出して内容を更新します。こうすることで、例えば1000件中10件しか表示領域に入らなければ、UI要素も10個程度しか生成しません。

この仕組みにより、大きなリストでもスムーズなスクロールと低負荷を実現しています。ListViewを使用する際は、このitemsSourceとmakeItem/bindItemを正しく実装することが重要です。

ScrollViewで実装する場合との比較:ListViewが持つ仮想化などの利点と効率性を解説します

ListViewが無い場合、開発者がScrollView上で上記の仮想化を自前で行う必要があります。つまり、「見えていない要素は作らない/破棄する」「スクロール位置に応じて要素を更新する」といった処理を実装しなければなりません。これはかなり高度な実装になります。一方、ListViewを使えばそれらは内部処理に隠蔽され、開発者はデータと見た目の定義に専念できます。

利点のまとめ:

  • ListViewは大量データでもメモリ・CPU効率が良い(仮想化による)。
  • バインドロジックが用意されているため、データ変更時にRefreshItemsを呼ぶだけでUIを最新状態に更新できる。
  • 選択状態や複数選択、アイテムのリオーダー(ドラッグ&ドロップで順序変更)など、高度な機能もオプションでサポート。

ScrollView単体の実装では、例えば1000件の項目を追加すればその分生成コストがかかり、スクロールも重くなる可能性があります。ListViewでは上記の通り仮想化しているので1000件でも軽快に扱えます。表示すべきデータ量が明らかに多い場合、最初からListViewを選択することがベストプラクティスです。

ListViewの利用方法:UI Builderでの設定とC#コードによる初期化手順を紹介します!

UI Builder上でもListViewは配置可能で、ScrollViewと同様に大きさや位置を指定できます。しかし、ListViewの肝となるitemsSourceやmakeItem関数はコードでセットする必要があるため、完全な設定はC#側で行うケースが多いです。

ListViewを使う手順の一例:

  1. UI BuilderでListViewを画面に配置し、名前やサイズを設定しておく(例えばListView要素をドラッグ&ドロップ)。
  2. C#スクリプトでListViewのインスタンスを取得(root.Q<ListView>(\"myListView\")など)。
  3. ListView.itemsSourceにデータリスト(例えばListなど)を設定。
  4. ListView.makeItemに、新しいVisualElement(例えばLabel)を生成して返すラムダ関数を割り当て。
  5. ListView.bindItemに、与えられたVisualElementにitemsSourceの対応データをセットするラムダ関数を割り当て。
  6. 必要に応じてfixedItemHeight(各項目の高さ)やselectionType(選択可否)などを設定。

例えば、文字列リストを表示するListViewのコード概要:

var listView = root.Q<ListView>(\"myListView\"); var fruits = new List<string> { \"Apple\", \"Banana\", \"Cherry\" }; listView.itemsSource = fruits; listView.makeItem = () => new Label(); listView.bindItem = (element, index) => { (element as Label).text = fruits[index]; }; listView.fixedItemHeight = 16; // 各項目の高さ(px)を指定 

このように設定すると、ListViewはfruitsリストの内容を元に項目を生成・表示します。itemsSourceにリストを変更したらlistView.Rebuild()またはRefreshItems()を呼ぶことで更新できます。UI Builderで配置してコードで初期化する流れを取ることで、見た目のレイアウトとデータバインド処理を分離して実装できる点もListView活用のポイントです。

ScrollViewとListViewの適材適所:大量データ表示にはListViewを選ぶべき理由を解説

ここまでの説明から明らかなように、大量のデータを表示する場合は迷わずListViewを使うべきです。ListViewはScrollViewを内包しつつも、それ単体でスクロール可能なリストUIを完結させています。ScrollViewはあくまで汎用的なスクロールコンテナなので、自前実装しない限り仮想化などの高度な機能は提供されません。

一方、項目数が少ない場合や、リストと言えども各項目のレイアウトが大きく異なるような自由度が必要な場合には、ScrollView+手動配置のほうが簡潔に済むこともあります。ListViewは「各項目が同じテンプレートで繰り返し表示される」ケースに最適化されているため、内容のバラつきが大きいUIには向きません。

適材適所のまとめとしては、

  • 数十〜数百以上の同種データを一覧表示する→ListViewを使う(パフォーマンス、機能面で有利)。
  • 数が少なくレイアウトもまちまちなUIや、特殊なスクロール挙動が必要な場合→ScrollViewで柔軟に組む。

となります。UI Toolkitではどちらのアプローチも統一的なスタイルシートやイベントシステムの下で動くため、組み合わせることも可能です。例えば、ListView内の項目テンプレートにScrollViewを入れる、といったこともできます(ただし複雑になるので通常避けますが)。要求に応じて最適なコンポーネントを選択しましょう。

ScrollViewのScrollbarを強制的に再描画する方法

ScrollViewを使っていると、コンテンツを動的に追加・削除した際にスクロールバーの表示範囲が更新されない問題に遭遇することがあります。例えば、ScrollViewに大量の要素をClear()で削除した直後、本来スクロールバーは消えるはずなのに残ったままになる、といった現象です。これはUI Toolkitのレイアウト再計算タイミングやバグによるもので、明示的にスクロールバーの再描画(レイアウト再計算)をトリガーしてやる必要があります。この章では、その問題の概要と解決策としてのGeometryChangedEventを使った強制再描画手法、さらに内部仕組みに関する補足を解説します。

ScrollViewのスクロールバーが更新されない問題:コンテンツ動的変更時に起こる不具合の概要を解説

ScrollViewにおいて、子要素を大幅に追加・削除した後などに、スクロールバーの長さや表示有無が正しく更新されない不具合が報告されています。具体的には、例えばScrollViewいっぱいに要素が入ってスクロールバーが表示されている状態から、Clear()で全要素を削除したケースを考えます。本来コンテンツが空になればスクロールバーは非表示になるべきですが、UIによってはスクロールバーの表示が残留したままになったり、逆に表示範囲が不正なサイズのままになったりします。

この問題は、UI Toolkitのレイアウトエンジンが特定の状況下でスクロール範囲の再計算を怠る(または遅延させる)ことに起因します。特に、エディタ拡張や複雑なUIを構築している際に見られるケースですが、ランタイムUIでも条件が重なれば発生し得ます。表面的には「コンテンツを消したのにスクロールバーだけ残って動かない」といったユーザーから見て明らかな不具合として現れるため、開発者側で対処する必要があります。

スクロールバー再描画が必要となるケース:コンテンツ変更後にスクロール範囲が更新されない問題を解説します

上記のようなケースでは、ScrollView内部のコンテンツサイズが急激に変化したにも関わらず、スクロール範囲(scrollView.verticalScroller.highValueなどで表される最大スクロール位置)が更新されず、UI表示が古い状態のままになります。ユーザーから見ると、実際にはもうスクロールできるコンテンツは無いのにスクロールバーだけが操作可能だったり、逆にコンテンツが増えたのにスクロールバーのつまみが大きいままで全部見切れてしまう、といった不具合になります。

このような問題が起きやすいのは、動的なコンテンツ変化が発生した直後にUIレイアウトのフレーム更新が行われない場合や、UI Toolkitのバグで内部状態が不整合になった場合です。特にEditor拡張でIMGUIとUI Toolkitを組み合わせている場合や、複数のScrollViewをネストしている場合などに起こることが報告されています。

いずれにせよ、このような状況では開発者側で明示的に「レイアウトを更新せよ」と指示を出す必要があります。それが次に述べるGeometryChangedEventを用いた再描画トリガー手法です。

解決策:GeometryChangedEventでScrollViewのスクロールバーを更新する方法

スクロールバーの更新不良を解消するには、ScrollViewに対してGeometryChangedEvent(ジオメトリ変更イベント)を発生させる方法が有効です。GeometryChangedEventはUI要素のレイアウト(位置・サイズ)が変化した際にUnityが送出するイベントですが、これを人工的に発生させてやることでレイアウト再計算を強制的に走らせることができます。

具体的な手順は、ScrollViewのcontentContainerに対し、現在のレイアウトRectを使ったGeometryChangedEventを注入するというものです。コード例を示します。

private static void ForceUpdateScrollView(ScrollView scrollView) { // 現在のレイアウト矩形を取得 Rect currentRect = scrollView.layout; // ダミーの古い矩形を用意(ここではゼロ矩形) Rect zeroRect = Rect.zero;
// GeometryChangedEventを生成してcontentContainerに送信
var geomEvent = GeometryChangedEvent.GetPooled(zeroRect, currentRect);
geomEvent.target = scrollView.contentContainer;
scrollView.contentContainer.SendEvent(geomEvent);
}

上記のForceUpdateScrollViewメソッドを呼び出すと、ScrollViewのContentContainerに対して「レイアウトが変わったよ」というイベントが送られます。これによりUI Toolkitはスクロール領域などを再計算し、結果的にスクロールバーの表示・非表示やつまみ長さが正しい状態に更新されます。

この手法はUnity公式フォーラムやコミュニティでも紹介されており、現在(2024年時点)UI Toolkitにおける既知の問題への対症療法として知られています。将来的なUnityアップデートで根本解決される可能性もありますが、現状では必要に応じてこのような強制更新を行うことが実践的な解決策となります。

強制再描画処理の実装例:ScrollView.contentContainerにGeometryChangedEventを送信するC#コード

前述のForceUpdateScrollViewメソッドを実際に使用する場面を考えてみましょう。例えば、ScrollViewに対して大量の要素をClear()した直後に再計算を行いたい場合、次のようにします。

// ScrollView内の要素をすべてクリア myScrollView.Clear(); // 強制更新メソッドを呼ぶ ForceUpdateScrollView(myScrollView);

これで、Clearによって生じたスクロール領域の変化をGeometryChangedEventで補正し、ユーザーに正しいUIが即座に提示されます。別の例として、ScrollViewのサイズ自体をコードで変更した際(例えば表示エリアの高さを変えた)にスクロールバー表示が乱れる場合にも、このメソッドを呼ぶことで整合性を保てます。

なお、上記コードではGeometryChangedEvent.GetPooledを使っていますが、Unityのイベントはプールされて再利用される仕組みがあります。GetPooledはそうしたイベント取得用のユーティリティです。また、event.targetを明示的にcontentContainerに設定している点も重要です。contentContainerがレイアウト変化の主体であるため、これをターゲットにイベントを投げることでScrollViewは「中身のサイズが変わった」と認識します。

MarkDirtyRepaintが効かない理由:UI Toolkitにおけるレイアウト再計算の仕組み

ScrollViewの再描画問題に直面した際、初めに思いつく対処としてMarkDirtyRepaint()を呼ぶ方法があります。VisualElement.MarkDirtyRepaint()は、その要素の描画更新を要求するものですが、残念ながらレイアウトの再計算までは保証しません。スクロールバーの挙動にはレイアウト(Geometry)の再計算が必要であり、単なるRepaint要求では不十分なのです。

UI Toolkitでは、レイアウトと描画の更新が分離されています。VisualElementのサイズや位置が変化した場合にはGeometryChangedEventが発行されレイアウトパスが走りますが、サイズが変わらない単なるRepaintではスクロール範囲などの計算は行われません。そのため、MarkDirtyRepaintだけではスクロールバーの問題は解決しなかったわけです。

今回紹介したGeometryChangedEvent送信のテクニックは、UI Toolkitの内部挙動を逆手に取った方法と言えます。将来的にUnityがこの不具合を修正すれば不要になるかもしれませんが、現時点では開発者が積極的に介入できる数少ない手段です。UI Toolkitは進化途中のフレームワークであり、バージョンアップ毎にこうした問題は減っていくと期待されます。それまでは、本節で解説した仕組みを理解しつつ対応していきましょう。

uGUIのScrollViewとの違いとUI Toolkit特有の注意点(マウスドラッグ不可など)

UI ToolkitのScrollViewは、従来のuGUI(Unity UI)のScroll Rect+Scrollbarと似た役割を持ちますが、その設計や挙動にはいくつか違いがあります。ここでは、uGUIのScrollView(ScrollRect)との主要な相違点と、UI Toolkit版ScrollViewを使う際に知っておくべき注意点についてまとめます。特に、マウスによるドラッグスクロールができない点や、コンポーネント構造の違い、レイアウトの扱いなどに着目します。

UI Toolkit ScrollView最大の違い:コンテンツをマウスドラッグでスクロールできない点に注意

まず押さえておきたい大きな違いは、UI ToolkitのScrollViewではコンテンツ部分を直接マウスドラッグしてスクロールできないという点です。uGUIのScrollRectでは、コンテンツ領域をつかんでドラッグすればその方向にスクロールしました。しかし、UI Toolkitではデフォルトの挙動としてそれが無効になっています。ユーザーはスクロールバーのつまみをドラッグするか、マウスホイール/タッチパッド、またはキーボード(矢印キー)でスクロールする必要があります。

これはUI Toolkitの入力システム仕様によるもので、マウスのドラッグイベント(PointerMoveEvent)がScrollViewに対してスクロール操作として処理されないためです。モバイル等のタッチデバイスでは指のドラッグでスクロール可能ですが、PC上ではデフォルトではホイールなどに頼る形になります。この違いにより、PCアプリケーションでUI Toolkit ScrollViewを使う際には、「ドラッグでスクロールできないのは不便だ」と感じるユーザーがいるかもしれない点に注意が必要です。

必要であれば後述するようにPointerイベントをハンドリングして独自にドラッグスクロールを実装することも可能ですが、標準では用意されていないためUI設計時に留意しましょう。

スクロールバー実装の違い:uGUIではScrollRect+Scrollbar、UI Toolkitではスクロールバー内蔵

uGUIにおけるスクロールビュー実装は、ScrollRectコンポーネント + Scrollbarコンポーネントの組み合わせで成り立っていました。ScrollRectがスクロール機能本体で、オプションで垂直/水平のScrollbarコンポーネントをそれぞれ紐付ける形です。それに対し、UI ToolkitのScrollViewは1つの要素の中にスクロールバーを内包しています。垂直スクロールバー・水平スクロールバーともにScrollView内部の子要素(ScrollerというUI要素)として実装されており、別途アタッチする必要はありません。

この違いにより、uGUIではスクロールバーのスタイルを変更する際に別のGameObject上のScrollbarコンポーネントの設定を弄る必要がありましたが、UI ToolkitではScrollViewのスタイルシートを通じてスクロールバーの見た目を変えることになります。例えば、スクロールバーの幅や色を変えるには、.unity-scroll-view__scrollbar.unity-scroll-view__sliderといったUSSクラスに対してスタイルを指定します(UI Builder上ではまだ直接いじりづらい部分でもあります)。

また、ScrollView内蔵のスクロールバーは自動的にそのScrollViewに連動するため、uGUIのように手で結び付ける手間が無い反面、必要ない場合に完全に取り除くことは難しくなっています。スクロールバー自体を消したいときはVisibility設定をHiddenにするか、USSでdisplay: noneを適用する形になります。

レイアウトとサイズ設定の違い:UI Toolkitでは自動レイアウト(ContentSizeFitter不要)

uGUIでは、スクロールビュー内のコンテンツサイズに応じてContentオブジェクトのサイズを調整するために、Content Size FitterLayout Groupを使う必要がありました。例えば垂直レイアウトグループ+コンテンツサイズフィッターを組み合わせて、子要素の合計高さに従ってContentの高さを伸縮させる、といった設定が必要でした。

UI ToolkitのScrollViewでは、こうした作業は不要です。ContentContainerは子要素を入れればその分自動で大きくなり(折り返ししない限り無限大に近い概念的サイズを持つ)、ScrollViewはそれとの差分でスクロール範囲を決定します。つまり、UI Toolkitではレイアウトエンジンがコンテンツサイズ計算を自動で行うため、開発者はレイアウトグループやコンテンツフィッターを意識する必要がありません。

これは非常に便利ですが、裏を返せば「常にContentContainerがサイズ更新されるのを待つ必要がある」ということでもあります。前章で触れたスクロールバー更新の問題などは、この自動レイアウトに絡んで発生する部分もあります。とはいえ、通常のケースでは特別なコンポーネントを追加しなくても自然に期待通り動くため、UI設計の手間は軽減されています。

スクロール挙動の違い:慣性スクロールやバウンス(Elasticity)の有無と設定を比較します!

uGUIのScrollRectでは、インスペクタ上に「Inertia(慣性)」「Elasticity(弾性)」「Deceleration Rate(減速率)」といったオプションがあり、タッチやドラッグ時の挙動を細かく設定できました。UI ToolkitのScrollViewでも同様の概念はありますが、その設定の露出方法が異なります。

UI Toolkit ScrollViewの場合、scrollDecelerationRateelasticityといったプロパティでコードやUXMLから設定します。現時点ではUI BuilderのInspectorでこれらを直接編集するUIはない(あるいは限定的)ため、必要ならUSS変数やC#で指定します。uGUIではチェックボックスやスライダーで直感的に設定できた部分が、UI Toolkitでは若干開発者寄りの手段に委ねられているわけです。

また、前述のようにマウスドラッグでの慣性スクロールはUI Toolkitでは標準サポートしません。一方、uGUI ScrollRectではInertia有効時、マウスでもドラッグ後にスッと離せば慣性が働いていました。UI Toolkitで同様の体験を与えるには、マウスドラッグをエミュレートする実装+scrollDecelerationRate設定を組み合わせる必要があります。

スクロールの滑らかさ(補間)については、UI Toolkitは基本的にフレーム毎にスクロール位置がピクセル単位で更新されるシンプルな動きです。uGUIではUpdate関数内で物理計算的に減速する実装になっていました。見た目上の差異は小さいですが、微妙なスクロール感の違いとして現れる可能性があります。こうした挙動の違いは、必要に応じてUI Toolkit側のパラメータを調整することでかなり埋められます。

その他の注意点:UI Toolkit ScrollView特有の制限事項とuGUIとの開発上の違いについて

他にも細かな違いや注意点がいくつかあります。

  • 入力イベントの扱い: UI Toolkitではイベントのバブリング(伝播)があるため、ScrollView内の要素でスクロール関連のイベントが扱われると親ScrollViewに伝わらない場合があります。uGUIではイベントシステムでScrollRectとScrollbarが直接連携していましたが、UI Toolkitではイベント伝播の流れを把握して適切にStopPropagation()などを使う必要が出ることがあります。
  • プラットフォームサポート: UI ToolkitはもともとEditor拡張用に生まれた経緯があり、Unityの特定バージョンまではランタイムUIとしては実験的扱いでした。2023年現在では正式にランタイムサポートされていますが、Unityのバージョンアップによって挙動が変わったりバグ修正が行われたりしています。uGUIは長年使われて安定していますが、UI Toolkitは最新Unityを使うことでより安定する可能性が高いです。
  • スタイルカスタマイズ: uGUIではScrollRectやScrollbarの細かい部分を変更するのにUnityEngine.UI上のスクリプトやPrefabをいじる必要がありましたが、UI ToolkitではUSSによるスタイリングが中心です。そのため、デザイナー寄りの変更(色やサイズ)は容易ですが、独自挙動を追加する場合はC#でカスタムVisualElementを作成するなどのアプローチになります。

総じて、UI ToolkitのScrollViewは最新のUIフレームワークらしく洗練されていますが、uGUIに慣れた開発者には挙動の違いに戸惑う部分もあるでしょう。本節で挙げた点を踏まえて設計・実装すれば、大きな問題なく移行・活用できるはずです。

UI Toolkitのバグ・制限に対処するための実践的なテクニック集!

最後に、UI ToolkitのScrollViewを使用する上で直面しがちな制約やバグへの対処法、そして知っておくと便利な実践的テクニックをいくつか紹介します。UI Toolkitは日々改善されていますが、それでもまだ開発者が工夫を凝らす場面があります。ここではマウスドラッグでのスクロール実装方法、スクロール位置の保存・復元、ネストしたScrollViewの扱い、プログラムによるスクロール制御、そして最新情報の追跡などについて触れます。

マウスドラッグでScrollViewをスクロールさせるテクニック:Pointerイベントでコンテンツを掴んで移動

前述の通り、UI ToolkitのScrollViewは標準ではマウスドラッグによるスクロールができません。しかし、Pointerイベントを自前で処理することで、ドラッグスクロールを擬似的に実装できます。方法はいくつか考えられますが、代表的なアプローチは次のようなものです。

  1. ScrollViewのContentContainerに対してPointerDownEvent(マウス押下)のリスナーを登録し、押下開始位置を記録します。
  2. PointerMoveEvent(マウス移動)のリスナーで、押下中であれば現在位置と開始位置の差分を計算し、scrollView.scrollOffsetをその差分だけ変更します。これによりコンテンツをドラッグして動かしているようにスクロール位置が変化します。
  3. PointerUpEventでドラッグ操作を終了し、必要なら慣性を計算して少し余韻でスクロールさせる(物理計算)処理を行います。

このような低レベル実装を行うと、uGUIのScrollRectと同等のドラッグ体験を提供できます。ただし、そこまで実装するのは手間がかかるため、PC対象のUIではマウスホイール中心のUXに割り切ることも多いです。重要なのは「UI Toolkit ScrollViewはドラッグ非対応だ」ということを念頭に置いてUI設計することです。必要なら、ここで述べたようなPointerイベント制御で補完することも可能であると覚えておきましょう。

ScrollViewのスクロール位置をプログラムから調整:ScrollToメソッドとscrollOffset設定の活用

ScrollViewで、「一番下まで自動でスクロール」や「特定の要素までジャンプする」といった操作をしたい場合があります。そのために用意されているのがScrollView.ScrollTo(VisualElement)メソッドです。引数に子要素を渡すと、その要素がViewPort内に収まるよう自動スクロールします。

例えばチャットアプリで新しいメッセージを追加した際、ScrollViewを自動的に最新メッセージまでスクロールさせたい場合、scrollView.ScrollTo(newMessageLabel)と呼ぶだけで、newMessageLabelが見える位置までスクロールしてくれます。

また、ScrollToメソッドを使わず、直接scrollView.scrollOffsetプロパティを設定する方法もあります。scrollOffsetVector2で、(x,y)それぞれScrollViewの現在のスクロール量を表します。たとえば一番下にスクロールしたければ、scrollView.scrollOffset = new Vector2(0, scrollView.verticalScroller.highValue)と設定することも可能です。このhighValueは縦スクロールバーの最大値(つまりコンテンツ全体の高さ - Viewport高さ)を表すので、これにより最下部へ移動できます。

プログラムによるスクロール制御は、ユーザー操作以外でUIを動かしたいときに重宝します。ただし、コンテンツの動的変更直後などでは前述の通りスクロール範囲が古い可能性があるため、適切なタイミングで行う必要があります。通常はコンテンツ追加直後にScrollToを呼べば問題ありませんが、もし効かない場合はschedule.Executeで1フレーム遅らせるなど工夫してください。

スクロール位置の保存と復元:view-data-keyを用いてScrollViewの状態を保持する方法

UI Toolkitには、エディタ拡張などで使われるView Dataという仕組みがあります。view-data-keyを設定したVisualElementは、そのキーに紐づく状態(たとえばツリービューの展開状況や、ScrollViewのスクロール位置)をUnityエディタが自動的に保存・復元します。ランタイムでは基本的にこの機能は使われませんが、エディタ上で複数回ウィンドウを開き直してもスクロール位置を維持したい場合などに役立ちます。

ScrollViewにview-data-keyを設定すると、そのScrollViewのscrollOffset(スクロール位置)がキーに基づいて保存されます。例えば、エディタ拡張のカスタムウィンドウでScrollViewを使っており、ウィンドウを閉じて再度開いたときに前回のスクロール位置を再現したい場合、ScrollViewのUXMLもしくはコードでscrollView.viewDataKey = \"MyUniqueKey\";と設定しておくと、Unityエディタが自動的にそのキー名でEditorPrefsに状態を保存してくれます。

ランタイムアプリケーションでユーザーのスクロール位置を保存したい場合は、自前でscrollOffsetを記録しておき、復元時に再度設定する必要があります。例えばゲーム内ブラウザ的なUIで、戻ったときに前回見ていた位置から表示したい場合などです。その場合でも単にVector2 offset = scrollView.scrollOffset;を保存し、復帰時にscrollView.scrollOffset = offset;とすればOKです。

このように、ScrollViewの状態保持は比較的シンプルに行えます。UI Toolkitでは開発者が必要な情報にアクセスできる設計になっているため、こうしたカスタム実装も難しくありません。

ネストしたScrollViewの挙動制御:NestedInteractionKindで親子スクロールの干渉を防ぐ

ScrollViewの中にさらにScrollViewが入っている、いわゆるネストしたスクロールビューのケースでは、スクロール操作の衝突に注意が必要です。例えば、内側のScrollViewを下方向にスクロールしきった状態でさらに下にスクロールしようとすると、親のScrollViewがスクロールを開始してほしい場合があります。逆に、内側がスクロール可能なうちは親をスクロールさせたくないこともあります。

UI ToolkitのScrollViewには、この状況を制御するためのnestedInteractionKindプロパティがあります。これはNestedInteractionKind列挙型で、典型的には以下の設定値があります。

  • StopPropagation: 内側のScrollViewが端に達したら親にはスクロールイベントを伝えない(内側で止める)。
  • AllowPropagation: 内側が端に達したらスクロールイベントを親に伝播させ、親ScrollViewをスクロールさせる。

デフォルトでは StopPropagation(だったはず)で、内側ScrollViewのスクロールが限界に達しても親には影響を与えません。しかし場合によっては、内側をスクロールし終わったら親全体のスクロールに移行してほしいUIもあります。その際にはscrollView.nestedInteractionKind = ScrollView.NestedInteractionKind.AllowPropagation;と設定することで、スクロールイベントが親に引き継がれるようになります。

この設定はUI/UX上微妙な調整ですが、知っておくと特殊なUIで役に立ちます。例えば、スクロール領域が入れ子になっているチャットウィンドウや、左右スクロール内に縦スクロールリストがある場合などで、意図通りのスクロール挙動を実現するのに有用です。

UI Toolkit ScrollViewの既知の不具合とアップデート情報:最新Unityへのアップグレード検討

最後に、開発中に遭遇するかもしれないUI Toolkit ScrollView関連の既知の不具合や制限について触れておきます。例えば前述したスクロールバー更新の問題などはその一例です。他にも、UnityのバージョンによってはScrollViewに関するバグフィックスや仕様変更が行われています。

Unity公式のリリースノートやUI Toolkitのアップデート情報を定期的に確認し、問題が報告されている場合はワークアラウンドを探す、あるいは新しいバージョンで改善されていないか検証することが重要です。実際、2022~2023年にかけてUI Toolkitは大幅な改良が加えられており、スクロールビュー周辺の安定性も向上してきています。

また、UnityのフォーラムやIssue Trackerで「UI Toolkit ScrollView」のキーワードで検索すると、開発者から報告された不具合やその解決策が見つかることがあります。必要に応じてそうした情報源も活用しましょう。UI Toolkitは比較的新しい技術のため、最新Unityへアップグレードすることが問題解決に直結するケースも少なくありません。

総じて、UI ToolkitのScrollViewを扱う際は、本記事で解説した基本知識やテクニックに加え、コミュニティで共有されている知見も取り入れていくと良いでしょう。常に最新の情報をキャッチアップしつつ、安定したスクロールUIを実現してください。

資料請求

RELATED POSTS 関連記事