Hotfixing widgets for iOS 17: containerBackground + padding
How to fix the issue with background and extra padding.
Published: July 4, 2023 Sponsored App StoreIf you built your app that has Home Screen widgets with Xcode 15 you might notice they are broken now. Apple has introduced some big changes to widgets that you need to take into account. One issue is that instead of your widget content, you might see just a message that says: “please adopt containerBackground API”.
Another problem is that there is now extra padding applied that most likely pushes your existing backgrounds from the edges. Both of these are the indirect result of introduction of the StandBy mode which also utilizes widgets.
Properly supporting these new features might require some thinking depending on the sophistication of your widgets. In this post my goal is to show how to “hot fix” them for now to get back to the status quo so you can publish your beta versions without the widgets not working or looking weird.
Fixing the containerBackground API
Because the StandBy mode meant to use the widgets without backgrounds, the system needs to know which views are part of the background to remove them in this context. Hence Apple added this “please adopt containerBackground API” message.
To adopt this API, we need to use the new modifier containerBackground
, which works much like the old background
but takes contextual parameter that tells it for which context we want the background.
Suppose we have existing backgroundView
for our widget, with containerBackground
we would use it like this:
.containerBackground(for: .widget) {
backgroundView
}
Once you add this modifier to your widget, it will start working again on iOS 17. Unfortunately this new modifier isn’t backwards compatible, so if you want to support earlier versions of iOS, you can do something like this:
extension View {
func widgetBackground(backgroundView: some View) -> some View {
if #available(iOSApplicationExtension 17.0, *) {
return containerBackground(for: .widget) {
backgroundView
}
} else {
return background(backgroundView)
}
}
}
And then usage:
var body: some View {
ZStack {
// widget content
}
.widgetBackground(backgroundView: backgroundView)
}
Fixing extra padding
Once you managed to fix the “please adopt containerBackground API” message, you will probably notice that your widgets don’t look the same. There is now extra padding added by the system depending on the context where the widget is displayed.
The easiest fix that should really be temporary, is to opt out of this new behavior with newly introduced modifier contentMarginsDisabled
. This will make the widget appear the same as on iOS 16 however in StandBy and possibly other (even future?) context the margins may be too small.
This new modifier is applied to the widget configuration and is backwards compatible (prior to iOS 17 it just does nothing), so we don’t need any extra logic for keeping backwards compatibility.
It is possible you will need to force widgets to reload for this change to take effect.
struct CountdownWidget: Widget {
let type = "countdown"
var body: some WidgetConfiguration {
IntentConfiguration(kind: type, intent: SelectGameIntent.self, provider: CountdownTimelineProvider()) { entry in
CountdownWidgetView(entry: entry)
}
.configurationDisplayName("Release Countdown".localized())
.description("release_countdown_description".localized())
.supportedFamilies([.systemSmall, .systemMedium])
.contentMarginsDisabled()
}
}
More details
I recommend watching the WWDC session “Bring widgets to new places” from WWDC 23 for more details and context.
It is also possible this API will change with the final version of Xcode 15. I will try to keep this blog post updated if things change.
If you have any comments, improvements etc. Do let me know, thanks!