从一月中旬以来我全身心投入到 WhereNotes App 的Apple Watch部分的工作中。我也有幸被邀请到在Cupertino 的Apple Watch 实验室。在过去的三个半月中,我搜集了很多技巧,并在这篇文章中涵盖了它们中的大部分。我希望这些技巧能够对你的Apple Watch开发有所帮助。
也许你对于这些如applicationWillEnterForeground:和applicationDidEnterBackground:方法非常熟悉。它们在与之相对应的通知(UIApplicationWillEnterForegroundNotification和UIApplicationDidEnterBackgroundNotification)前被调用。与之相等价(鲜有人知)的WatchKit NSExtensionContext通知如下:
NSExtensionHostWillEnterForegroundNotification?
NSExtensionHostDidEnterBackgroundNotification?
NSExtensionHostWillResignActiveNotification?
NSExtensionHostDidBecomeActiveNotification
我与大多数苹果开发者社区中的成员都有这样的经验,用一个在充电器上的Watch测试会获得更好更可靠的调试经验。
在iOS上可以随时更新界面元素,但是在WatchKit上,只能更新当前激活的、可视的视图控制器中的元素。直到didDeactivate方法被调用时,更新才能被安全的执行。(注意你不能在此方法中更新界面元素)这意味着如果你打算更新一个当前隐藏的视图控制器(例如你正在查看最顶部的模态控制器),你会需要执行当前控制器的willActivate方法,这个方法会在模态控制器消失的时候被调用。
除包含在你的Watch app bundle中的assets之外,每个app 只允许5MB的高速图片缓存,通过extension使用 WKInterfaceDevice 中的方法引入和管理图片缓存。从extension发送图片到watch端需要消耗时间和电池电量,所以需要重用图片(即使只是用一次),这都是值得缓存的。如果使用 addCachedImage:name: 发送一张图片,那么这张图片会自动的被以PNG格式编码,并发送到缓存中。不论PNG是否是最佳的格式(但这是最安全的格式)。如果你的图片能以JPG的格式呈现,我强烈地推荐使用addCachedImageWithData:name: 来取代上述方法。以JPG格式编码图片和试用图片质量设置,不仅图片将会更快速地传输,同时也会拥有更多的缓存空间用于储存更多的图片。
依据之前的建议,你可以在后台进程缓存图片(依据一个在开发者社区中的苹果雇员所述)。我在我的Watch app中,使用提前缓存图片这项技术。
如果你使用前边提及到的图片缓存,这里没有内置方法决定删除使用过最旧的图片。如果你的app管理了许多图片,你会想要封装关于缓存的manager。
为了测试Apple Watch上的通知,在Apple Watch配对的app的通用中设置关闭手腕检测。
想要强制退出app,按住旁边的按钮,一段时间后再次按住它(注意强制退出你的app不会强制退出你的extension)。
在willActivate中最小化处理的工作,以减少加载等待时间。
考虑用户在使用iPhone app之前打开你的Watch app以及设计的一致性。App审核会发现这些情况。
记住你的Watch app 是以extension的方式运行。你的Watch app的内存限制要比iPhone app要更为严格。如果处理大量的图片,在iPhone app上完成此项工作会更好(使用openParentApplication:reply:)。还得注意模拟器不会执行这些内存限制,所以必须在真实地设备上进行测试。
为了发现你的app是否与Watch匹配,为Watch app共享的NSUserDefaults(使用 shared app group)设置BOOL值,你的iPhone app能够获取它。
为了同步在iPhone与Watch之间的数据,你可以调用你的iPhone app执行所有的数据更新(使用 openParentApplication:reply:),或使用 Darwin notifications 在extension和iPhone app之间发送事件。Darwin notifications不支持数据装载,所以如果你想通过通知传输数据,可以查看非常有用的 MMWormhole 工程。
你除了使用timer更新和刷新界面元素,还可以使用KVO,如果你的数据源支持。这就是我在我的Watch app中所使用的方法。使用这种方法,界面元素只会在它们改变的时候被更新,同时降低通讯消耗和节省电池电量。
如果你需要跟踪控制器界面,在 awakeWithContext: 中考虑给self传输引用建立关系。我已经在我的app中通过我的 JBInterfaceController subclass 大量地使用这种模式。使用类似这类的技术让你如使用代理模式一样去工作。同时,更多以类似UIViewController方式考虑你的controller。
WatchKit extension是前台的extension,所以如果你需要获得 Core Location 的授权允许,你只需要在授权需要时请求。
除非你的场景需要,必须谨慎考虑你是否需要“在线更新”,即在Watch和iPhone之间立即同步。用户通常不会同时使用两个设备,所以在下一次Watch或iPhone app激活时简单地更新数据就能避免大量的同步逻辑。不幸的是看着watch与iPhone的模拟器的屏幕紧邻彼此,就很有可能去建立复杂的同步逻辑。也许我做了,但我没有告诉你。
当你不能用程序创建和控制视图控制器时,你能明智地了解你是如何隐藏和显示视图元素。这会成为一个通用地WatchKit实践,例如,建立一个全页的label,如果有一则重要的消息需要展示时它可以被显示。或者,如果你有两个你需要编程选择的布局,可以包含它们到最上层的组,并在需要的时候让其隐藏和显示。
记住每个屏幕的触摸和界面更新需要Watch和iPhone之间来回的通讯。
WatchKit界面元素是只写的(它们通常也有setter方法),这需要手动跟踪你已经设置后你不想再次设置的值。WatchKit尝试在每次run loop合并值以及只发送最后的值,但你也能参与其中跟踪你自己的值。
然而这里没有内建的活动指示控件,当进行一项长时间的处理时(如图片传输或下载),你可以显示一系列的动态图片。更新于5/3/2015:我刚在GitHub发布了JBWatchActivityIndicator 工程,它使得创建活动指示图片序列更加容易。它也包含了一些苹果风格的预先渲染的序列。
确认下载和查看Apple Watch Design Resouces。除了有用的颜色和尺寸的推荐外,也包含了用于市场截图的高质量图片。正当我说着这个话题时,你提交的app截图不包含bezel 图片是没有价值的。
许多开发者对于模拟器上图片展示正确,但在真实的watch上却不一致的情况表示沮丧。事实上这是许多App被拒绝的原因。这个问题的出现与文件命名和文件丢失有关。最安全的解决办法是在Watch app中(不是extension)的assets library 中包含所有的图片。这就是我在我的Watch app中所作的。我推荐你也使用同样的方法。
虽然能从Watch加载你的iPhone app到前台是一个非常常见的需求,但通过编程无法实现(即使有方法可以在模拟器上运行),考虑用 Handoff 取代。
如果你需要在两个视图控制器间传递信息,但你不能通过 awakeWithContext: 完成,考虑使用发送 NSNotifications。在extension中它们运行良好。否则远程指定克隆我的JBInterfaceController subclass,并使用代理模式。
本地通知要求设置 soundName 属性以生成触觉反馈和铃声。
模拟器是好的开始,但在真实硬件设备上测试你的app才是关键。