作为一名 iOS 开发人员,该渠道有一些令人兴奋的特性和功用值得探究。 其间,小部件是我的最爱。 小部件已成为 iOS 和 macOS 体验中不可或缺的一部分,而且随着 SwiftUI 中引进的最新功用,它们现在变得更加强壮。 在本文中,咱们将讨论怎么通过交互性和动画使小组件变得栩栩如生,使它们更具吸引力和视觉吸引力。 咱们将深入讨论动画怎么与小组件合作运用的细节,并展现新的 Xcode Preview API,它能够完结快速迭代和自界说。 此外,咱们将探究怎么运用了解的控件(如 Button 和 Toggle)向小部件添加交互性,并利用 App Intents 的强壮功用。 那么让咱们开端吧!

小部件中的交互性 小部件在独自的进程中呈现,它们的视图代码仅在归档期间运行。 为了使小组件具有交互性,咱们能够运用 Button 和 Toggle 等控件。 但是,由于 SwiftUI 不会在应用程序的进程空间中履行闭包或改动绑定,因而咱们需求一种办法来表明可由小部件扩展履行的操作。 App Intents 为此供给了一个解决方案,答应咱们界说可由体系调用的操作。 通过导入 SwiftUI 和 AppIntents,咱们能够运用接受 AppIntent 作为参数的 Button 和 Toggle 初始值设定项来履行所需的操作。

现在咱们要为现有项目创立小组件。

交互小组件 — iOS 17

相应地命名它。 请注意,禁用两个复选框

交互小组件 — iOS 17

现在我将运用清单和按钮重写现有代码。

struct Provider: TimelineProvider {
    func placeholder(in context: Context) -> SimpleEntry {  
        SimpleEntry( checkList: Array(ModelData.shared.items.prefix(3)))  
    }  
    func getSnapshot(in context: Context, completion: @escaping (SimpleEntry) -> ()) {  
        let entry = SimpleEntry(checkList: Array(ModelData.shared.items.prefix(3)))  
        completion(entry)  
    }  
    func getTimeline(in context: Context, completion: @escaping (Timeline<Entry>) -> ()) {  
        //var entries: [SimpleEntry] = []  
        // Generate a timeline consisting of five entries an hour apart, starting from the current date.  
        let data = Array(ModelData.shared.items.prefix(3))  
        let entries = [SimpleEntry(checkList: data)]  
        let timeline = Timeline(entries: entries, policy: .atEnd)  
        completion(timeline)  
    }  
}
struct SimpleEntry: TimelineEntry {  
    var date: Date = .now  
    var checkList: [ProvisionModel]  
}
struct InteractiveWidgetEntryView : View {  
    var entry: Provider.Entry  
    var body: some View {  
        VStack(alignment: .leading, spacing: 5.0) {  
            Text("My List")  
            if entry.checkList.isEmpty{  
                Text("You've bought all")  
            }else{  
                ForEach(entry.checkList) { item in  
                    HStack(spacing: 5.0){  
                        Image(systemName: item.isAdded ? "checkmark.circle.fill":"circle")  
                        .foregroundColor(.green)  
                        VStack(alignment: .leading, spacing: 5){  
                            Text(item.itemName)  
                            .textScale(.secondary)  
                            .lineLimit(1)  
                            Divider()  
                        }  
                    }  
                }  
            }  
        }  
        .containerBackground(.fill.tertiary, for: .widget)  
    }  
}
struct InteractiveWidget: Widget {  
let kind: String = "InteractiveWidget"  
var body: some WidgetConfiguration {  
    StaticConfiguration(kind: kind, provider: Provider()) { entry in  
            InteractiveWidgetEntryView(entry: entry)  
        }  
        .configurationDisplayName("My Widget")  
        .description("This is an example widget.")  
    }  
}

供给的代码在 iOS 或 macOS 应用程序中运用 SwiftUI 界说小部件。 让咱们分化代码并解说每个部分:

  1. Provider:该结构体契合TimelineProvider协议,担任向widget供给数据。 它包含三个功用:
  • placeholder(in:):此函数回来一个占位符条目,表明首次添加小部件时的外观。 它运用派生自 ModelData.shared.items 的清单数组创立 SimpleEntry。
  • getSnapshot(in:completion:):此函数生成一个表明小部件当时状况的快照条目。 它运用派生自 ModelData.shared.items 的清单数组创立 SimpleEntry。
  • getTimeline(in:completion:):此函数生成小部件的条目时间线。 它运用派生自 ModelData.shared.items 的清单数组创立 SimpleEntry 实例的数组,并回来包含这些条目的时间线。
  1. SimpleEntry:此结构契合 TimelineEntry 协议,表明小部件时间线中的单个条目。 它包含一个表明条目日期的日期特点和一个 checkList 特点,后者是一个 ProvisionModel 项的数组。
  2. InteractiveWidgetEntryView:此结构界说用于显现小部件条目的视图层次结构。 它选用 Provider.Entry 类型的条目作为输入。 在 body 特点内部,它创立一个具有对齐和距离设置的 VStack。 它显现一个标题,并依据 checkList 数组是否为空,显现一条消息或迭代该数组以显现每个项目的信息。
  3. InteractiveWidget:该结构界说小部件自身。 它契合Widget协议并指定了Widget的品种。 它供给了一个 StaticConfiguration,其间包含一个 Provider 实例作为数据供给者,并供给一个 InteractiveWidgetEntryView 作为每个条目的视图。 它还设置小部件的显现名称和描述。
  4. Preview:此代码块用于在开发过程中预览小部件的外观。 它为 .systemSmall 巨细的小部件创立预览,并供给 SimpleEntry 实例作为条目。 总的来说,此代码设置了一个运用 SwiftUI 结构显现清单的小部件。 小部件的数据由 Provider 结构供给,条目的视图由 InteractiveWidgetEntryView 结构界说。 InteractiveWidget 结构装备小部件并供给用于开发目的的预览。

还有按钮动作!

Apple 为此推出了 AppIntents!

我现已创立了视图模型和应用程序目的。

struct ProvisionModel: Identifiable{
    var id: String = UUID().uuidString  
    var itemName: String  
    var isAdded: Bool = false  
}  
class ModelData{  
    static let shared = ModelData()  
    var items: [ProvisionModel] = [.init(  
    itemName: "Orange"  
    ), .init(  
    itemName: "Cheese"  
    ), .init(  
    itemName: "Bread"  
    ), .init(  
    itemName: "Rice"  
    ), .init(  
    itemName: "Sugar"  
    ), .init(  
    itemName: "Oil"  
    ), .init(  
    itemName: "Chocolate"  
    ), .init(  
    itemName: "Corn"  
    )]  
}

供给的代码包含两个数据结构的界说:ProvisionModel 和 ModelData。 以下是每项的解说:

ProvisionModel:该结构表明清单中的一个供给项。 它契合可辨认协议,该协议要求它具有仅有的标识符。 它具有以下特点:

id:一个字符串特点,保存运用 UUID 生成的仅有标识符。 每个 ProvisionModel 实例都会有一个不同的 id。

itemName:表明供给项目名称的字符串特点。

isAdded:一个布尔特点,指示该项目是否已添加到清单中。 它运用默认值 false 进行初始化。

ModelData:此类充当数据存储和单例,供给对供给项的同享拜访。 它具有以下组件: 同享:ModelData 类型的静态特点,表明类的同享实例。 它遵从单例形式,答应跨应用程序拜访同一实例。

items:一个数组特点,包含表明供给项的 ProvisionModel 实例。 该数组运用一组预界说的项目进行初始化,每个项目都运用特定的 itemName 进行初始化。 ModelData.shared 实例供给对此数组的拜访。 总的来说,此代码为清单应用程序设置了数据模型。 ProvisionModel 结构界说每个供给项的特点,包含其仅有标识符以及是否已添加到清单中。 ModelData 类供给对供给项列表的同享拜访,并遵从单例形式以确保拜访和修改数据的一致性。

现在是 appIntent 的时候了!

struct MyActionIntent: AppIntent{
    static var title: LocalizedStringResource = "Toggle Task State"  
    @Parameter(title: "Task ID")  
    var id: String  
    init(){  
    }  
    init(id: String){  
    self.id = id  
}  
func perform() async throws -> some IntentResult {  
    if let index = ModelData.shared.items.firstIndex(where: { $0.id == id }) {  
        ModelData.shared.items[index].isAdded.toggle()  
        let itemToRemove = ModelData.shared.items[index]  
        ModelData.shared.items.remove(at: index)  
        DispatchQueue.main.asyncAfter(deadline: .now() + 2) {  
        ModelData.shared.items.removeAll(where: { $0.id == itemToRemove.id })  
        }  
        print("Updated")  
        }  
        return .result()  
    }  
}

供给的代码界说了一个名为 MyActionIntent 的结构,该结构契合 AppIntent 协议。 此结构表明在清单应用程序中切换使命状况的目的。 以下是对其组成部分的解说:

title(静态特点):该特点表明操作目的的标题。 它的类型为 LocalizedStringResource,它是用于本地化目的的本地化字符串资源。

id(特点装修器):该特点用@Parameter装修,表明需求切换的使命的ID。

init():这是结构的默认初始化程序。 它不履行任何特定的初始化。

init(id: String):此初始化程序答应您运用特定使命 ID 创立 MyActionIntent 实例。

Perform()(办法):AppIntent 协议需求此办法,并履行与 Intent 相关的操作。 以下是其实施细目: 它检查 ModelData.shared.items 数组中是否存在与目的中供给的 ID 匹配的使命。

如果找到匹配项,它将运用toggle() 办法切换使命的isAdded 特点。 这会改动使命的状况。 然后,它创立一个局部变量 itemToRemove 来存储切换的使命。 运用remove(at:)办法和找到使命的索引从ModelData.shared.items数组中删除使命。 延迟 2 秒后,运用removeAll(where:) 和检查匹配 ID 的闭包从 ModelData.shared.items 数组中删除 itemToRemove。

最后,“Updated”被打印到控制台。 return .result():该语句回来一个IntentResult实例,表明intent的完结,没有任何具体的成果值。 总的来说,此代码界说了一个目的,用于履行切换清单中使命状况的操作。 它拜访 ModelData 的同享实例,以依据供给的 ID 查找和修改使命。

现在是时候用 AppIntents 替换图像了

Button(intent: MyActionIntent(id: item.id)) {
    Image(systemName: item.isAdded ? "checkmark.circle.fill":"circle")  
    .foregroundColor(.green)  
    }  
.buttonStyle(.plain)

交互小组件 — iOS 17

声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。