こんにちは、あんこ先生です。
前回は、サンクスアプリの設計を決めて、SharePointリストを3つ用意しました。
今回はいよいよ画面づくりです。
投稿がずらっと並ぶタイムライン画面を作っていきましょう。
SNSでいちばん見られる画面ですから、ここが気持ちよく動くと一気に「それっぽく」なりますよ。
まだ読んでいない人は、先に設計とリスト準備の回でリストを用意しておいてくださいね。
画面の骨組みは3つだけ

最初に、この画面の構造を確認しておきましょう。
置くものは3つです。
上に画面タイトル、まん中に投稿一覧のギャラリー、下にボトムメニュー。
たったこれだけです。
まずは新しい画面を追加して、名前を scrTimeline にしておきます。
画面の Fill は、うっすらグレーにしておくと投稿カードが引き立ちますよ。
//画面 scrTimeline の Fill(投稿カードを引き立てる薄いグレー) RGBA(245, 246, 247, 1)
いちばんの山場は「行の高さ」

さて、ここからが今回の本題です。
タイムラインに並ぶ投稿は、本文の長さがバラバラですよね。
ひとことだけの「ありがとう!」もあれば、5行くらいの熱いメッセージもあります。
ここで、ギャラリーの高さを決め打ちしてしまうと、こうなります。
短い投稿は下がスカスカ、長い投稿は文字がはみ出して次の投稿と重なってしまいます。
わたしも最初これで「なんか汚いな…」となりました。
解決策は3つの合わせ技です。
① ギャラリーは「縦(可変の高さ)」を選ぶ
まず、ギャラリーを挿入するときに、レイアウトで「縦(可変の高さ)」を選びます。
名前は galTimeline にしておきましょう。
ふつうの「縦」ギャラリーは、TemplateSize で決めた高さで全部の行がそろいます。
いっぽう「可変の高さ」は、行の中身に合わせて高さが決まります。
並べるデータは、Postsを新しい順にします。
//ギャラリー galTimeline の Items(投稿を新しい順に並べる) SortByColumns(Posts, "Created", SortOrder.Descending)
Created は、SharePointが自動で記録している登録日時のことですね。
ギャラリーの高さと位置も決めておきます。
| プロパティ | 設定する式 |
|---|---|
| Y | 70 |
| Height | Parent.Height – 70 – 64 |
| Width | Parent.Width |
| TemplatePadding | 0 |
高さの式は、画面の高さから上のタイトル分(70)と、下のメニュー分(64)を引いています。
こうしておけば、ギャラリーがメニューの下に潜り込みません。
ギャラリーそのものの基本をおさらいしたい人は、すぐできるギャラリーコントロールの使い方と活用事例もどうぞ。
② ラベルは AutoHeight をオンにする
次に、中に置くラベルを「文字の量で伸びる」状態にします。
AutoHeight を true にするだけです。
これを本文のラベルに付けると、3行の投稿なら3行分、6行の投稿なら6行分の高さになります。
③ Y座標を「ひとつ上の下端」から積む

ここがコツです。
ラベルの高さが変わるのですから、その下に置く部品のY座標も、固定の数字にしてはいけません。
//本文ラベル lblBody の Y(ひとつ上の下端から積むので、上が伸びても重ならない) lblAuthorToUser.Y + lblAuthorToUser.Height + 6
「ひとつ上の部品のY座標 + その高さ + すき間」で位置を決めます。
こうすると、上の部品が伸びたぶんだけ、下の部品も一緒に下がってくれます。
投稿の中身を並べる

では、ギャラリーの中に置くものを順番に見ていきましょう。
左にアバター、右に投稿者名・本文・タグ・画像、という並びにします。
アバター(投稿者の顔写真)
まずは左側のアバターから。
モダンコントロールのアバターを追加して、名前を avaAuthor にします。
| プロパティ | 設定する式 |
|---|---|
| Image | ThisItem.登録者.Picture |
| Shape | ‘Avatar.Shape’.Circular |
| Width | 50 |
| X | 16 |
| Y | 12 |
投稿者の写真は、SharePointが自動で記録している登録者の列から取れます。
ここで、ちょっとした落とし穴があります。
アバターコントロールそのものの使い方は、モダンコントロール アバター(Avatar)の使い方にまとめてありますので、あわせてどうぞ。
投稿者 → 受取者と、投稿時間
次に、誰から誰へのありがとうかを表示します。
テキストラベルを追加して、名前は lblAuthorToUser に。
ここで、投稿時間も一緒に出してしまいましょう。
SNSらしく「3分前」「2時間前」という表示にしたいですよね。
Text プロパティに、こちらを入れます。
//ラベル lblAuthorToUser の Text(誰から誰へ+投稿からの経過時間を1行で出す)
//経過分数を With でまとめ、範囲ごとに表示を切り替える
ThisItem.Author.DisplayName & " → " & ThisItem.ToUser.DisplayName
& " ・ "
& With(
{ mins: DateDiff(ThisItem.Created, Now(), TimeUnit.Minutes) },
Switch(
true,
mins < 1, "たった今",
mins < 60, Text(mins) & "分前",
mins < 1440, Text(RoundDown(mins / 60, 0)) & "時間前",
mins < 10080, Text(RoundDown(mins / 1440, 0)) & "日前",
Text(ThisItem.Created, "yyyy/mm/dd")
)
)やっていることは単純です。
DateDiff で「投稿から今まで何分たったか」を出して、その分数で表示を切り替えているだけ。
60分未満なら「◯分前」、1日未満(1440分)なら「◯時間前」、1週間未満(10080分)なら「◯日前」。
それより古いものは、素直に日付を出します。
計算に使った分数は With でまとめてあります。
同じ計算を何度も書かずに済むので、こういう分岐では重宝します。
このラベルの位置とサイズは、こうしておきます。
| プロパティ | 設定する式 |
|---|---|
| AutoHeight | true |
| FontWeight | FontWeight.Semibold |
| Size | 14 |
| Width | Parent.TemplateWidth – 102 |
| X | 86 |
| Y | 12 |
Width は、左のアバター(幅50 + 余白)を避けた残りの幅にしています。
本文
本文のラベル lblBody は、シンプルです。
| プロパティ | 設定する式 |
|---|---|
| Text | ThisItem.Body |
| AutoHeight | true |
| Size | 14 |
| Width | Parent.TemplateWidth – 102 |
| X | 86 |
| Y | lblAuthorToUser.Y + lblAuthorToUser.Height + 6 |
さっそく Y が「ひとつ上の下端」から積まれていますね。
ここから下の部品は、すべてこの形でつないでいきます。
タグ
タグのラベル lblTags は、複数選択の列なので少し工夫します。
//タグラベル lblTags の Text(複数選択のタグをつなげて1つの文字列にする) Concat(ThisItem.Tags, Value & " ")
Concat で、選ばれたタグをつなげて1つの文字列にしています。
| プロパティ | 設定する式 |
|---|---|
| AutoHeight | true |
| Color | RGBA(0, 120, 212, 1) |
| Size | 12 |
| Width | Parent.TemplateWidth – 102 |
| X | 86 |
| Y | lblBody.Y + lblBody.Height + 6 |
タグだけ青くしておくと、SNSらしさが出ますよ。
画像
画像は、投稿によって「ある・ない」が分かれますよね。
画像コントロール imgPost を追加して、こう設定します。
| プロパティ | 設定する式 |
|---|---|
| Image | ThisItem.Image |
| Visible | !IsBlank(ThisItem.Image) |
| Width | If(IsBlank(ThisItem.Image), 0, 300) |
| Height | Self.Width |
| X | 86 |
| Y | lblTags.Y + lblTags.Height + 6 |
画像が無いときは、見えなくするだけでなく幅と高さを 0 にするのがポイントです。
さきほどの「ドミノ倒し」を思い出してください。
高さが 0 になれば、その下の部品がぴったり詰まってくれます。
Visible を false にしただけだと、場所は空いたままで、無駄なすき間が残ってしまうんですね。
ちなみに Height に Self.Width を入れると、正方形で表示できます。
投稿の区切り線
最後に、投稿と投稿のあいだに線を引きます。
四角形(長方形)を追加して、名前を recDivider に。
| プロパティ | 設定する式 |
|---|---|
| Fill | RGBA(225, 227, 229, 1) |
| Height | 1 |
| Width | Parent.TemplateWidth |
| X | 0 |
| Y | imgPost.Y + imgPost.Height + 16 |
高さ1の四角形を置くだけで、細い罫線になります。
この線がいちばん下の部品なので、この線の位置が行の高さを決めていることになりますね。
ボトムメニューをギャラリーで作る
さて、画面の下に固定するメニューを作りましょう。
タイムライン・投稿・マイページ・ランキングの4つを行き来できるようにします。
ここで、素直に作ろうとすると、こうなりがちです。
アイコンを4つ置いて、ボタンを4つ置いて、それぞれに座標と遷移先を書いて…。
正直、しんどいです。
そこで、メニューもギャラリーで作ってしまいます。
メニューの中身をテーブルで書く
横方向のギャラリーを追加して、名前を galBottomMenu にします。
Items には、メニュー項目そのものを書いてしまいます。
//ボトムメニュー galBottomMenu の Items(メニュー項目そのものをデータとして書く)
//Key は画面を見分ける文字列、icon は表示するアイコン
Table(
{ Key: "Timeline", icon: Icon.Home },
{ Key: "Post", icon: Icon.Add },
{ Key: "MyPage", icon: Icon.Person },
{ Key: "Ranking", icon: Icon.TrendingUpwards }
)Table 関数で、その場でデータを作っています。
Key は画面を見分けるための文字列、icon は表示するアイコンです。
面白いのは、アイコンそのものをデータとして持たせられるところ。
ギャラリー側は、画面下に貼り付くように設定します。
| プロパティ | 設定する式 |
|---|---|
| Fill | RGBA(255, 255, 255, 1) |
| Height | 64 |
| Width | Parent.Width |
| Y | Parent.Height – 64 |
| TemplateSize | Parent.Width / 4 |
| TemplatePadding | 0 |
TemplateSize を画面幅の4分の1にしているので、4つがちょうど並びます。
ボタンは1つ置くだけ
あとは、ギャラリーの中にモダンボタンを1つ置くだけです。
名前は btnMenu にします。
| プロパティ | 設定する式 |
|---|---|
| Icon | ThisItem.icon |
| Text | “” |
| Layout | ButtonLayout.IconOnly |
| Height | Parent.TemplateHeight |
| Width | Parent.TemplateWidth |
Layout を IconOnly にすると、文字なしのアイコンだけのボタンになります。
遷移は OnSelect に書きます。
//メニューボタン btnMenu の OnSelect(押された項目の Key を見て行き先を振り分ける)
Switch(
ThisItem.Key,
"Timeline", Navigate(scrTimeline, ScreenTransition.None),
"Post", Navigate(scrPost, ScreenTransition.None),
"MyPage", Navigate(scrMyPage, ScreenTransition.None),
"Ranking", Navigate(scrRanking, ScreenTransition.None)
)押された項目の Key を見て、行き先を振り分けています。
そして今いる画面だけ色を変えたいので、Appearance で分岐します。
//メニューボタン btnMenu の Appearance(今いる画面だけ目立たせる) //ほかの画面に置くときは、この文字列をその画面の Key に変える If(ThisItem.Key = "Timeline", ButtonAppearance.Primary, ButtonAppearance.Transparent)
この画面はタイムラインなので、Key が “Timeline” のときだけ目立つ見た目になります。
ほかの画面に貼るときは、ここの文字列を変えるだけでOKです。
モダンボタンのプロパティをもう少し知りたい人は、モダンコントロール ボタン(Button)の使い方もどうぞ。
つまずきやすいプロパティ名
モダンコントロールは、クラシックと名前が違うプロパティがあります。
わたしが実際にエラーを出したところを、まとめておきますね。
| やりたいこと | 誤り | 正しい書き方 |
|---|---|---|
| 文字を太字にする | Weight | FontWeight |
| 文字の色を変える | FontColor | Color |
| 右寄せにする | Align.End | Align.Right |
| ボタンにアイコンを付ける | Icon.ThumbLike | “ThumbLike”(文字列) |
いちばん引っかかったのは、いちばん下のアイコンです。
モダンボタン のアイコンは、列挙型ではなく文字列で指定します。
クラシックのアイコンコントロールは Icon.Home のように書くので、つい同じ書き方をしてしまうんですね。
ちなみに、さきほどのボトムメニューでは ThisItem.icon と書いていました。
これは Table の中に Icon.Home という形で持たせているので、そのまま渡せています。
まとめ
今回はここまでにしましょう。
・本文の長さがバラバラなら、ギャラリーは「縦(可変の高さ)」、ラベルは AutoHeight にする。
・下に置く部品のY座標は「ひとつ上の下端+すき間」で積む。いちばん下の部品の位置が、そのまま行の高さになる。
・画像のように「ある・ない」が分かれるものは、無いときに幅と高さを 0 にして詰める。
・メニューもギャラリーで作れる。項目を Table で書いて、ボタンは1つだけ置けばいい。
次の一手として、次回はこのタイムラインにいいね機能を付けます。
一度押したら青くなって、もう一度押すと取り消せる、あのトグルですね。
「同じ人が二重にいいねできないようにする」ところが、ちょっとした腕の見せどころです。
それでは、次の記事でお会いしましょう。


















