SwiftUI九创建watchOS应用
代码下载
这篇教程让可以应用之前所学到的SwiftUI知识,把Landmarks应用从iOS平台迁移到watchOS平台上。在拷贝可以共用的数据和视图文件之前,需要先给项目中添加一个对应watchOS的Target编译目标。在所有 assets 就绪后,将自定义SwiftUI视图在watchOS上显示详细信息和列表视图。
按照下面的步骤构建工程,或者下载完成后的项目文件学习,项目文件。
添加一个watchOS编译目标
要创建一个watchOS应用,第一步是给项目添加一个对应watchOS平台的编译目标。Xcode在新增Target的同时,添加一个文件组和相应的文件到工程中,同时还会新增编译运行方案,指定应用要运行的平台或模拟器。

1、选择菜单 File->New->Target。当模板列表出现后,在 watchOS 选项卡下选择 App
模板,并点击下一步(Next)。
这个模板将一个新的watchOS应用程序添加到项目中。
2、在模板创建表中,输入 WatchLandmarks 作为产品名称,设置语言为Swift,用户界面为SwiftUI实现方式,并勾选 Watch App for Existing iOS App并选择 Landmarks (我在这里选的是之前的 InterfacingWithUIKit,因为不是以上面的项目文件中的工程为基础开发的),最后点击完成(Finish)。

3、如果Xcode提示激活watchOS平台编译运行方案,点击激活(Activate)。这会把编译运行方案从iOS平台切换到watchOS平台上来,这能够开始处理新目标。
4、在watchOS模拟器下拉列表中选择Apple Watch Series 9 (45mm)设备。
5、选择 WatchLandmarks Watch App
Target 并导航到 Target 的 General 选项卡,下勾选 Supports Running Without iOS App Installation
复选框。
在 Target 间共享文件
现在watchOS平台的编译目标(Target)已经创建好,为了避免重复工作,使用watchOS目标集,可以复用一些之前在iOS项目中的资源。地标的数据模型文件可以复用,一些资源文件以及一些两个平台下不需要修改就可以展示的视图文件也可复用。

首先,删除watchOS应用程序的入口点。不需要它,因为将重用在 Landmarks (我在这里是用InterfacingWithUIKit) App中定义的入口点。
1、在项目导航器中,删除 WatchLandmarks Watch App 文件夹中 的 WatchLandmarksApp 文件;当出现提示时,选择 “Move to trash”。
接下来,选择watchOS目标可以与现有iOS目标共享的所有文件,包括应用程序的入口点。
2、在Project navigator中,Command-click选择以下文件:LandmarksApp(我这里是InterfacingWithUIKitApp), LandmarkList, LandmarkRow, CircleImage, MapView。
第一个是共享应用程序定义。其他是应用程序可以在watchOS上显示的视图,无需更改。
3、继续 Command-clicking 添加以下模型文件:ModelData、Landmark、Hike、Profile。
这些项定义了应用程序的数据模型。不会使用模型的所有方面,但是需要所有的文件来成功编译这个应用。
4、完成command-clicking,添加由模型加载的资源文件:landmarkData、hikeData和Assets。
5、在文件检查器中,选择 Target Membership
部分中的 WatchLandmarks Watch App
复选框。
这使得在前面步骤中选择的内容可用于 watchOS 应用程序。
最后,添加一个与已有的iOS应用图标相匹配的 watchOS 应用图标。
6、选择 WatchLandmarks Watch App 文件夹中的 Assets 文件,然后导航到空的AppIcon项。
7、将单个png文件从下载的项目的Resources文件夹中拖到现有的空AppIcon集中。
稍后,当创建通知时,系统会显示应用程序的图标,以帮助识别通知的来源。
创建详情视图
iOS编译目标下的资源可以在手表应用下使用,但需要创建一个专门适配手表尺寸的地标详情页来展示地标的具体信息。为了测试视图是否能适配手表展示,需要分别为最大尺寸和最小尺寸手表创建预览视图,并根据情况适当的调整圆形视图的布局来适应手表的界面大小。

1、添加一个新的自定义视图到 WatchLandmarks Watch App 文件夹,名为 LandmarkDetail.swift。
这个文件与iOS项目中的同名文件的区别在于它的目标成员——它只适用于WatchLandmarks Watch App目标。
2、将modelData、landmark和landmarkIndex属性添加到新的LandmarkDetail结构中,这些与在处理用户输入中添加的属性相同。
struct LandmarkDetail: View {@Environment(ModelData.self) var modelDatavar landmark: Landmarkvar landmarkIndex: Int {modelData.landmarks.firstIndex { $0.id == landmark.id } ?? 0}
}
3、在预览视图中,创建一个用户数据的实例,并给WatchLandmarkDetail结构体的初始化器中传入一个地标对象作为参数。这里需要把用户数据设置为视图的环境对象。
#Preview {let modelData = ModelData()return LandmarkDetail(landmark: modelData.landmarks[0]).environment(modelData)
}
4、设置设备选择器,使预览显示一个大手表。
5、从body()方法返回一个CircleImage视图。
这里重用了iOS项目中的CircleImage视图。因为创建了一个可调整大小的图像,所以调用scaledToFill()会调整圆圈的大小,使其填充显示器。
var body: some View {CircleImage(image: landmark.image.resizable()).scaledToFill()}
6、改变设备选择器,使预览显示一个小手表。
通过对最大和最小的表盘进行测试,可以看到应用程序是如何适应显示器的。一如既往,应该在所有支持的设备尺寸上测试用户界面。
7、在VStack中嵌入圆形图像。在图像下方显示地标名称及其信息。正如所看到的,信息不太适合手表屏幕,但可以通过将VStack放置在滚动视图中来解决这个问题。
8、把VStack整体嵌入到一个ScrollView中,这就让视图获取了滚动查看的能力,但同时也引入了另一个问题:CircleImage现在扩展到完全尺寸,把其它元素挤到没有地方显示。所以需要缩放CircleImage,让圆形图片和地标名称可以在一屏内同时显示出来。
9、将scaleToFill()更改为scaleToFit()并添加padding。这将缩放圆形图像以匹配显示器的宽度,并确保地标名称在圆形图像下方可见。
10、在分隔符后添加MapView。地图显示在屏幕外,但可以向下滚动查看。
11、为后退按钮添加标题。将后退按钮的文本设置为“Landmarks”。
var body: some View {@Bindable var modelData = modelDataScrollView {VStack {CircleImage(image: landmark.image.resizable()).scaledToFit()Text(landmark.name).font(.headline).lineLimit(0)Toggle(isOn: $modelData.landmarks[landmarkIndex].isFavorite, label: {Text("Favorite")})Divider()Text(landmark.park).font(.caption).bold().lineLimit(0)Text(landmark.state).font(.caption)Divider()MapView(coordinate: landmark.locationCoordinate).scaledToFit()}.padding(16)}.navigationTitle("Landmarks")}
添加地标列表
为iOS创建的LandmarkList也适用于手表应用,它会自动导航到刚刚为watchOS编译时创建的手表地标详情视图。接下来,将把列表连接到手表的ContentView,这样它就可以作为手表应用的顶层视图。

1、在 WatchLandmarks Watch App 文件夹中选择ContentView。
与LandmarkDetail一样,watchOS目标的内容视图与iOS目标的名称相同。保持名称和接口相同可以方便地在目标之间共享文件。
watchOS应用程序的根视图显示默认的 “Hello, World!” 消息。
2、修改ContentView,使其显示列表视图。
确保为预览提供模型数据作为环境。LandmarksApp(我这里是InterfacingWithUIKitApp)已经在运行时的应用级别提供了这个,就像它在iOS中所做的那样,但还必须为任何需要它的预览提供它。
struct ContentView: View {var body: some View {LandmarkList()}
}#Preview {ContentView().environment(ModelData())
}
3、确保在实时预览检查应用程序的行为。
创建自定义通知界面
watchOS平台的Landmarks应用已经接近完成了。在最后一节中,会创建一个通知界面,当用户的地理位置靠近自己收藏过的地标位置时会收到通知提示用户,通知界面展示当前正在接近的地标相关信息。
提示:本节只讲当用户收到通知时怎样显示通知界面,不涉及怎样设置和发送通知给用户的内容。

1、添加一个新的自定义视图到 WatchLandmarks Watch App 文件夹 NotificationView.swift,并创建一个视图来显示有关地标、标题和消息的信息。
struct NotificationView: View {var title: String?var message: String?var landmark: Landmark?var body: some View {VStack {if let landmark {CircleImage(image: landmark.image.resizable()).scaledToFit()}Text(title ?? "Unknown Landmark").font(.headline)Divider()Text(message ?? "You are within 5 miles of one of your favorite landmarks.").font(.caption)}}
}
2、添加一个预览,用于设置通知视图的标题、消息和地标属性。
这将显示提供数据时通知视图的预览。因为任何通知值都可以为nil,所以在没有提供数据时保持通知视图的默认预览非常有用。
#Preview {NotificationView(title: "Turtle Rock", message: "You are within 5 miles of Turtle Rock.", landmark: ModelData().landmarks[0])
}
3、创建一个新的Swift文件NotificationController.swift,并添加一个包含landmark、title和message属性的托管控制器结构。
这些属性存储有关传入通知的值。
import WatchKit
import SwiftUI
import UserNotificationsclass NotificationController: WKUserNotificationHostingController<NotificationView> {var landmark: Landmark?var title: String?var message: String?override var body: NotificationView {NotificationView()}
}
4、更新body()方法来使用这些属性。此方法实例化前面创建的通知视图。
override var body: NotificationView {NotificationView(title: title,message: message,landmark: landmark)}
5、定义landmarkIndexKey。可以使用此键从通知中提取地标索引。
let landmarkIndexKey = "landmarkIndex"
6、添加 didReceive(_:)
方法来解析来自通知的数据。
这个方法更新控制器的属性。调用此方法后,系统将使控制器的body属性失效,从而更新通知视图。然后,系统将通知显示在Apple Watch上。
override func didReceive(_ notification: UNNotification) {let modelData = ModelData()let notificationData =notification.request.content.userInfo as? [String: Any]let aps = notificationData?["aps"] as? [String: Any]let alert = aps?["alert"] as? [String: Any]title = alert?["title"] as? Stringmessage = alert?["body"] as? Stringif let index = notificationData?[landmarkIndexKey] as? Int {landmark = modelData.landmarks[index]}}
当Apple Watch收到通知时,它会在你的应用程序中寻找与该通知类别相关的场景。
7、转到LandmarksApp(我这里是InterfacingWithUIKitApp)并使用LandmarkNear类别添加一个WKNotificationScene。
这个场景只对watchOS有意义,所以添加条件编译。
#if os(watchOS)WKNotificationScene(controller: NotificationController.self, category: "LandmarkNear")#endif
在应用程序可以显示通知之前,需要请求权限。
8、转到ContentView并请求授权以启用通知中心的通知。
使用异步任务修改器发出请求,SwiftUI在内容视图首次出现时调用该修改器。
struct ContentView: View {var body: some View {LandmarkList().task {let center = UNUserNotificationCenter.current()_ = try? await center.requestAuthorization(options: [.alert, .sound, .badge])}}
}
使用LandmarkNear类别配置测试数据(payload),并传递通知控制器期望的数据。
9、添加一个新的通知模拟文件到 WatchLandmarks Watch App 文件夹,名为 PushNotificationPayload.apns。
不要将 PushNotificationPayload 文件添加到任何目标,因为它不是应用的一部分。
10、更新title, body, category, landmarkIndex和Simulator Target Bundle属性。请确保将“类别”设置为“LandmarkNear”。还可以删除教程中不使用的任何键,例如副标题、WatchKit Simulator Actions和customKey。
{"aps": {"alert": {"title": "Silver Salmon Creek","body": "You are within 5 miles of Silver Salmon Creek."},"category": "LandmarkNear","thread-id": "5280"},"landmarkIndex": 1,"Simulator Target Bundle": "com.example.apple-samplecode.Landmarks.watchkitapp"
}
注意:Simulator Target Bundle 一定要填写这个watchOS应用的 Bundle id。
Payload文件是用来模拟从服务端发送的远程推送的数据。
11、在模拟器上构建并运行WatchLandmarks Watch App方案。第一次运行这款应用时,系统会请求发送通知的许可。选择允许。
12、在授予权限后,使用Xcode停止应用程序,然后将 PushNotificationPayload 文件拖到表盘上。
模拟器显示一个可滚动的通知,其中包括:应用程序的图标以帮助识别地标应用程序作为发送者,通知视图和通知操作的按钮。
相关文章:

SwiftUI九创建watchOS应用
代码下载 这篇教程让可以应用之前所学到的SwiftUI知识,把Landmarks应用从iOS平台迁移到watchOS平台上。在拷贝可以共用的数据和视图文件之前,需要先给项目中添加一个对应watchOS的Target编译目标。在所有 assets 就绪后,将自定义SwiftUI视图…...

【Linux进阶】文件和目录的默认权限与隐藏权限
1.文件默认权限:umask OK,那么现在我们知道如何建立或是改变一个目录或文件的属性了,不过,你知道当你建立一个新的文件或目录时,它的默认权限会是什么吗? 呵呵,那就与umask这个玩意儿有关了&…...
SQL 查询中 (+) 符号的含义
您已经很好地解释了 SQL 查询中 () 符号的含义,它确实用于表示左外连接(LEFT OUTER JOIN),这是 SQL 中的一种连接操作。以下是对您提供的信息的补充和完善: ### 左外连接(LEFT OUTER JOIN)&…...

基于Vue的MOBA类游戏攻略分享平台
你好呀,我是计算机学姐码农小野!如果有相关需求,可以私信联系我。 开发语言:Java 数据库:MySQL 技术:Java技术、SpringBoot框架、B/S模式、Vue.js 工具:MyEclipse、MySQL 系统展示 首页 用…...
如何在PhpStorm中运行SQL文件?
如何在PhpStorm中运行SQL文件? 提问:如何在PhpStorm中运行SQL文件? 解答:本文将详细介绍如何在PhpStorm中运行SQL文件的步骤,包括如何配置数据库连接和执行SQL脚本,并附带示例SQL代码。 1. 配置数据库连…...

Qt实现检测软件是否多开
Qt实现检测软件是否多开 在桌面软件开发中,软件通常要设置只允许存在一个进程,像一些熟知的音乐软件,QQ音乐这种。而这些软件在限制只有一个进程的同时,通常还会有双击桌面图标唤醒已运行的后台进程的功能。关于双击桌面唤醒已运…...
spring security + vue,登录功能
前端代码:https://gitee.com/forgot940629/vuelogin 后端代码:https://gitee.com/forgot940629/springbootloginv2 参考代码:https://github.com/PuZhiweizuishuai/SpringSecurity-JWT-Vue-Deom 基于前后端分离实现登录功能。登录成功后后端向…...

64.函数参数和指针变量
目录 一.函数参数 二.函数参数和指针变量 三.视频教程 一.函数参数 函数定义格式: 类型名 函数名(函数参数1,函数参数2...) {代码段 } 如: int sum(int x,int y) {return xy; } 函数参数的类型可以是普通类型,也可以是指针类…...

原创作品 —(金融行业)年金系统交互和视觉设计
金融行业软件交互设计要点:“简化操作流程,确保流畅易用,同时注重交易环境的安全可靠,通过个性化体验提升用户满意度,并及时收集反馈以持续优化。” 2.UI设计要点:“注重视觉效果与用户体验的平衡ÿ…...

3D slicer
...
面试题--SpringCloud
SpringCloud SOA 和微服务的区别?(必会) 谈到 SOA 和微服务的区别, 那咱们先谈谈架构的演变 1. 集中式架构 项目功能简单, 一个项目只需一个应用, 将所有功能部署在一起, 这样的架构好处是减 少了部署节点和成本. 缺点: 代码耦合,开发维护困难 2. 垂直拆分架构 …...
Qt windeployqt 打包的Qt动态库介绍
前言 在 Windows 平台,通常使用 Qt 的工具 windeployqt 将可执行文件的相关 Qt 依赖项部署到当前路径下。 Windows windeployqt 部署工具 Windows 部署工具 windeployqt 旨在自动化创建一个可部署文件夹的过程,该文件夹包含运行应用程序所需的 Qt 相关…...

WordPress付费进群V2主题,多种引流方法,引私域二次变现
全新前端UI界面,多种前端交互特效让页面不再单调,进群页面群成员数,群成员头像名称,每次刷新页面随机更新不重复,最下面评论和点赞也是如此随机刷新不重复 进群页面简介,群聊名称,群内展示&…...
【Linux】性能分析器 perf 详解(三):kmem、mem
上一篇:【Linux】性能分析器 perf 详解(二) 1、perf kmem 1.1 简介 kmem 用于跟踪/测量内核内存属性的工具,如分配、释放、碎片率等。可以帮助开发者和系统管理员理解内核内存的分配和释放模式,从而诊断内存泄漏、过度分配等问题。 功能: 跟踪内存分配和释放:监控内…...
微信小程序怎样跳转页面?
在微信小程序中,页面跳转通常使用 wx.navigateTo、wx.redirectTo、wx.switchTab、wx.navigateBack 以及 wx.reLaunch 等API。这些API提供了不同的页面跳转方式,适用于不同的场景。 以下是这些API的详细代码示例和说明: 1.wx.navigateTo&…...

针对SVN、GIT版本管理工具进行源代码加密保护
针对SVN、GIT版本管理工具进行源代码加密保护 在软件开发过程中,版本管理工具如SVN和GIT是不可或缺的组成部分,它们帮助团队管理源代码的变更和版本。然而,这些工具也面临着源代码泄露的安全风险。如果不针对数据进行加密保护,很…...
js条件引用
在JavaScript中,条件引用模块通常涉及到在运行时根据条件判断来决定是否加载某个模块。这在某些情况下非常有用,比如在不同环境下使用不同的模块,或者在某些特定条件下才需要某个模块的功能。 以下是几种在JavaScript中实现条件引用的方法&a…...

帝国CMS(EmpireCMS)漏洞复现
简介 《帝国网站管理系统》英文译为Empire CMS,简称Ecms,它是基于B/S结构,且功能强大而帝国CMS-logo易用的网站管理系统。 帝国CMS官网:http://www.phome.net/ 参考相关漏洞分析文章,加上更详细的渗透测试过程。 参考…...

DP:解决路径问题
文章目录 二维DP模型如何解决路径问题有关路径问题的几个问题1.不同路径2.不同路径Ⅱ3.下降路径最小和4.珠宝的最高价值5.地下城游戏 总结 二维DP模型 二维动态规划(DP)模型是一种通过引入两个维度的状态和转移方程来解决复杂问题的技术。它在许多优化和…...

Halcon OCR字符识别(极坐标转换,字符识别)
Halcon OCR字符识别(极坐标转换,字符识别) 代码 * 1.加载图片 *************************************************** dev_close_window () read_image (Image, ./img) get_image_size (Image, Width, Height) dev_get_window (WindowHandle…...

智能在线客服平台:数字化时代企业连接用户的 AI 中枢
随着互联网技术的飞速发展,消费者期望能够随时随地与企业进行交流。在线客服平台作为连接企业与客户的重要桥梁,不仅优化了客户体验,还提升了企业的服务效率和市场竞争力。本文将探讨在线客服平台的重要性、技术进展、实际应用,并…...
【Web 进阶篇】优雅的接口设计:统一响应、全局异常处理与参数校验
系列回顾: 在上一篇中,我们成功地为应用集成了数据库,并使用 Spring Data JPA 实现了基本的 CRUD API。我们的应用现在能“记忆”数据了!但是,如果你仔细审视那些 API,会发现它们还很“粗糙”:有…...

c#开发AI模型对话
AI模型 前面已经介绍了一般AI模型本地部署,直接调用现成的模型数据。这里主要讲述讲接口集成到我们自己的程序中使用方式。 微软提供了ML.NET来开发和使用AI模型,但是目前国内可能使用不多,至少实践例子很少看见。开发训练模型就不介绍了&am…...
rnn判断string中第一次出现a的下标
# coding:utf8 import torch import torch.nn as nn import numpy as np import random import json""" 基于pytorch的网络编写 实现一个RNN网络完成多分类任务 判断字符 a 第一次出现在字符串中的位置 """class TorchModel(nn.Module):def __in…...

CVE-2020-17519源码分析与漏洞复现(Flink 任意文件读取)
漏洞概览 漏洞名称:Apache Flink REST API 任意文件读取漏洞CVE编号:CVE-2020-17519CVSS评分:7.5影响版本:Apache Flink 1.11.0、1.11.1、1.11.2修复版本:≥ 1.11.3 或 ≥ 1.12.0漏洞类型:路径遍历&#x…...
SQL慢可能是触发了ring buffer
简介 最近在进行 postgresql 性能排查的时候,发现 PG 在某一个时间并行执行的 SQL 变得特别慢。最后通过监控监观察到并行发起得时间 buffers_alloc 就急速上升,且低水位伴随在整个慢 SQL,一直是 buferIO 的等待事件,此时也没有其他会话的争抢。SQL 虽然不是高效 SQL ,但…...

Golang——9、反射和文件操作
反射和文件操作 1、反射1.1、reflect.TypeOf()获取任意值的类型对象1.2、reflect.ValueOf()1.3、结构体反射 2、文件操作2.1、os.Open()打开文件2.2、方式一:使用Read()读取文件2.3、方式二:bufio读取文件2.4、方式三:os.ReadFile读取2.5、写…...

2.3 物理层设备
在这个视频中,我们要学习工作在物理层的两种网络设备,分别是中继器和集线器。首先来看中继器。在计算机网络中两个节点之间,需要通过物理传输媒体或者说物理传输介质进行连接。像同轴电缆、双绞线就是典型的传输介质,假设A节点要给…...

2025年- H71-Lc179--39.组合总和(回溯,组合)--Java版
1.题目描述 2.思路 当前的元素可以重复使用。 (1)确定回溯算法函数的参数和返回值(一般是void类型) (2)因为是用递归实现的,所以我们要确定终止条件 (3)单层搜索逻辑 二…...

Tauri2学习笔记
教程地址:https://www.bilibili.com/video/BV1Ca411N7mF?spm_id_from333.788.player.switch&vd_source707ec8983cc32e6e065d5496a7f79ee6 官方指引:https://tauri.app/zh-cn/start/ 目前Tauri2的教程视频不多,我按照Tauri1的教程来学习&…...