
iOS 27 で safeAreaBar のエフェクトが hard 相当に変わっていたので検証・修正してみた
個人開発している iOS アプリ「NSEasyConnect」を Xcode 27(iOS 27 SDK)でビルドしたところ、画面下部のボタン周りの見た目が iOS 26 のときと変わっていることに気づいた。アプリのコードは一切変更しておらず、ビルドに使う SDK が変わっただけで生じた差異だ。
| iOS 26.5 | iOS 27.0 |
|---|---|
![]() |
![]() |
本記事では、この変更の原因と対策についてまとめた。
検証環境
- MacBook Pro(Apple M1 Pro、メモリ 32GB)
- macOS 26.6.2(25G83)
- Xcode 27.0(27A266a)
- iPhone 17 Pro Max シミュレータ
- iOS 26.5 / iOS 27.0
対策前の挙動
私のアプリでは、共有・保存画面やゲームデータベースの更新画面で、画面下部にボタンを置いている。スクロールするコンテンツがボタンの下に潜り込んだときに Liquid Glass らしい見た目になるよう、iOS 26 以降では safeAreaBar(edge:alignment:spacing:content:) を使っている。
iOS 18 以前でも iOS 26 以降と近い見た目になるよう、safeAreaBarCompat という互換用の View 拡張を用意している。iOS 18 以前の実装は本記事の主題から外れるため割愛する。
public extension View {
@ViewBuilder
func safeAreaBarCompat(
edge: VerticalEdge,
showsDivider: Bool = true,
@ViewBuilder content: () -> some View,
) -> some View {
if #available(iOS 26.0, macOS 26.0, *) {
safeAreaBar(edge: edge, content: content)
} else {
// iOS 18 以前は safeAreaInset で代替する
// ...
}
}
}
ここからは NSEasyConnect 本体ではなく、再現用に用意した最小構成の検証アプリで確認していく。safeAreaBar を使った画面を Xcode 27(iOS 27 SDK)でビルドし、iOS 26.5 と iOS 27.0 のシミュレータで表示した。以降のスクリーンショットはすべてこの検証アプリのものだ。
| iOS 26.5(既定) | iOS 27.0(既定) |
|---|---|
![]() |
![]() |
冒頭の NSEasyConnect と同じ現象が再現できている。iOS 26.5 では、ボタンの下に潜り込んだコンテンツがグラデーションでなだらかにぼけていく。一方 iOS 27.0 では、ボタンの少し上にくっきりした境界線が入り、その下がすりガラスのように塗りつぶされる。
scroll edge effect と既定のスタイル
原因を説明する前に、この見た目を決めている仕組みを確認しておく。
スクロールするコンテンツと、ツールバーや safeAreaBar のような固定されたコントロールとの境界に適用されるぼかしは scroll edge effect と呼ばれ、そのスタイルは ScrollEdgeEffectStyle で表される。用意されているのは次の 3 つだ。
automatic: システムがスタイルを決めるhard: 不透明に近く、はっきりした直線的な境界を引くsoft: 控えめでぼかされた境界にする
ここで重要なのは automatic の扱いだ。ドキュメントには、既定では automatic なスタイルが設定され、どのスタイルを適用するかはプラットフォームとコンテキストに応じてシステムが決める、と書かれている。hard と soft は、自動で選ばれたスタイルがコンテンツに合わない場合に開発者が明示的に指定するものだ。
つまり、iOS 26 の既定が soft だったわけではなく、automatic の解決結果が soft 相当だった、と理解するのが正確だ。
原因
iOS 27 SDK でビルドした場合に、safeAreaBar に対する scroll edge effect で automatic が解決するスタイルが hard 相当に変わっている。Xcode 26.6(iOS 26 SDK)でビルドしたアプリを iOS 27.0 上で動かしても影響はなかった。
今回の検証では、iOS 26.5 で scrollEdgeEffectStyle(.hard, for: .bottom) を指定したときとほぼ同じエフェクトだった。
| iOS 26.5(.hard 指定) |
|---|
![]() |
リリースノートについては、iOS & iPadOS 27 Release Notes と Xcode 27 Release Notes の双方を確認したが、関連する記述は見当たらなかった。
対策
scrollEdgeEffectStyle(_:for:) で .soft を明示的に指定すると、システムの自動判定から外れ、iOS 27 でも iOS 26 に近いなだらかなグラデーションになる。
ScrollView {
// ...
}
.scrollEdgeEffectStyle(.soft, for: .bottom)
.safeAreaBar(edge: .bottom) {
// 画面下部のボタン
}
scrollEdgeEffectStyle(_:for:) は ScrollView や List に直接付けなくても、それらを包む外側のビューに付ければ適用される。さきほどの safeAreaBarCompat には以下のように変更を加えた。
public extension View {
@ViewBuilder
func safeAreaBarCompat(
edge: VerticalEdge,
showsDivider: Bool = true,
@ViewBuilder content: () -> some View,
) -> some View {
if #available(iOS 26.0, macOS 26.0, *) {
// iOS 27 で automatic の解決結果が hard 相当に変わったため、soft を明示して揃える
scrollEdgeEffectStyle(.soft, for: edge == .top ? .top : .bottom)
.safeAreaBar(edge: edge, content: content)
} else {
// iOS 18 以前は safeAreaInset で代替する
// ...
}
}
}
scrollEdgeEffectStyle(_:for:) は iOS 26 以降の API であるため、追加の #available チェックは不要だ。ただし、以下のとおり iOS 26 と iOS 27 で .soft を指定したときの見た目は厳密には一致しない。
| iOS 26.5(.soft) | iOS 27.0(.soft) |
|---|---|
![]() |
![]() |
エフェクト自体が不要であれば、scrollEdgeEffectHidden(_:for:) を使えば、指定した端のエフェクトを取り除ける。
検証していないこと
今回確認したのは safeAreaBar の下端(.bottom)のみだ。上端(.top)やツールバー、safeAreaBar を使わない素の ScrollView の端で同じ変化が起きるかは検証していない。また、検証はすべてシミュレータ上でおこなっている。
まとめ
SDK を切り替えただけで見た目が変わるのは厄介だが、scrollEdgeEffectStyle(.soft, for:) の 1 行で対処できるため影響は小さい。
リリースノートに記載はないものの、automatic はドキュメント上「システムが決める」と明記されたスタイルだ。その解決結果が OS のバージョンによって変わること自体は仕様の範囲内といえる。加えて、iOS 27 SDK でビルドした場合にのみ挙動が変わるという SDK 依存の切り替え方は、Apple が既存アプリを壊さずに挙動を変更する際の定石だ。これらを踏まえると、意図的な変更と考えるのが妥当だろう。
裏を返せば、automatic のままにしている限り、今後の OS バージョンでも同様の変化が起こりうる。見た目を固定したい箇所ではスタイルを明示しておくのが安全だ。Xcode 27 でビルドする際は、safeAreaBar 周りの見た目を一度確認しておくとよいだろう。













