Widget配置Intent加载对象时如何显示进度指示器?
Widget配置面板加载数据时显示进度指示器的解决方案
问题背景
在Widget配置流程中,通过Intents Extension提供动态选项列表,选项数据需从后端下载。由于数据获取可能耗时,默认配置面板在加载完成前无任何反馈,希望添加进度指示器,但遇到以下问题:
provideMyOptionsCollection作为异步方法,系统未自动显示加载进度handle方法从未被调用,无法通过它处理加载状态
核心原因
- 系统默认的配置界面不会在
provideMyOptionsCollection等待回调期间展示进度,该方法被设计为尽快返回结果,无内置进度反馈机制 handle方法仅在用户提交Intent(如点击"完成")时触发,不参与选项加载阶段的逻辑
解决方案:自定义配置界面
要实现进度展示,需通过INUIHostedViewControlling协议自定义Widget配置界面,完全掌控UI状态。
步骤1:创建自定义配置ViewController
实现INUIHostedViewControlling协议,添加进度指示器和选项列表控件:
import IntentsUI import UIKit class WidgetConfigViewController: UIViewController, INUIHostedViewControlling { @IBOutlet weak var activityIndicator: UIActivityIndicatorView! @IBOutlet weak var optionsTableView: UITableView! private var fetchedOptions: [IntentData] = [] private var currentIntent: MyConfigurationIntent! private var completionHandler: ((INIntentResponse) -> Void)? // 初始化配置界面 func configureView(for parameters: Set<INParameter>, of intent: INIntent, interactiveBehavior: INUIInteractiveBehavior, context: INUIHostedViewContext, completion: @escaping (Bool, Set<INParameter>, CGSize) -> Void) { currentIntent = intent as? MyConfigurationIntent completionHandler = { response in completion(true, parameters, self.desiredSize) } // 启动进度指示器并开始加载数据 activityIndicator.startAnimating() loadOptionsFromBackend() // 返回初始UI尺寸 completion(true, parameters, desiredSize) } // 后端数据加载逻辑 private func loadOptionsFromBackend() { URLSession.shared.dataTask(with: URL(string: "your-backend-api-url")!) { [weak self] data, _, error in guard let self = self, error == nil, let data = data else { DispatchQueue.main.async { self?.activityIndicator.stopAnimating() // 处理加载失败,比如显示错误提示 } return } // 解析数据为IntentData数组(根据你的实际数据结构调整) let decodedOptions = try? JSONDecoder().decode([IntentData].self, from: data) DispatchQueue.main.async { self.activityIndicator.stopAnimating() self.fetchedOptions = decodedOptions ?? [] self.optionsTableView.reloadData() } }.resume() } // 自定义界面尺寸 var desiredSize: CGSize { let maxWidth = extensionContext?.hostedViewMaximumAllowedSize.width ?? 320 return CGSize(width: maxWidth, height: 400) } } // 实现列表数据源与代理 extension WidgetConfigViewController: UITableViewDataSource, UITableViewDelegate { func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return fetchedOptions.count } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "OptionCell", for: indexPath) let option = fetchedOptions[indexPath.row] cell.textLabel?.text = option.displayTitle // 替换为你的数据字段 return cell } // 处理选项选择逻辑 func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { let selectedOption = fetchedOptions[indexPath.row] currentIntent.myOptions = selectedOption // 绑定到Intent的对应属性 let response = MyConfigurationIntentResponse(code: .success, userActivity: nil) completionHandler?(response) } }
步骤2:关联自定义界面到Intent
打开你的.intentdefinition文件:
- 选中目标
MyConfigurationIntent - 在右侧检查器的"Custom UI"区域,设置
Custom View Controller Class为WidgetConfigViewController - 确保配置界面的Storyboard/XIB已正确关联该ViewController
步骤3:调整原IntentHandler代码
原provideMyOptionsCollection方法可保留(作为备用逻辑),自定义UI已接管选项加载:
extension IntentHandler: MyIntentHandling { func provideMyOptionsCollection(for intent: MyConfigurationIntent, searchTerm: String?, with completion: @escaping (INObjectCollection<IntentData>?, Error?) -> Void) { // 自定义UI已处理加载,此处可返回空集合或缓存数据 completion(INObjectCollection(sections: []), nil) } func handle(intent: MyConfigurationIntent, completion: @escaping (MyConfigurationIntentResponse) -> Void) { // 用户提交配置时的逻辑,比如保存设置到UserDefaults completion(MyConfigurationIntentResponse(code: .success, userActivity: nil)) } }
效果说明
自定义界面会在数据加载时显示进度指示器,加载完成后展示选项列表,解决了原配置面板无反馈的问题。
内容的提问来源于stack exchange,提问作者Wojciech Kulik
相关产品推荐
相关产品推荐

