You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

iOS Widget预览正常但模拟器无法加载,长按无编辑选项求助

iOS Widget 模拟器显示占位状态无法编辑问题

我开发了一款iOS Widget,在预览面板中运行完全正常,但在模拟器中始终显示占位状态,长按也无法调出「编辑Widget」选项。我另有写法几乎完全一致的Widget均可正常运行,不清楚该Widget问题出在哪里,相关代码如下:

import WidgetKit
import AppIntents
import SwiftUI

private let defaultSkiresortDetail = SkiresortDetail(
    id: "foobar"
)

private struct SkiresortWidgetIntent: WidgetConfigurationIntent {
    static var title: LocalizedStringResource = "Ski Resort"
    static var description = IntentDescription("Select the ski resort to display.")
    
    @Parameter(title: "Ski Resort")
    var skiresort: SkiresortDetail
}


private struct SkiresortQuery: EntityQuery {
    func entities(for identifiers: [SkiresortDetail.ID]) -> [SkiresortDetail] {
        return [defaultSkiresortDetail]
    }
    
    func suggestedEntities() -> [SkiresortDetail] {
        [defaultSkiresortDetail]
    }
    
    func defaultResult() -> SkiresortDetail? {
        suggestedEntities().first
    }
}

private struct SkiresortDetail: AppEntity {
    let id: String
    
    static var typeDisplayRepresentation: TypeDisplayRepresentation = "Ski Resort"
    static var defaultQuery = SkiresortQuery()
    
    var displayRepresentation: DisplayRepresentation {
        DisplayRepresentation(title: "\(id)")
    }
}

private struct SkiresortWidgetProvider: AppIntentTimelineProvider {
    func placeholder(in context: Context) -> SkiresortWidgetTimelineEntry {
        return SkiresortWidgetTimelineEntry(date: .now)
    }
    
    func snapshot(for configuration: SkiresortWidgetIntent, in context: Context) -> SkiresortWidgetTimelineEntry {
        return SkiresortWidgetTimelineEntry(date: .now)
    }
    
    func timeline(for configuration: SkiresortWidgetIntent, in context: Context) -> Timeline<SkiresortWidgetTimelineEntry> {
        return Timeline(
            entries: [
                SkiresortWidgetTimelineEntry(date: .now)
            ],
            policy: .atEnd
        )
    }
    
}

private struct SkiresortWidgetTimelineEntry: TimelineEntry {
    let date: Date
}

private struct SkiresortWidgetView: View {
    let entry: SkiresortWidgetTimelineEntry
    
    var body: some View {
        Text("Hello")
        .font(.title3)
        .containerBackground(for: .widget) {}
    }
}

struct SkiresortWidget: Widget {
    let kind: String = "Skiresort_Status"
    
    var body: some WidgetConfiguration {
        AppIntentConfiguration(
            kind: kind,
            intent: SkiresortWidgetIntent.self,
            provider: SkiresortWidgetProvider()
        ) {
            entry in SkiresortWidgetView(entry: entry)
        }
        .configurationDisplayName("Ski Resort Status")
        .description("Your favorite ski resort status at a glance.")
        .supportedFamilies([.systemMedium])
    }
}

#Preview(as: .systemMedium) {
    SkiresortWidget()
} timeline: {
    SkiresortWidgetTimelineEntry(date: .now)
}

问题原因

核心问题是你将与Widget配置Intent相关的结构体都标记为了private,包括SkiresortWidgetIntent、SkiresortQuery、SkiresortDetail:

  • 预览能正常运行是因为预览代码和这些结构体在同一文件内,可以访问private类型;
  • 但在模拟器实际运行时,WidgetKit需要从外部访问这些Intent相关类型来处理配置流程(比如加载实体选择界面),private修饰符会阻止外部访问,导致系统无法完成Widget的初始化配置,最终表现为Widget一直显示占位状态,且长按无法调出编辑选项。

解决方案

移除这些结构体的private修饰符(Swift默认访问级别为internal,足够WidgetKit访问):

修改后的关键代码片段:

let defaultSkiresortDetail = SkiresortDetail(
    id: "foobar"
)

struct SkiresortWidgetIntent: WidgetConfigurationIntent {
    static var title: LocalizedStringResource = "Ski Resort"
    static var description = IntentDescription("Select the ski resort to display.")
    
    @Parameter(title: "Ski Resort")
    var skiresort: SkiresortDetail
}


struct SkiresortQuery: EntityQuery {
    func entities(for identifiers: [SkiresortDetail.ID]) -> [SkiresortDetail] {
        return [defaultSkiresortDetail]
    }
    
    func suggestedEntities() -> [SkiresortDetail] {
        [defaultSkiresortDetail]
    }
    
    func defaultResult() -> SkiresortDetail? {
        suggestedEntities().first
    }
}

struct SkiresortDetail: AppEntity {
    let id: String
    
    static var typeDisplayRepresentation: TypeDisplayRepresentation = "Ski Resort"
    static var defaultQuery = SkiresortQuery()
    
    var displayRepresentation: DisplayRepresentation {
        DisplayRepresentation(title: "\(id)")
    }
}

其他如SkiresortWidgetProvider、SkiresortWidgetTimelineEntry、SkiresortWidgetView可以保留private,因为它们只在Widget内部使用,不需要WidgetKit外部访问。

修改后重新编译运行模拟器,Widget应该能正常加载,长按也能调出编辑选项了。

内容的提问来源于stack exchange,提问作者Fez Vrasta

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.07 10:57:52