SwiftUI Widget 在构建到设备或模拟器时为空
Posted
技术标签:
【中文标题】SwiftUI Widget 在构建到设备或模拟器时为空【英文标题】:SwiftUI Widget empty when built onto device or simulator 【发布时间】:2021-03-29 17:57:37 【问题描述】:最近在玩 SwiftUI 和 WidgetKit,遇到了一个讨厌的问题。我的小部件似乎在 SwiftUI Canvas 中工作,但在构建到模拟器或设备上时它是完全空的。
图片:
在 SwiftUI 画布中:
https://github.com/beanut/images/blob/main/Screenshot%202020-12-19%20at%204.00.57%20PM.png?raw=true
内置到设备上时:
https://github.com/beanut/images/blob/main/Screenshot%202020-12-19%20at%204.01.13%20PM.png?raw=true
我的代码:
'''
import WidgetKit
import SwiftUI
import Intents
struct Provider: IntentTimelineProvider
func placeholder(in context: Context) -> SimpleEntry
SimpleEntry(date: Date())
func getSnapshot(for configuration: ConfigurationIntent, in context: Context, completion: @escaping (SimpleEntry) -> ())
let entry = SimpleEntry(date: Date())
completion(entry)
func getTimeline(for configuration: ConfigurationIntent, in context: Context, completion: @escaping (Timeline<Entry>) -> ())
var entries = [SimpleEntry]()
let currentDate = Date()
let midnight = Calendar.current.startOfDay(for: currentDate)
let nextMidnight = Calendar.current.date(byAdding: .day, value: 1, to: midnight)!
//To refresh timeline every min
for offset in 0 ..< 60 * 24
let entryDate = Calendar.current.date(byAdding: .minute, value: offset, to: midnight)!
entries.append(SimpleEntry(date: entryDate))
let timeline = Timeline(entries: entries, policy: .after(nextMidnight))
completion(timeline)
struct SimpleEntry: TimelineEntry
let date: Date
struct WidgetEntryView : View
var entry: Provider.Entry
var body: some View
HStack()
VStack
Spacer()
VStack(alignment: .leading)
Text(DateManager().getDayOfWeekInString(date: entry.date)!)
.font(Font(UIFont(name: "HoeflerText-Italic", size: 44)!))
.foregroundColor(.white)
.multilineTextAlignment(.trailing)
.opacity(1)
Text("\(DateManager().getDayAsString(date: entry.date)) \(DateManager().monthAsString(date: entry.date))")
.font(Font(UIFont(name: "Copperplate", size: 26)!))
.multilineTextAlignment(.trailing)
.opacity(1)
.foregroundColor(.gray)
.padding(.leading, 20)
.padding(.bottom, 20)
Spacer()
VStack(alignment: .trailing, spacing: -20)
Text(DateManager().getHour(date: entry.date))
.font(Font(UIFont(name: "Copperplate-Bold", size: 86)!))
.foregroundColor(.white)
.multilineTextAlignment(.trailing)
.opacity(1)
Text(DateManager().getMinuteWithTwoDigits(date: entry.date))
.font(Font(UIFont(name: "Copperplate-Bold", size: 86)!))
.multilineTextAlignment(.trailing)
.opacity(1)
.foregroundColor(.gray)
.padding(.trailing, 15)
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(Image(uiImage: #imageLiteral(resourceName: "moon")), alignment: .center)
@main
struct Widget: SwiftUI.Widget
let kind: String = "Widget"
var body: some WidgetConfiguration
IntentConfiguration(kind: kind, intent: ConfigurationIntent.self, provider: Provider()) entry in
WidgetEntryView(entry: entry)
.configurationDisplayName("TestWidget")
.description("This is an example widget.")
struct Widget_Previews: PreviewProvider
static var previews: some View
WidgetEntryView(entry: SimpleEntry(date: Date()))
.previewContext(WidgetPreviewContext(family: .systemMedium))
'''
**更新 我注意到如果我删除了 VStack,小部件会显示。但它没有显示我是否将代码添加回来。 删除 VStack:
struct WidgetEntryView : View
var entry: Provider.Entry
var body: some View
HStack()
// VStack
// Spacer()
// VStack(alignment: .leading)
// Text(DateManager().getDayOfWeekInString(date: entry.date)!)
// .font(Font(UIFont(name: "HoeflerText-Italic", size: 44)!))
// .foregroundColor(.white)
// .multilineTextAlignment(.trailing)
// .opacity(1)
// Text("\(DateManager().getDayAsString(date: entry.date)) \(DateManager().monthAsString(date: entry.date))")
// .font(Font(UIFont(name: "Copperplate", size: 26)!))
// .multilineTextAlignment(.trailing)
// .opacity(1)
// .foregroundColor(.gray)
//
//
// .padding(.leading, 20)
// .padding(.bottom, 20)
Spacer()
VStack(alignment: .trailing, spacing: -20)
Text(DateManager().getHour(date: entry.date))
.font(Font(UIFont(name: "Copperplate-Bold", size: 86)!))
.foregroundColor(.white)
.multilineTextAlignment(.trailing)
.opacity(1)
Text(DateManager().getMinuteWithTwoDigits(date: entry.date))
.font(Font(UIFont(name: "Copperplate-Bold", size: 86)!))
.multilineTextAlignment(.trailing)
.opacity(1)
.foregroundColor(.gray)
.padding(.trailing, 15)
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(Image(uiImage: #imageLiteral(resourceName: "moon")), alignment: .center)
它确实显示了 UI 元素: https://github.com/beanut/images/blob/main/IMG_002DAA718D1C-1.jpeg?raw=true
请帮助我,并在此先感谢:)!
【问题讨论】:
您找到解决此问题的方法了吗?我遇到了非常相似的事情。 【参考方案1】:我偶然发现了这个问题,试图为自己找到解决同样问题的方法,并且已经找到了解决方案。
我不认为VStack
是这里的问题,而是强制解开其中的 UI 元素的一个或多个值,例如这一行:
Text(DateManager().getDayOfWeekInString(date: entry.date)!)
强制解开 nil
值是阻止我为所有尺寸绘制小部件 UI 的原因。尝试以安全的方式展开值或通过 nil 合并提供后备。
【讨论】:
【参考方案2】:这可能是因为您在getTimeline
函数中调用了WidgetCenter.shared.reloadAllTimelines
:
func getTimeline(for configuration: ConfigurationIntent, in context: Context, completion: @escaping (Timeline<Entry>) -> ())
...
for offset in 0 ..< 60 * 24
WidgetCenter.shared.reloadAllTimelines() // <- this is wrong
let entryDate = Calendar.current.date(byAdding: .minute, value: offset, to: midnight)!
entries.append(SimpleEntry(date: entryDate))
let timeline = Timeline(entries: entries, policy: .after(nextMidnight))
completion(timeline)
WidgetCenter.shared.reloadAllTimelines
的全部意义在于强制刷新时间线并有效地再次调用getTimeline
函数。
从getTimeline
内部调用它是个坏主意,尤其是在循环中。
预览中的代码可能工作正常,因为 getTimeline
函数只被调用一次,所有重复调用都被忽略了。
【讨论】:
谢谢,但它并没有解决我的主要问题,但我的 Mac 现在不再像疯了一样发热。 :) 似乎小部件在删除 VStack 时工作,而在我添加堆栈时停止工作。我已经更新了我的问题:)以上是关于SwiftUI Widget 在构建到设备或模拟器时为空的主要内容,如果未能解决你的问题,请参考以下文章
是否可以将设备 ROM 闪存到 AVD 模拟器或 genymotion?
SwiftUI navigationBarItems:设备旋转时出现“通过属性检测到循环”错误