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代码库?

SwiftUI navigationBarItems:设备旋转时出现“通过属性检测到循环”错误

Flutter:在连接的设备上运行或构建到 apk 时未加载 Json 文件

在 SwiftUI 中呈现两张工作表

SwiftUI onDrag 在设备上运行时不显示预览图像