ウィジェットの横幅が、アップデートで22pt狭くなった——contentMarginsDisabled と GeometryReader の落とし穴
Sheet Widget は、スプレッドシートの表をウィジェットにそのまま描くアプリです。表の大きさがウィジェットの大きさに直結するので、数ポイントの違いがそのまま見た目に出ます。
あるアップデートで、ウィジェットの表が前のバージョン(1.0)より狭く見えるという問題が起きました。直すのに3回かかったので、そのときに分かったことをまとめます。
きっかけ:機種ごとの寸法を持つのをやめた
1.0 では、iPhone の機種ごとのウィジェットの大きさを、コードの中に定数で持っていました。これだと新しい機種が出るたびに追加が必要になるので、GeometryReader で実際の大きさを受け取って描く形に変えました。
1回目:22pt 狭くなった
GeometryReader の大きさをそのまま使うようにしたところ、1.0 より横幅が 22pt 狭くなりました。iPhone 12 で測ると、1.0 では表の幅が 329.3pt だったのが、306.7pt になっていました。
原因は、iOS 17 から入ったウィジェットのコンテンツマージンです。iOS 17 以降のウィジェットは、標準で内側に余白(マージン)が付きます。GeometryReader が返す大きさも、その余白を引いたあとの値になっていました。
2回目:今度ははみ出した
余白をなくすために、ウィジェットに .contentMarginsDisabled() を付けました。
StaticConfiguration(kind: kind, provider: Provider()) { entry in
SheetsWidgetEntryView(entry: entry)
}
.contentMarginsDisabled()
すると今度は、広くなりすぎて、ウィジェットの丸い角から表がはみ出すようになりました。測ってみると、ヘッダーが 330.7pt、表が 340.3pt で、本来はヘッダーの内側に収まるはずの表のほうが広くなっていました。余白がゼロになったことで、ウィジェットの外枠いっぱいまで描いてしまったためです。
3回目:実寸から固定の余白を引く
最終的には、.contentMarginsDisabled() で本当の大きさを受け取ったうえで、自分で決めた固定の余白(左右 4.5pt、上下 1.5pt)を引く形にしました。この値は、1.0 の iPhone 12 での寸法(枠 338×158pt、表 329×155pt)から逆算したものです。
/// 枠の内側へ寄せる量。1.0 の見た目(338×158 の枠に 329×155)から逆算した値。
private static let inset = CGSize(width: 4.5, height: 1.5)
var body: some View {
GeometryReader { geo in
let inner = CGSize(width: max(geo.size.width - Self.inset.width * 2, 0),
height: max(geo.size.height - Self.inset.height * 2, 0))
content(fullSize: inner)
.frame(width: inner.width, height: inner.height)
.padding(.horizontal, Self.inset.width)
.padding(.vertical, Self.inset.height)
}
}
実寸から引く形なので、iPad のように枠の大きさが違う端末でも、同じように内側に収まります。
この形にしてから実機で測ると、ヘッダーが 332.7pt、表が 331.3pt で、表がヘッダーの内側にきちんと収まり、1.0 と同じ見た目に戻りました。
教訓:数字で確かめる
3回とも、見た目だけでは「たぶん直った」と思える程度の差でした。毎回、実機のスクリーンショットを撮って、ヘッダーと表の幅を画素で測り、1.0 の値と比べることで、直ったかどうかを判断しました。
- iOS 17 以降のウィジェットには、標準で余白がある
GeometryReaderの大きさは、その余白を引いたあとの値.contentMarginsDisabled()を使うなら、余白は自分で決めて引く- 見た目の調整は、実機の画像を測って数字で確かめる
ウィジェットのデザインを「前と同じ見た目のまま」作り直すときは、この余白の扱いに注意してください。
Sheet Widget は App Store で公開しています。