在本节,咱们将实现一个简单的杂乱功用。

新建项目

新建项目,RandomAddress,字如其名,咱们后续或许将在 App 中获取随机地址:

「Apple Watch 应用开发系列」复杂功能实践

「Apple Watch 应用开发系列」复杂功能实践

Complication DataSource

在项目中新增文件 ComplicationController.swift

import ClockKit
class ComplicationController: NSObject, CLKComplicationDataSource {
    func currentTimelineEntry(
        for complication: CLKComplication
    ) async -> CLKComplicationTimelineEntry? {
        return nil
    }
}

在 Xcode 14 之前的版别,当咱们创立一个 watchOS 项目时,Xcode 会自动生成 ComplicationController.swift。而 Xcode14 后,咱们需求手动进行创立。假如用的是 Xcode 14 之前的版别,咱们会发现在上述代码中,删除了除 CLKComplicationDataSource 所需的一种办法之外的所有内容。大多数样板代码都是不必要的,这儿为了简单起见,咱们不进行保留。

当时 Timeline entry

当 watchOS 想要更新杂乱功用显现的数据时,它会调用 currentTimelineEntry(for:)。咱们应该立即回来要显现的数据或回来 nil。

假如咱们无法供给当时时刻的数据,watchOS 将在 App 的扩展程序的 Assets.xcassets 中查找。假如你运用 Xcode 14 前的版别,或许现已留意到 Assets.xcassets 中有一个之前没有运用过的 Complication 文件夹。

「Apple Watch 应用开发系列」复杂功能实践

currentTimelineEntry(for:) 回来 nil 时,watchOS 将运用Assets.xcassets 中存在的恰当命名的图画。

模拟器的默许表盘是 Meridian,它运用 .graphicCircular 杂乱功用系列来处理大多数可装备的杂乱功用。关于你第一次测验在 App 中支撑杂乱功用,请将办法主体替换为:

func currentTimelineEntry(
    for complication: CLKComplication
) async -> CLKComplicationTimelineEntry? {
    guard complication.family == .circularSmall else {
      return nil
    }
    let template = CLKComplicationTemplateGraphicCircularStackText(
      line1TextProvider: .init(format: "line1"),
      line2TextProvider: .init(format: "line2"))
    return .init(date: Date(), complicationTemplate: template)
}

在上述代码中:

  1. 假如是不支撑的杂乱功用系列类型,则回来 nil。

  2. 创立恰当类型的杂乱功用模板并装备要显现的文本。

  3. 回来一个 CLKComplicationTimelineEntry,它指定数据的时刻和要显现的模板。指定的日期不该该是将来,但它可所以曩昔。

在第二步中,请留意模板选用里两个文本。每个模板运用不同的文本或图形元素,请查阅各种模板的文档以确认哪些合适咱们的。

装备杂乱功用

假如你运用的是 Xcode 14 及以上版别,需求手动装备 Scheme。咱们新建一个 Schema,并修改对应的姓名:

「Apple Watch 应用开发系列」复杂功能实践

「Apple Watch 应用开发系列」复杂功能实践

接着修改咱们的 Scheme,将 Interface 修改为 Completion:

「Apple Watch 应用开发系列」复杂功能实践

打开项目的 Info,新增 ClockKit Complication - Principal Class Key,并将 Value 设置为 $(PRODUCT\_MODULE\_NAME).ComplicationController

「Apple Watch 应用开发系列」复杂功能实践

「Apple Watch 应用开发系列」复杂功能实践

展现杂乱功用

将 Scheme 换到 Complication,然后再次构建并运转。运用该 Scheme 可保证在不运用缓存的情况下运用 App 支撑的系列。该 Scheme 还将模拟器直接发动到表盘,并为咱们的 App 供给少数后台处理时刻。

找一个支撑咱们刚刚杂乱功用代码的表盘,长按表盘,修改器将出现,向左滑动两次,以便挑选要替换的杂乱功用:

「Apple Watch 应用开发系列」复杂功能实践

「Apple Watch 应用开发系列」复杂功能实践

「Apple Watch 应用开发系列」复杂功能实践

「Apple Watch 应用开发系列」复杂功能实践

点按咱们期望替换的圆形杂乱功用,查看能够挑选的杂乱功用列表:

「Apple Watch 应用开发系列」复杂功能实践

翻滚直到咱们看到咱们自己的 App,挑选咱们刚刚创立的闪亮的新杂乱功用。可实际上没有看到咱们的 App。 不好了! 什么地方出了错?

CLKComplicationDataSource 有一个名为 complicationDescriptors() 的可选办法。 这其实不是“可选的”。 假如咱们不供给该办法,咱们将不会看到列出的杂乱功用。

早起版别的 watchOS 在 Info.plist 中查找支撑的杂乱功用。这就是为什么该办法是可选的。 根据 Apple 的建议,不要再运用 Info.plist。

在 ComplicationController.swift 里,持续增加以下代码:

func complicationDescriptors() async -> [CLKComplicationDescriptor] {
    return [
        .init(identifier: "layer.practice.RandomAddress",
              displayName: "RandomAddress",
              supportedFamilies: [.graphicCircular])
    ]
}

在上述代码中:

  1. 咱们供给一个 CLKComplicationDescriptor 项数组作为此办法的回来值。 每个 CLKComplicationDescriptor 都出现在可供挑选的杂乱功用列表中。

  2. 咱们支撑的每个杂乱功用功用都应该有一个唯一的名称。 保证名称是确认性的,而且不会在运用程序发动之间发生变化。

  3. displayName 是用户从运用支撑的列表中挑选杂乱功用时看到的内容。

  4. 杂乱功用供给了他们支撑的系列。

再次构建并运转。 这一次,当咱们翻滚杂乱功用时,会看到咱们的杂乱功用被列为可供挑选的选项。

「Apple Watch 应用开发系列」复杂功能实践

圈子里的“–”是怎么回事? 为什么它没有显现咱们在时刻轴中指定的音讯?

样本数据

currentTimelineEntry 既不会显现在此列表中,也不会显现在 iPhone 上的 Watch App 中。 当问询 currentTimelineEntry 时,咱们的 App 或许有必要履行昂贵的操作或异步运转某些东西。运用这儿的数据作为样本数据是不妥的。

咱们将运用一组样本数据来使显现,并防止履行其他代码,带来 App 中的潜在副作用。 CLKComplicationDataSource 供给了另一个可选的——名为 localizableSampleTemplate(for:) 的办法,当 Apple Watch 需求在列表中显现杂乱挑选器时 watchOS 调用该办法。

持续增加以下代码:

func localizableSampleTemplate(
    for complication: CLKComplication
) async -> CLKComplicationTemplate? {
    guard complication.family == .graphicCircular,
          let image = UIImage(systemName: "xmark")?.withTintColor(.white)
    else { return nil }
    return CLKComplicationTemplateGraphicCircularStackImage(
        line1ImageProvider: .init(fullColorImage: image),
        line2TextProvider: .init(format: "hhh"))
}

在该办法中:

  1. 它确系列是咱们支撑的系列。 同时保证你能够加载默许图画以显现在杂乱功用预览中。

  2. 供给 CLKComplicationTemplateGraphicCircularStackImage 作为样本数据。

再次构建并运转。 这一次,当咱们测验挑选杂乱功用时,咱们会看到更好的显现。接着击该行以挑选杂乱功用,然后回来 Apple Watch 的主屏幕。咱们却没有看到咱们的杂乱功用显现:

「Apple Watch 应用开发系列」复杂功能实践

「Apple Watch 应用开发系列」复杂功能实践

更新杂乱功用的数据

Apple Watch 只会在咱们指定新数据可用时测验更新表盘上的杂乱功用。 想象一下,假如 watchOS 有必要每秒查询 App 的杂乱功用以查看是否有新数据点可用,会耗费多少电量?

告知 watchOS 有新数据

新增文件 Model.swift,他将获取随机的城市:

import SwiftUI
struct Address: Decodable {
    var city: String
}
class Model {
    static let shared = Model()
    var address: Address?
    func getAddress() async {
        let (data, _) = try! await URLSession.shared.data(from: URL(string: "https://random-data-api.com/api/v2/addresses")!)
        address = try! JSONDecoder().decode(Address.self, from: data)
        print(address!.city)
    }
}

能够测验在 ContentView 展现时,拉取该信息:

struct ContentView: View {
    var body: some View {
        VStack {
            Image(systemName: "globe")
                .imageScale(.large)
                .foregroundColor(.accentColor)
            Text("Hello, world!")
        }
        .padding()
        .task {
            await Model.shared.getAddress()
        }
    }
}

切换 Scheme 运转项目,咱们会看到控制台有城市信息输出:

「Apple Watch 应用开发系列」复杂功能实践

回到 Model.swift:

import ClockKit

修改 Model:

class Model {
    static let shared = Model()
    var address: Address?
    func getAddress() async {
        let (data, _) = try! await URLSession.shared.data(from: URL(string: "https://random-data-api.com/api/v2/addresses")!)
        address = try! JSONDecoder().decode(Address.self, from: data)
        print(address!.city)
        DispatchQueue.main.async {
            let server = CLKComplicationServer.sharedInstance()
            server.activeComplications?.forEach {
                server.reloadTimeline(for: $0)
            }
        }
    }
}

一旦咱们获取到数据,咱们告知 watchOS 它需求从头加载时刻线以处理当时表盘上的任何杂乱功用。

根据咱们的 App 及其数据模型,从头加载整个时刻线或许不是最有用的挑选。假如咱们的杂乱功用的时刻线中的现有数据依然有用,而且咱们仅仅增加新数据,则应改为调用 extendTimeline(for:)

留意:假如咱们现已超出了运用程序的预算的履行时刻,那么对任一办法的调用都不会履行任何操作。

为杂乱功用供给数据

切换回 ComplicationController.swift 并将 currentTimelineEntry(for:) 的主体替换为:

func currentTimelineEntry(
    for complication: CLKComplication
) async -> CLKComplicationTimelineEntry? {
    guard complication.family == .graphicCircular,
          let address = Model.shared.address else {
        return nil
    }
    let template = CLKComplicationTemplateGraphicCircularStackImage(
        line1ImageProvider: .init(fullColorImage: UIImage(systemName: "xmark")!.withTintColor(.white)),
        line2TextProvider: .init(format: address.city))
    return .init(date: Date(), complicationTemplate: template)
}

在上述代码中,咱们获取了 Model 单例里的 Address 并进行更新。

再次构建并运转。 请稍等片刻,从网络下载数据,然后切换回表盘。 咱们将看到现在显现的实在数据:

「Apple Watch 应用开发系列」复杂功能实践

支撑多个系列

尽管咱们现在具有一个支撑杂乱功用的 App,但它的功用十分有限。为了让用户运用咱们的杂乱功用,他们有必要运用支撑 .graphicCircular 的表盘。每当咱们为 Apple Watch 规划杂乱功用时,咱们都应该努力支撑各种类型的系列。

回想一下,当咱们在 complicationDescriptors() 中生成CLKComplicationDescriptor 时,咱们为 supportedFamilies 参数指定了一个系列。尽管咱们需敲几下键就能够增加其余类型,甚至只需指定 CLKComplicationFamily.allCases,单咱们依然有必要处理每个不同的模板类型。

咱们在网上看到的大多数资源都告知咱们只需在每种办法中针对系列创立一个 switch 句子来确认要采取的操作。尽管咱们能够这样做,但控制器将变得十分臃肿而且难以保护。有一种常见的规划形式,称为工厂办法,在这儿,它的效果很好,欢迎测验实现。

链接

  • 你能够在这儿取得文章项目:github.com/LLLLLayer/A…

  • 运用 random-data-api.com/ 供给的 Fake 数据。