当前位置: 首页 > 科技观察

AppleWatchKit抢先看

时间:2023-03-12 13:57:31 科技观察

随着苹果今天凌晨发布了最新版本的WatchKitAPI,对于开发者来说,关于这款新设备的一些更详细的信息也逐渐浮出水面。可以说,WatchKit最新版本的开放功能总体上是令人满意的。Apple继续履行其逐步开放的承诺。原本WWDC之后,期待TodayWidget会成为各种新奇应用的舞台,iOS功能的一次大扩展,但随着Launcher、PCalc等富有创意的TodayWidget的陆续下架,开发者不得不将对WatchKit的期待降级.但至少从目前的信息来看,WatchKit允许复杂的交互,完成一些独立的功能。虽然需要依赖iPhoneapp,但至少可以发挥的舞台和空间比我原先想象的要大很多。当然,由于设备本身的限制,无论是功率还是计算能力,Watchapp的开发仍然存在诸多制约因素。现在Watch应用仅作为视图显示存在并返回用户交互,但考虑到这是该设备的最新版本SDK,而且苹果也承诺允许实际运行在Watch上的应用出现,AppleWatchWatchKit的未来还是值得期待的。废话不多说,下面简单了解一下WatchKit的一些基本信息。WhatwecandoWatchapp架构首先需要明确的是,在iOS系统上,app本体是核心。所有的运行实体都是基于本体的:在iOS8之前,这一点是毋庸置疑的,iOS8中添加的各种扩展也必须和应用本体一起捆绑¥,作为应用功能的补充。虽然Watchapp和这个类似,但是如果我们要为AppleWatch开发,我们首先需要创建一个传统的iOSapp,然后将WatchApptarget添加到里面。添加之后,你会发现项目中多了两个target:一个是WatchKit的扩展,一个是WatchApp。在项目的对应组下,可以看到WatchKitExtension包含代码(InterfaceController.h/m等),而WatchApp只包含Interface.storyboard。但目前的好消息是,Apple并未明确要求为Watch开发的应用程序仍必须像iPhoneExtension一样基于iOS应用程序。换句话说,它可能会被允许剥离iOS应用程序,专注于提供Watch的UI和体验。应用安装时,负责逻辑部分的WatchKitExtension会和iOS应用的主目标一起安装到iPhone上,负责界面部分的WatchKitApp会在主程序运行后被iPhone检测到安装。提示安装到AppleWatch。所以在实际使用中,所有的计算、逻辑和控制其实都是在iPhone中完成的。当需要刷新界面时,iPhone向Watch发送指令,将其绘制并显示在表盘上。反过来,用户触摸手表进行交互时的信息也会从手表传回iPhone进行处理。而这个过程WatchKit会在幕后为我们完成,开发者无需操心。我们需要知道的是,原则上应该把接口相关的内容放在WatchApp的target中,把所有的代码逻辑放在Extension中。当你点击手表上的app图标运行WatchApp时,手表会负责唤醒手机上的WatchKitExtension。WatchKitExtension与iOSapp的数据交互需求由AppGroups完成,与TodayWidget等一些Extension一样。如果你还没有读过它,你可以参考我之前在TodayExtension上写的教程。主类WKInterfaceController和生命周期WKInterfaceController与WatchKit中的UIViewController一样,也会是WatchApp开发耗时最多的类。每个WKInterfaceController或其子类都应对应手表上的全屏内容。但是你需要记住,整个WatchKit是独立于UIKit而存在的。WKInterfaceController是一个直接继承自NSObject的类,它没有像UIKit中的UIResponser那样对用户交互的响应函数和完整的回调。不仅在功能上比UIViewController简单很多,而且生命周期也大大简化。每个WKInterfaceController对象都必须调用三个生命周期方法,即-initWithContext:对象初始化时,-willActivate将被呈现时,-didDeactivate呈现结束后。与UIViewController类似,可以理解为分别对应-viewDidLoad、viewWillAppear:和-viewDidDisappear:。虽然看方法名和实际使用,你可能会认为-initWithContext:应该对应UIViewController的init或initWithCoder:方法,但实际上WKInterfaceController中的“视图元素”是在-initWithContext:中(请注意,我这里加了引号,因为不是真正的view,后面会解释)已经初始化可用了,其实更类似于-viewDidLoad中的行为。我们一般在-initWithContext:和-willActivate中配置“视图元素”的属性,在-didDeactivate中去激活像NSTimer这样会holdself的对象。需要注意的是,在-didDeactivate中设置“视图元素”属性是无效的,因为当前的WKInterfaceController已经处于非活动状态。WKInterfaceObject及其子类WKInterfaceObject负责具体的界面元素设置,包括WKInterfaceButton、WKInterfaceLabel或WKInterfaceImage等对象,也就是我们上面提到的“视图元素”。一开始可能是错觉,认为WKInterfaceObject应该对应UIView,其实不然。WKInterfaceObject只是WatchKit实际View的WatchExtension端的代理,而不是View本身。在WatchApp中实际显示和呈现在屏幕上的视图对于代码来说是不直接可见的。我们只能在Extensiontarget中通过相应的代理对象来设置属性,然后在每次runloop需要刷新UI的时候,WatchKit将新的属性值从手机传递给手表中的WatchApp,并刷新界面.相反,手表中的实际视图希望通过WKInterfaceObject代理将用户交互事件传递给iPhone。每一个交互的WKInterfaceObject子类都对应一个动作,例如按钮对应点击事件,开关对应开启或关闭状态,滑块对应表示选中值的浮点值等等。关联这些事件也很简单,只需将Ctrl从StoryBoard文件中拖放到实现中即可生成相应的事件。虽然UI资源文件和代码实现在不同的目标中,但在Xcode中的协作已经是无缝的。Watch应用的布局与iOS应用完全不同。你不能随意指定一个view的具体坐标,当然也不能使用像AutoLayout或者SizeClasses这样灵活的界面布局方案。WatchKit提供的布局可能性和灵活性相对较小。只能以“行”为基本单位,用组在行内进行“列”布局。这带来了相对简单的布局实现,当然也对界面交互的设计提出了挑战。另外值得一提的是,随着WatchKit的出现及其开发方式的改变,这个争论多年的到底是写UI还是用StoryBoard的话题可以暂时告一段落了。Watch的开发不能使用代码。首先,所有的WKInterfaceObject对象都必须在设计时通过StoryBoard来添加,而我们在运行时不能再向界面添加或移除元素(如果需要移除,可以使用hidden);其次,一些WKInterfaceObject与布局相关的一些属性,例如行高和行数,不能在运行时更改和设置。基本上在运行时我们只能改变视图的内容,并通过隐藏一些视图元素来有限地改变布局(其他视图元素会尝试填充隐藏的元素)。总之,在Apple的不断努力下,为UI编写代码的传统,在??WatchKit发布的今天被正式宣判死刑。Table和ContextMenuWKInterfaceObject的大部分子类都很简单,但是有两个我想单独说一下,那就是WKInterfaceTable和WKInterfaceMenu。每个人都熟悉UITableView。虽然WatchKit中的WKInterfaceTable也是用来展示一组数据的,但是因为WatchKitAPI的数据传输特性,所以使用上和UITableView相比有很大的不同和简化。首先,这里没有DataSource和Delegate,WKInterfaceTable中需要呈现的数据个数直接通过它的实例方法-setNumberOfRows:withRowType:来设置。设置完成后,使用-rowControllerAtIndex:枚举所有rowController进行设置。这里的rowController相当于StoryBoard中设置的UITableViewCell,但是和其他WKInterfaceObject一样,直接继承自NSObject。可以自定义rowController并连接StoryBoard的元素,获取rowController后进行设置,完成表格的展示。代码如下所示://MyRowController.swiftimportFoundationimportWatchKitclassMyRowController:NSObject{@IBOutletweakvarlabel:WKInterfaceLabel!}//InterfaceController.swiftimportWatchKitimportFoundationclassInterfaceController:WKInterfaceController{@IBOutletweakvartable:WKInterfaceTable!letdata=["Index0","Index1","Indexin2context:AnyObject){//Initializevariableshere.super.init(context:context)//Configureinterfaceobjectshere.NSLog("%@init",self)//注意在StoryBoard中需要设置myRowControllerType//类似于cellreuseidtable.setNumberOfRows(data.count,withRowType:"myRowControllerType")for(i,value)inenumerate(data){ifletrowController=table.rowControllerAtIndex(i)as?MyRowController{rowController.label.setText(value)}}}}对于点击事件,还有没有实际的delegate,但是和其他WKInterfaceObject类似,点击哪一行作为参数传回WKInterfaceController处理。WatchKit,在iOS中不可用。存在。在任意一个WKInterfaceController界面,按住手表屏幕,如果当前WKInterfaceController中有上下文菜单,它会尝试调出寻找这个界面对应的上下文菜单。该菜单最多可以提供四个按钮,用于根据当前环境要求用户进行操作。因为手表屏幕有限,在显示信息的同时放一些交互按钮不太现实,而且会很难看。上下文菜单很好地解决了这个问题。相信长按呼出交互菜单的操作在未来会成为WatchApp非常标准的交互操作。添加一个ContextMenu很简单,只需要在StoryBoard中的WKInterfaceController中添加一个Menu,并在Menu中添加对应的MenuItem即可。在WKInterfaceController中我们也有相应的API可以在运行时根据上下文添加MenuItems(这是为数不多的允许我们在运行时添加元素的方法之一)。-addMenuItemWithItemIcon:title:action:-addMenuItemWithImageNamed:title:action:-addMenuItemWithImage:title:action:-clearAllMenuItems但是我们没有办法得到Menu和MenuItem对应的类WKInterfaceMenu和WKInterfaceMenuItem。没错,它们在文档中根本不存在:(基本导航WKInterfaceController内置的导航关系基本上分为三类。第一类是UINavigationController控制的类栈导航方式。相关的API是-pushControllerWithName:context:,-popController和-popToRootController后两者我觉得不需要过多解释,第一种方法需要使用目标控制器的Identifier字符串(只能在StoryBoard中设置))创造。context参数也会被传递给目标控制器的-initWithContext:,因此您可以使用它在控制器中传递数据。另一种是熟悉的模态形式,对应的API是-presentControllerWithName:context:和-dismissController。对于这种导航,与UIKit不同的是在目标控制器中默认会在左上角添加一个取消按钮。单击它将直接关闭呈现的控制器。我只想说苹果终于想通了,一个modal出来的每一个controller都需要关闭这个事实……***一个导航方式是类似于UIPageController的分页导航。在iOSApp中,这类导航在App刚启动时的教程模块中非常常见,在WatchKit中可以说是发扬光大了。事实上,我个人认为这将是WatchKit中使用最多的导航方法。WatchKit上的页面导航可能对应iOS应用的Tab导航提供的功能。在实现上,页面导航需要使用segue来连接StoryBoard中的不同页面。新添加的nextpagesegue就是为了这个目的:另外一个模态导航的API-presentControllerWithNames:contexts:接受多个名称和上下文这样模态调用的多个Controller也会在页面导航模式中呈现。当然,作为StoryBoard的经典使用方式,modal和push导航方式也可以通过StoryBoard中的segue来实现。同时WatchKit也为segue方法提供了必要的API。有些界面的做法因为整个架构跟UIKit是完全不同的,所以之前的很多做法是不能直接搬到WatchKitApp上去的。图片处理在UIKit中,我们一般使用UIImageView来展示图片,然后为它的image属性设置一个创建好的UIImage对象。对于WatchKit,最好的做法是将图片存储在WatchApp的target(即StoryBoard的target)中,在设置WKInterfaceImage的图片时尽量使用其-setImageNamed:方法。这种方式只会将图片名称通过手机传递给手表,然后手表会在自己的bundle中查找并??加载图片,这是最快的方式。注意我们的代码运行在手表的WatchApp的不同设备上。虽然我们也可以先通过UIImage的相关方法生成UIImage对象,然后使用-setImage:或者-setImageData:来设置手表上的图片,但是这种情况下,我们需要把图片放在Extension的target中,并且需要通过蓝牙将图片数据传输到手表。一般来说,这会造成不可忽略的延迟,极大地影响体验。如果在某些情况下,我们只能获取Extensiontarget中的图片(比如网络下载或者动态生成代码等),需要复用,最好使用WKInterfaceDevice的-addCachedImage:name:方法将其缓存到手表中。这样以后我们在使用这张图片的时候,直接通过-setImageNamed:就可以快速生成并从手表上使用。每个app的缓存大小在20M左右。如果超过,WatchKit将删除最旧的数据以为新数据腾出空间。由于动画无法获取到实际的视图元素,只能代理WKInterfaceObject等对象,加上布局系统的限制,复杂的动画,尤其是UIView系列或者CALayer系列的动画,是无法实现的。现在看来唯一可行的就是帧动画,可以通过为WKInterfaceImage设置一张包含多张图片的图片,或者通过定时器定时更换图片来实现。虽然苹果自己的例子也是用这种方式来实现动画,但是对于设备的存储空间和能耗可能是一个挑战,需要在实际拿到设备后进行实验和观察。使用其他CocoaTouch框架Apple建议不要使用需要提示用户许可的功能,例如CoreLocation定位等。因为实际的代码是在手机上运行的,这种权限也会在手机上弹出,但是用户不一定是在看手机,所以很可能会造成体验的下降。另外,大部分后台运行权限都不推荐。对于这些数据和权限的获取,苹果建议在iOSApp中进行,可以通过AppGroups共享数据,这样就可以在WatchExtension中获取数据。代码共享因为现在一个项目有很多不同的target,所以需要用框架把不同target的公共部分的代码封装起来,只在每个target中实现接口相关的代码。这样做的好处不仅是减少代码重复,还可以提高代码测试和质量。如果没有把逻辑部分的框架和测试分开,在实现各种扩展或者WatchApps的时候可能会遇到很多麻烦。因为如果原来的app有计划扩展推出各种Extension,那么把逻辑代码抽取出来封装成一个框架应该是重中之重。另外,如果新开的项目没有特殊原因,强烈建议使用framework来组织公共代码。Glance和Notification除了WatchApp本体,手表的Glance和Notification也是重要的使用场景。虽然Notification在概念上比较简单,但它与iOS通知有很大不同。WatchKit的通知允许开发人员构建自己的界面。我们可以通过payload来设置更复杂的通知,包含更多的信息,包括图片、大段文字,甚至交互按钮,而不是局限于iOS上的文字和一个。在对话框中。首先通知无论是通过Local还是Remote发送,都会先到达iPhone,然后iPhone根据内容判断是否转发给手表。WatchKitApp收到通知后,首先会显示一个简短的通知,告诉用户该应用有通知。如果用户对通知的内容感兴趣,可以点击或举手观看,这样就会出现开发者定制的长版通知。Glance是WatchKit的一个新概念,它允许WatchApp显示一个固定布局的WKInterfaceController页面。它相对于WatchAppbody的位置相当于iOS上的TodayWidget和iOSapp本身,是app在手表上最重要的信息展示。扫视,顾名思义,是短暂的提醒,不能有交互元素。但是,如果用户点击Glance页面,则可以启动到WatchApp。目前尚不清楚Glance本身是如何启动和呈现的,猜测它是某种类似TodayWidget的呈现方式?(比如按两下手表侧面的旋钮)问题和改进方向WatchKit总体上令人满意,提供的API和开发环境足以让开发者做出一些有趣的东西。但是有几个现在看起来很明显的局限性以及希望可以加强的方向。首先,从目前来看,WatchKit并没有提供任何获取设备传感器信息的API。无论是心跳、计步还是用户是否佩戴了Watch信息,我们都无法获取,这就限制了很多健康类APP的数据采集和监测的制作。如果要请求数据,还是得求助于HealthKit。但随着iPhone6和6s的更大屏幕,可以说运动时携带iPhone的人越来越少了。如果Watch在没有与iPhone配对的情况下无法采集记录,再与iPhone连接后将数据传回,苹果的健康卡就失败了一大半。相信苹果不会放过这个绑定用户的机会……但如果第三方应用能够实时获取用户的佩戴状态,相信会出现很多有趣的应用。另外,作为发布会上提倡的交互革命的旋钮和触摸屏,目前似乎还没有API开放给开发者使用,所以我们无法获知用户旋转手表旋钮这个重要的交互事件。现在看来,我们唯一可以获得的操作是用户点击屏幕上的按钮或拖动滑块。从这个角度来看,目前的WatchKit还远远不能颠覆移动应用。希望苹果未来能给我们带来其他的好消息。总之,舞台已经搭好了,接下来要唱什么样的戏,就看我们自己了。