日記アプリ Lanternly のバージョン1.1では、App Store Connect の新しい2つの場所、つまりプロダクトページのヘッダーと検索結果に使う画像が必要でした。そのための新しいデザインはなく、アプリを撮り直す気にもなれませんでした。その代わり、公開済みの英語とロシア語の iPhone のスクリーンショットがありました。そこから、両方の場所に使える5244 × 2950のファイルを1つ作りました。
Apple のサイズ、フォーマット、ルールは Creative Assets の記事ですでに解説したので、ここでは繰り返しません。この記事で書くのは組み立て方と、アップロードした後に自分のカバー画像で見つけたミスについてです。
なぜスクリーンショットから作るのか#
スクリーンショットは審査を通っていて、アプリにあるものを正確に見せています。そこから画面を持ってくれば、カバー画像が余計な約束をすることはありません。言語ごとに描き直す必要もありません。画面はすでにローカライズされているので、訳すのは見出しとその下の1行だけです。
構成はシンプルです。上部中央に見出しとサブ見出し、その下に3台の電話(中央に「Today」、両脇に Luna とのチャットとカレンダー)、そして中央の電話の上にマスコットの Luna。画面に写っているのは iPhone だけで、これは譲れない点です。
これには経緯があります。同じスクリプトの最初の版は iPhone Duo 用の画像も作っていて、iPad のスクリーンショットを Duo のサイズに切り抜いていました。これはやってはいけないことです。デバイスの枠には、そのデバイスで撮った画像を入れるべきです。そうでなければ、スクリーンショットはユーザーが実際に手にするものとは違うものを見せることになります。私はこれらのセットを App Store Connect から削除しました。Duo のシミュレータで撮影できるようになるまで、Duo の枠は空のままです。
ファイルの組み立て方#
Python のスクリプトが3つのステップを踏みます。
スクリーンショットから電話を切り出す。 App Store 用のアートボードでは、電話は明るい背景の上に暗いフレームで置かれています。スクリプトは暗いピクセルが目立って多くなる最初の行を探します。それがフレームの上端です。その行で左右の端を見つけ、アートボードの下端まで電話を切り出します。角の丸みは幅の12.5 %の半径を持つマスクで覆い、その外側が古いアートボードの切れ端ではなく透明な背景になるようにします。
HTML ページを組み立てる。 背景、柔らかな光のにじみ、影と±6°の傾きを付けた3台の電話、Lora フォントの見出しと Inter の1行。アプリと同じフォントです。Luna はステッカーのソースから持ってきたベクターの SVG です。
headless Chrome で描画する。 ページは2622 × 1475 CSS ピクセルでレイアウトされ、倍率2で撮影されます。
"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" \
--headless=new --hide-scrollbars --allow-file-access-from-files \
--virtual-time-budget=3000 --force-device-scale-factor=2 \
--window-size=2622,1475 --screenshot=header-en.png \
file:///path/to/_header-en.html出力はちょうど5244 × 2950になります。--virtual-time-budget は撮影前にフォントと SVG が読み込まれる時間を与えます。これがないと、画像には代替フォントが使われてしまいます。
大事な細かい点として、PNG にはアルファチャンネルがあってはいけません。Apple は透明部分を受け付けないからです。ページの背景が不透明なら Chrome のスクリーンショットにアルファは含まれませんが、何か後処理をしたときは必ず RGB で保存し直しています。
アップロード後に見つけたミス#
切り抜きについて、Apple は大まかなことしか書いていません。
Be sure your focal point artwork is within the center of your composition to prevent any unwanted clipping.
正確な寸法はありません。16:9のユニバーサルアセットが中央で切り抜かれると仮定すると、こうなります。
| 場所 | 比率 | 5244 × 2950のうち残る部分 | 切り落とされる部分 |
|---|---|---|---|
| ヘッダー | 21:9 | 幅全体、高さ2247 px | 上下それぞれ351 px |
| 検索 | 3:2 | 高さ全体、幅4425 px | 左右それぞれ410 px |
この2つの枠を自分のカバー画像に重ねてみると、見出しが上から240ピクセル目で始まっていました。21:9のヘッダーでは、上の境界は351ピクセル目を通ります。切り抜きが中央なら、見出し「A journal that stays yours」は真ん中で切れてしまいます。
最初の版は、確認する前に App Store Connect にアップロードしてしまっていました。審査には出ていません。バージョン1.1はまだ提出していないからです。
今はアップロードの前に確認しています。カバー画像の上に両方の枠を描き、縮小したプレビューを保存するスクリプトがこれです。
#!/usr/bin/env python3
"""16:9のアセットに21:9と3:2の切り抜き枠を描く: python3 crop_check.py header.png preview.png"""
import sys
from PIL import Image, ImageDraw
src, preview = sys.argv[1], sys.argv[2]
im = Image.open(src).convert("RGB")
W, H = im.size
d = ImageDraw.Draw(im)
h21 = round(W * 9 / 21) # ヘッダー: 幅全体
top = (H - h21) // 2
d.rectangle((0, top, W - 1, top + h21), outline=(255, 0, 0), width=14)
w32 = round(H * 3 / 2) # 検索: 高さ全体
left = (W - w32) // 2
d.rectangle((left, 0, left + w32, H - 1), outline=(0, 0, 255), width=14)
im.resize((W // 4, H // 4)).save(preview)
print(f"21:9 y {top}..{top + h21}, 3:2 x {left}..{left + w32}")赤い枠がヘッダー、青い枠が検索です。読めなければいけないものはすべて、2つの枠が重なる部分に収める必要があります。
作り直さずに直した方法#
この時点で元のアートボードはもう削除していたので、カバー画像を一から組み立て直す材料がありませんでした。助かったのは、構成の上部が何もない背景だったことです。画像全体を260ピクセル下にずらし、上に空いた帯を同じ背景の最初の260行で埋めました。
from PIL import Image
im = Image.open("header-en.png").convert("RGB")
W, H = im.size
D = 260
out = Image.new("RGB", (W, H))
out.paste(im.crop((0, 0, W, D)), (0, 0)) # 上部の背景の帯
out.paste(im.crop((0, 0, W, H - D)), (0, D)) # 構成全体を D だけ下へ
out.save("header-en-v2.png", optimize=True)見出しは今、500ピクセル目から始まり、両方の枠の内側に収まっています。下では電話の下部が260ピクセル分消えますが、画面はもともとフレームの外まで続いているので、何も壊れません。260行目のつなぎ目は見えません。そこは細部のない柔らかなグラデーションだからです。
この手が使えるのは、ずらす側の端が均一な背景のときだけです。上部に模様や写真があったら、組み立て直すしかなかったでしょう。
わからないこと#
- Apple が実際にどうアセットを切り抜くのか。 中央での切り抜きは私の推測で、根拠は「center of your composition」という一文です。正確なところは App Store Connect の「プレビュー」でしかわかりません。しかも見出しの長さは言語ごとに違うので、ローカライズごとに確認する必要があります。
- iPad、Mac、Vision でアセットがどう見えるか。 プレビューに出るのは iPhone と iPad です。ほかのデバイスでは見たことがありません。
- 検索結果でカバー画像がスクリーンショットより効果を上げるか。 これはバージョンのリリース後の統計でしかわかりません。
似たようなカバー画像を作るなら、最初のアップロードの前に21:9と3:2の枠を重ねてみてください。1分で済みますし、私なら余計な1周を省けていました。ストアページのほかの欄を39言語にどう訳したかは、別の記事に書いています。



