Xcode 15 iOS 17小组件适配必看:Widget needs to adopt container background |
您所在的位置:网站首页 › 小组件开发 › Xcode 15 iOS 17小组件适配必看:Widget needs to adopt container background |
当你更新了 Xcode 15,如果你的 app 中有小组件的代码,在 preview 的页面就会出现上图的预览错误。提示你:Widget needs to adopt container background。虽然这种 breaking change 看起来很吓人,但是适配起来还是很容易的,下面将一一列出 iOS17 小组件有关的 适配方案。 containerBackgroundiOS 17 中新增了一个模式 stand by。这个模式下手机横屏,可以并排显示两个小号的小组件。因为手机此时属于息屏状态,因此苹果建议小组件的背景图层隐藏,这样整体的风格也更搭。 同时锁屏小组件也带到了iPadOS 17 上。相比 iPhone 的锁屏小组件,iPad 因为尺寸更大,因此锁屏小组件的尺寸也支持了一个更大的尺寸。可以显示小号的方形小组件。 苹果为了强推小组件这两个功能,要求所有小组件都必须声明适配接口告知系统小组件的背景图层。这样当小组件显示在 standby 和 iPad 锁屏上时,渲染时可以隐藏背景图层。 如果你的 app 只需要支持 iOS 17(不会真有人这么幸福吧),那么你只需要在 view 实现这个 containerBackground 就可以了,把背景图层像 background 一样放在 content 闭包里。 .containerBackground(for: .widget) { // 背景view Color.black }但是做 iOS 的开发者运气都不会太差,你大概率会得到一个 error: 所以你需要自定义一个类似的方法,判断系统版本以向前兼容: extension View { @ViewBuilder func widgetBackground(_ backgroundView: some View) -> some View { if #available(iOS 17.0, *) { containerBackground(for: .widget) { backgroundView } } else { background(backgroundView) } } }如果你的小组件view不在 app 中展示,那么上述的方法已经足够用了。但是如果你的小组件要在 app 中展示,比如我目前的情况,小组件会在 app 中展示以让用户进行一些主题设置。那么你就会发现 containerBackground 的背景 view 在 app 中不会展示。 因此需要再加一层判断,如果在 app 中正常显示背景图层。 extension View { @ViewBuilder func widgetBackground(_ backgroundView: some View) -> some View { if Bundle.main.bundlePath.hasSuffix(".appex"){ if #available(iOS 17.0, *) { containerBackground(for: .widget) { backgroundView } } else { background(backgroundView) } } else { background(backgroundView) } } } contentMarginsDisabled在配置完 containerBackground 后小组件可以正常运行了,但是很快你就发现一个问题:小组件尺寸变小了。比如下图里黑色是小组件的背景色,外围的一圈是 safeArea。 原因和上一节讲的一样,因为小号小组件会出现在 standby 中,然而 standby 的尺寸更大。因此为了让小组件可以适配不同的尺寸,系统统一给小组件加了一个 safeArea。因此我们的小组件变小了。 如果你的小组件可以针对尺寸大小自适应的话,或者不在乎 standby 中的样式,可以直接在 WidgetConfiguration 中配置关闭系统统一发放的边距。需要注意的是这个配置在 widget 上,不在 view 上。 StaticConfiguration(kind: WorkerWidgetKind.workerSticker.rawValue, provider: WorkerStickerProvider()) { entry in WorkerStickerEntryView(entry: entry) } .contentMarginsDisabled()如果你打算针对不同的 margin 处理布局,你也可以通过全局变量获取到 margin 值。 @Environment(\.widgetContentMargins) var margins extension EnvironmentValues { /// A property that identifies the content margins of a widget. /// /// The content margins of a widget depend on the context in which it appears. The /// system applies default content margins. However, if you disable automatic application of /// default content margins with ``WidgetConfiguration/contentMarginsDisabled()``, the /// system uses the `widgetContentMargins` property in combination with ``View/padding(_)`` /// to selectively apply default content margins. /// @available(iOS 17.0, watchOS 10.0, macOS 14.0, *) @available(tvOS, unavailable) public var widgetContentMargins: EdgeInsets { get } }但是用这个值会有点痛苦,因为苹果常规操作这个全局变量 iOS 17 only。View 相关全局变量的如果要向前兼容需要包在一个 container view 里,有些小麻烦。 showsWidgetContainerBackground如果你的小组件某些 UI 要针对在无背景场景做调整,需要通过 showsWidgetContainerBackground 全局变量来判断。 以我的小组件周五日历为例,本来有背景中间的标题文字视觉就是居中的。但是如果没有背景,标题文字的视觉平衡就不在中间了。而且我的标题文字本来有一个透明度,但是在锁屏上因为没有背景了,有透明度反而让文字看不清了。 因此我需要针对在锁屏上做一点区分处理。如果在锁屏上就在背景上画一个边框。 struct FridayWidgetView: View { @Environment(\.showsWidgetContainerBackground) var showsWidgetContainerBackground var body: some View { ZStack { if !showsWidgetContainerBackground { RoundedRectangle(cornerRadius: 12) .stroke(Color.black, lineWidth: 3) } } .widgetBackground(viewModel.config.theme.coverView) } }特大喜讯,苹果工程师良心发现这个全局变量可以向前兼容。 下图是适配以后的样式。 小号的方形小组件可以在展示在锁屏上又引入了另外一个问题,锁屏中系统会对图片进行黑白处理,某些小组件的核心内容是图片的话不适合展示在图片上。 下面的示例图是我开发的打工人小组件,可以看到显示在 iPad 锁屏上显示效果差到无法用。 为了解决这个问题,需要在小组件配置中声明containerBackgroundRemovable(false)。 struct WeekCalendarWidget: Widget { var body: some WidgetConfiguration { IntentConfiguration(kind: WorkerWidgetKind.weekCalendar.rawValue, intent: WeekCalendarIntent.self, provider: WeekCalendarTimelineProvider()) { entry in WeekCalendarEntryView(entry: entry) } .configurationDisplayName("打工人周历") .description("熬夜可以,熬夜工作可不行") .containerBackgroundRemovable(false) .contentMarginsDisabled() } }配置了这个选项后小组件就不会出现在 iPad 锁屏小组件列表中。 如果要针对图片在不同场景中做单独处理,也可以通过 widgetRenderingMode 这个全局变量判断当前的渲染模式。坏消息:兼容性iOS 16 +。 Bring widgets to new places |
CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3 |