新しい iPhone が出るたびに、私はストア用のスクリーンショットを撮り直していました。壁紙アプリを16言語で出していると、機種サイズが1つ増えるだけで撮影対象が言語の数だけ膨らみます。iPhone Air が加わったとき、また同じ作業をするのかと少し気が重くなったのを覚えています。
ところが実際に App Store Connect の要求仕様を読み直すと、必須なのは6.9インチの iPhone と13インチの iPad の2枚だけでした。小さい機種は Apple が自動で縮小して表示します。つまり、増えたサイズの数だけ撮り足す必要は、もう何年も前からなかったのです。
この記事は、その事実を運用の仕組みに落とし込むまでの記録です。撮影対象を最大機種の1枚に絞り、fastlane で言語ごとに自動撮影し、frameit で端末フレームと訳文キャプションを合成するところまでを、実際に動く設定で示します。
App Store Connect が実際に要求するのは2枚だけ
まず前提を正確にしておきます。2026年時点で、App Store Connect が「必須」としているスクリーンショットの表示サイズは次の2つです。他の機種は、この2枚から自動で縮小生成されます。
| デバイスクラス | 必須の基準機種 | 解像度(px・縦) | アスペクト比 |
| iPhone(6.9インチ) | iPhone 17 Pro Max 相当 | 1320 × 2868 | 19.5 : 9 |
| iPad(13インチ) | iPad Pro 13インチ(M4) 相当 | 2064 × 2752 | 4 : 3 |
条件は2つあります。iPhone と iPad の両対応アプリは、それぞれのデバイスクラスに別々のスクショが要ります。そして各シェルフ(機種の枠)ごとに最低3枚は必要で、1枚だけでは登録できません。
ここで効いてくるのが「6.9インチ以外は自動縮小」という仕様です。現行のノッチ/Dynamic Island 世代の iPhone は、6.9インチも6.3インチも6.1インチも、すべて 19.5:9 の同じアスペクト比です。だから最大機種で撮った1枚を縮小しても、レイアウトが崩れません。iPad も同様に4:3で揃っています。撮り分けが必要だった時代の名残で全サイズを用意し続けるのは、単純に手間の二重払いです。
なぜ「一番大きい機種で撮る」が正解になるのか
かつては6.5インチと5.5インチの両方が必須で、私も両方を撮り分けていました。5.5インチ(iPhone 8 Plus)は16:9で、6.5インチとはアスペクト比が違うため、単純な縮小では余白や見切れが出たのです。この経験があると「サイズごとに撮る」という発想が体に染みつきます。
しかし5.5インチの必須要件は撤廃され、現在残っている iPhone のシェルフは6.9インチだけです。そして先ほど触れたとおり、必須機種と自動縮小先の機種はアスペクト比が一致しています。縮小は等比で行われ、文字の相対サイズも構図も保たれます。
私が最終的に採った判断はこうです。撮影は6.9インチ iPhone と13インチ iPad の2機種に固定し、それより小さい機種のプレビューは App Store Connect の縮小任せにします。実機でどう縮小されるかは、公開後に手持ちの iPhone で一度だけ確認すれば十分でした。ここを「全機種で完璧に」と欲張ると、言語の数だけ確認作業が増えて破綻します。
Rork や Rork Max で作ったアプリでも前提は同じです。ストア掲載画像は最終的に App Store Connect が扱う画像仕様に従うため、生成ツールが何であってもこの2枚に集約できます。
fastlane snapshot で6.9インチを1コマンドで撮る
撮影の自動化には fastlane の snapshot を使います。仕組みは、UI テストを起動して指定した画面で snapshot(...) を呼び、シミュレータのスクリーンショットを言語ごとに撮るというものです。
まず Snapfile で対象機種と言語を絞ります。ここで機種を6.9インチと13インチだけに書くことが、この記事の主眼そのものです。
# fastlane/Snapfile
devices([
"iPhone 17 Pro Max", # 6.9インチ = App Store 必須の基準機種
"iPad Pro 13-inch (M4)" # 13インチ iPad
])
languages([
"en-US",
"ja",
"de-DE",
"fr-FR",
"zh-Hans"
])
scheme("MyAppUITests")
output_directory("./fastlane/screenshots")
clear_previous_screenshots(true) # 撮り直しのたびに古い画像を消す
次に UI テスト側です。React Native / Expo で付けた testID は、iOS では accessibility identifier に変換されるため、そのまま XCUITest の要素検索に使えます。ここを知らないと「なぜ要素が見つからないのか」で詰まります。
// ScreenshotUITests.swift
import XCTest
final class ScreenshotUITests: XCTestCase {
override func setUpWithError() throws {
continueAfterFailure = false
let app = XCUIApplication()
setupSnapshot(app) // fastlane が言語・保存先を注入する
app.launch()
}
func testCaptureStoreScreens() throws {
let app = XCUIApplication()
// 起動直後のホーム。読み込みを待ってから撮る
XCTAssertTrue(app.otherElements["homeScreen"].waitForExistence(timeout: 10))
snapshot("01_Home")
// testID="browseTab" が iOS の accessibility identifier になる
app.buttons["browseTab"].tap()
snapshot("02_Browse")
app.buttons["settingsTab"].tap()
snapshot("03_Settings")
}
}
最後に lane をまとめます。capture_screenshots が Snapfile を読み、frame_screenshots が後述の frameit 設定を読みます。
# fastlane/Fastfile
platform :ios do
desc "多言語のストアスクショを撮ってフレーム合成する"
lane :screens do
capture_screenshots # Snapfile の機種×言語で撮影
frame_screenshots(white: true) # Framefile.json に従って端末枠を合成
end
end
これで fastlane screens の1コマンドで、2機種 × 5言語 × 3画面 = 30枚が生成されます。以前サイズを撮り分けていた頃は、同じ内容で3サイズ × 5画面 × 16言語 = 240枚を手作業で管理していました。枚数もさることながら、手作業の撮影から解放される効果が大きいです。
frameit で端末フレームと訳文キャプションを合成する
撮っただけの生スクショは、そのまま出すと訴求が弱くなります。frameit で端末フレームに収め、上部に訳文のキャッチコピーを載せると、掲載画像として成立します。
Framefile.json で全言語共通のスタイルを一括指定します。フォントは多言語で字形が破綻しない Noto 系に揃えるのが安全です。日本語・中国語・ドイツ語のウムラウトまで1つのフォントで面倒を見られます。
{
"default": {
"title": { "font": "./fonts/NotoSans-Bold.ttf", "color": "#111827" },
"keyword": { "font": "./fonts/NotoSans-Bold.ttf", "color": "#2563EB" },
"background": "./bg/soft-gradient.png",
"padding": 48,
"show_complete_frame": false,
"title_below_image": false
},
"data": [
{ "filter": "01_Home", "keyword": { "color": "#7C3AED" } }
]
}
キャプションの文言は、画像ファイルと同じ言語フォルダに title.strings と keyword.strings を置くと、frameit が言語ごとに差し替えます。
# fastlane/screenshots/ja/title.strings
"01_Home" = "毎朝、違う景色から始まる";
"02_Browse" = "5000点から今日の一枚を";
"03_Settings" = "広告なしで静かに使う";
言語ごとにコピーを用意するのは手間ですが、ここは機械翻訳のまま出さないほうがよいと私は考えています。スクショのキャッチは、その言語圏で「どう読まれるか」で刺さり方が変わるためです。この観点は壁紙アプリの App Store ローカライズを多言語で運用して見えた所感でも触れています。
言語×機種の本数をどこまで削れるか
「削ってよい」と「削ってはいけない」の線引きを、実運用の判断としてまとめておきます。
| 要素 | 削れるか | 判断の根拠 |
| 6.9インチ未満の iPhone サイズ | 削れる | Apple が等比縮小。アスペクト比が同一で崩れない |
| 11インチ以下の iPad サイズ | 削れる | 13インチから自動縮小される |
| 掲載言語そのもの | 削れない | 未指定の言語は既定言語の画像が出て訴求が落ちる |
| 1シェルフあたりの枚数 | 3枚未満は不可 | App Store Connect が登録を拒否する |
| iPad 対応アプリの iPad スクショ | 削れない | デバイスクラスごとに別途必須 |
私の壁紙アプリ群では、この整理で1アプリあたりの用意枚数が 240枚から 80枚(6.9インチのみ・5画面・16言語)に減りました。枚数にして約66%の削減です。個人開発で複数アプリを並行運用していると、この二重払いを止められた効果は地味に効いてきます。iPad 非対応のアプリならさらに iPad 分がまるごと不要です。ここで見落としがちなのが「言語は削れない」という点で、機種は縮小任せにできても、掲載言語を減らすと未指定言語の枠に既定言語(多くは英語)の画像が出てしまい、現地読者への訴求が確実に落ちます。
Rork Max(SwiftUI) と Rork(Expo) で撮影経路が変わる点
同じ「6.9インチで撮る」でも、生成基盤によって UI テストの当て方が変わります。
Rork(React Native / Expo)の場合は、npx expo prebuild で ios/ プロジェクトを生成してから、Xcode で UI テストターゲットを追加します。要素の指定は前述のとおり testID 由来の accessibility identifier を使います。Expo のプレビュービルドと本番ビルドで挙動が食い違うことがある点は、Rork Max のプレビュービルドと実機の不整合を直すの観点がそのまま役立ちます。
Rork Max(ネイティブ SwiftUI)の場合は、最初から Xcode プロジェクトなので UI テストターゲットの追加が素直です。SwiftUI のビューには .accessibilityIdentifier("browseTab") を明示的に付けておくと、XCUITest から安定して掴めます。自動生成されたビューは identifier が付いていないことが多いので、撮影対象の要素にだけ後付けするのが現実的です。
いずれの経路でも、撮影の出口である App Store Connect の2枚仕様は共通です。生成ツールを問わず、集約先は変わりません。
つまずきやすい落とし穴
実際に私が踏んだ非自明な罠を、対処とあわせて3つ挙げます。番号順に確認すれば、たいていの生成事故は回避できます。
-
snapshot(...) を呼ぶ前に画面の描画完了を待っていないと、ローディング中のスケルトンやスプラッシュが撮れてしまいます。上のコードで waitForExistence(timeout:) を挟むのが解決策です。特に画像を大量に読むアプリでは、待たずに撮ると空のグリッドが並んだ悲しい掲載画像になります。
-
言語ごとのフォント埋め込み漏れです。frameit のフォントを英字用のままにしておくと、日本語や中国語のキャプションが豆腐(□)になります。Noto Sans CJK のような多言語カバレッジのあるフォントを1つ指定し、全言語をそれで通すのが確実な回避策でした。
-
プレビュービルドで撮った画像を、そのまま本番運用の掲載に流用してしまうことです。プレビューと本番でフォントや余白が食い違うことがあり、掲載後に気づくと差し替えの手戻りになります。撮影は本番と同じビルド構成のシミュレータで回すのが安全です。
スクショの撮り直しに疲れている方は、まず自分のアプリの Snapfile を6.9インチ iPhone の1行に書き換えて、fastlane screens を一度走らせてみてください。そこから縮小任せにできる範囲が、思っていたより広いことに気づけるはずです。
ストアの見せ方は公開後も少しずつ調整していくものだと感じています。私自身もまだ最適な構図を探している途中ですが、撮影の手間を仕組みで削れた分を、キャッチコピーの磨き込みに回せるようになりました。お読みいただきありがとうございました。