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

Widget配置Intent加载对象时如何显示进度指示器?

Widget配置面板加载数据时显示进度指示器的解决方案

问题背景

在Widget配置流程中,通过Intents Extension提供动态选项列表,选项数据需从后端下载。由于数据获取可能耗时,默认配置面板在加载完成前无任何反馈,希望添加进度指示器,但遇到以下问题:

  • provideMyOptionsCollection作为异步方法,系统未自动显示加载进度
  • handle方法从未被调用,无法通过它处理加载状态

核心原因

  1. 系统默认的配置界面不会在provideMyOptionsCollection等待回调期间展示进度,该方法被设计为尽快返回结果,无内置进度反馈机制
  2. 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文件:

  1. 选中目标MyConfigurationIntent
  2. 在右侧检查器的"Custom UI"区域,设置Custom View Controller Class为WidgetConfigViewController
  3. 确保配置界面的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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.19 14:05:22