
iOS 8ã®ãªãªãŒã¹ã«ãããéçºè ã¯ãTodayç»é¢çšã«ç¬èªã®ãŠã£ãžã§ãããäœæããæ©äŒãåŸãããŸãã ãããŸã§ã®ãšãããAPIã¯æçµçã«è§£æ±ºãããŠããŸããã æ¢ç¥ã®åé¡ããããããã¥ã¡ã³ãã«èšèŒãããŠããªãå€ãã®ç¬éããããŸãã ããã§ããŠã£ãžã§ãããäœæãããå Žåã¯ãã«ããããé¡ãããŸãïŒäŸã§ã¯Swiftã䜿çšãããŠããŸãïŒã
æ¡åŒµæ©èœ
IOSã¯ãæ¡åŒµæ©èœãšããæ°ããæŠå¿µãå°å ¥ããŸããã æ¡åŒµæ©èœã䜿çšãããšãã¢ããªã±ãŒã·ã§ã³ã®å€éšã§ã³ã³ãã³ããšæ©èœã®äžéšãå©çšå¯èœã«ããããšãã§ããŸãã
æ¡åŒµæ©èœããµããŒãããã·ã¹ãã ã®éšåã¯ãæ¡åŒµãã€ã³ããšåŒã°ããŸãã iOSã§ã¯æ¬¡ã®æ¡åŒµãã€ã³ãã䜿çšã§ããŸãã
- TodayïŒéç¥ã»ã³ã¿ãŒïŒ-éç¥ã»ã³ã¿ãŒã®Todayç»é¢ããã¢ã¯ã·ã§ã³ããã°ããå®è¡ããããæ å ±ãååŸãããããŸã
- å ±æ-å人ãšããŸãã¯ä»»æã®ãµã€ãã®ãã£ãŒãã§ã³ã³ãã³ããå ±æããŸã
- ã¢ã¯ã·ã§ã³-å¥ã®ã¢ããªã±ãŒã·ã§ã³ã®ã³ã³ããã¹ãå ã§ã³ã³ãã³ãã衚瀺ãŸãã¯ç®¡çããŸã
- åçç·šé-åçã¢ããªå ã§åçããããªãç·šéããŸã
- ã¹ãã¬ãŒãžãããã€ããŒ-çŸåšã®ã¢ããªã±ãŒã·ã§ã³ã§äœ¿çšå¯èœãªããã¥ã¡ã³ãã®ã»ããããããã¥ã¡ã³ããéžæããŸã
- ã«ã¹ã¿ã ããŒããŒã-ãã€ãã£ãã®iOSããŒããŒãããã¹ãŠã®ã¢ããªã±ãŒã·ã§ã³ã§äœ¿çšããããã®ããŒããŒãã«çœ®ãæããŸã
æ¡åŒµæ©èœã¯ãéåžžã®ã¢ããªã±ãŒã·ã§ã³ã®ãã³ãã«ã§ããæ¡åŒµæ©èœã³ã³ããã§ã®ã¿é åžã§ããŸãã 1ã€ã®ã³ã³ããã«è€æ°ã®æ¡åŒµæ©èœãå«ããããšãã§ããŸãã
æ¡åŒµæ©èœã¯ç¹å¥ãªã¿ã€ãã®ãã€ããªãã¡ã€ã«ã§ããããšã«æ³šæããŠãã ããã ããã¯ã¢ããªã±ãŒã·ã§ã³ã§ã¯ãããŸããïŒ
æ®å¿µãªãããæ¡åŒµæ©èœã¯App to App IPCïŒãã€ãããœã±ãããªã©ïŒããµããŒãããŠããªãããã䜿ãæ £ãã[UIApplication openURLïŒ]ïŒæ¡åŒµæ©èœã§ã¯æ©èœããŸããã æ¢ç¥ã®åé¡ãåç §ïŒãŸãã¯App Groupãªã©ã䜿çšããå¿ èŠããããŸãã ã
åæ¡åŒµæ©èœã¯åå¥ã®ããã»ã¹ã§å®è¡ãããŸãã ãããã£ãŠãç°ãªãã¢ããªã±ãŒã·ã§ã³ã®ã³ã³ããã¹ãã«ãããåãæ¡åŒµæ©èœã¯ç°ãªãããã»ã¹ã§ãããåæã®åé¡ãå¿é ããå¿ èŠã¯ãããŸããã
æ¡åŒµæ©èœã®ããã¥ã¡ã³ãã¯ãã¡ãã§ãã
ãŠã£ãžã§ãã
ãŠã£ãžã§ããã¯æ¡åŒµæ©èœãšåŒã°ããTodayç»é¢ã®éç¥ã»ã³ã¿ãŒã«æ å ±ã衚瀺ãããããçŸæç¹ã§éèŠãªæ å ±ã衚瀺ããããã«èšèšãããŠããŸãã ãŠãŒã¶ãŒãTodayãéããšãèå³ã®ããæ å ±ãããã«å©çšã§ããããã«ãªããšæåŸ ããŠããŸãã
ãŠã£ãžã§ããã¯ããŠãŒã¶ãŒããŠã£ãžã§ãããå«ãã¢ããªã±ãŒã·ã§ã³ãã€ã³ã¹ããŒã«ããåŸã«äœ¿çšå¯èœã«ãªããŸãïŒã¢ããªã±ãŒã·ã§ã³ã®æåã®èµ·ååŸã«ãŠã£ãžã§ãããã€ã³ã¹ããŒã«ãããªãããšãèµ·ãããŸããçµå±ã®ãšãããããã¯ãŸã ããŒã¿çã§ãïŒã ãŠã£ãžã§ãããè¿œå ããã«ã¯ãéç¥ã»ã³ã¿ãŒã§[ä»æ¥]ç»é¢ãéãã[ç·šé]ãã¿ã³ãã¯ãªãã¯ããŠç®çã®ãŠã£ãžã§ãããè¿œå ããå¿ èŠããããŸãã
ã³ã³ãããšãŠã£ãžã§ããéã®æ¥ç¶ã¯ã NotificationCenter.frameworkãä»ããŠè¡ãããŸãã
åºæ¬çã«ããŠã£ãžã§ããã¯ãiOSããã°ã©ããŒã«éŠŽæã¿ã®ããUIViewControllerã§ãã ãããã£ãŠããŠã£ãžã§ãããäœæãããšãã«ã以åã«èç©ããç¥èã䜿çšã§ããŸãã ããšãã°ããŠã£ãžã§ããã衚瀺ããåã«äœããã®ã¢ã¯ã·ã§ã³ãå®è¡ããå¿ èŠãããå ŽåãviewWillAppearãªã©ããªãŒããŒã©ã€ãããå¿ èŠããããŸãã
ãŠã£ãžã§ãããåžžã«ææ°ã®ç¶æ ã«ããããã«ãiOSã¯æã ãŠã£ãžã§ããã®ã¹ãããã·ã§ãããäœæããŸãã ãŠã£ãžã§ãããåã³è¡šç€ºããããšãæåŸã®ã¹ãããã·ã§ãããæåã«è¡šç€ºããã次ã«å®éã®ãŠã£ãžã§ãããŠã£ã³ããŠã衚瀺ãããŸãã ã¹ãããã·ã§ããã®åã«ãŠã£ãžã§ããã®ç¶æ ãæŽæ°ããããã«ã NCWidgetProvidingãããã³ã«ã䜿çšãããŸãã
protocol NCWidgetProviding : NSObjectProtocol { // Called to allow the client to update its state prior to a snapshot being taken, or possibly other operations. // Clients should call the argument block when the work is complete, passing the appropriate 'NCUpdateResult'. @optional func widgetPerformUpdateWithCompletionHandler(completionHandler: ((NCUpdateResult) -> Void)!) // Clients wishing to customize the default margin insets can return their preferred values. // Clients that choose not to implement this method will receive the default margin insets. @optional func widgetMarginInsetsForProposedMarginInsets(defaultMarginInsets: UIEdgeInsets) -> UIEdgeInsets }
widgetPerformUpdateWithCompletionHandlerããŠã£ãžã§ããã§åŒã³åºããããšããã®ãŠã£ã³ããŠãæŽæ°ããŠããã次ã®å®æ°ã®ããããã«çããåŒæ°ã§completionHandlerãããã¯ãåŒã³åºãå¿ èŠããããŸãã
- NCUpdateResultNewData-æ°ããã³ã³ãã³ãã«ã¯ãŠã£ã³ããŠã®æŽæ°ãå¿ èŠ
- NCUpdateResultNoData-ãŠã£ãžã§ãããæŽæ°ããå¿ èŠã¯ãããŸãã
- NCUpdateResultFailed-æŽæ°ããã»ã¹äžã«ãšã©ãŒãçºçããŸãã
ãŠãŒã¶ãŒã¯éç¥ã»ã³ã¿ãŒããã®å³æã®åå¿ãåŸ ã£ãŠãããã·ã¹ãã ãã¹ãããã·ã§ãããå®è¡ããããããŠã£ãžã§ããã¯åã«ä»¥åã®ç¶æ ãä¿åãããã€ãŸããåäœã«å¿ èŠãªããŒã¿ããã£ãã·ã¥ããå¿ èŠããããŸãã
éç¥ã»ã³ã¿ãŒã¯ãŠã£ãžã§ããã®å¹ ã決å®ãããŠã£ãžã§ããèªäœã¯ãã®é«ãã決å®ããŸãã é«ãã決å®ããããã«ããŠã£ãžã§ããã¯UILayoutControllerã€ã³ã¹ã¿ã³ã¹ã®Auto LayoutãŸãã¯preferedContentSizeããããã£ã䜿çšã§ããŸãã
override func viewDidLoad() { super.viewDidLoad() self.preferredContentSize.height = 350 }
ãŠã£ãžã§ããã«ã¯æ¬¡ã®èŠä»¶ãããããšãããããŸãã
- 衚瀺ãããã³ã³ãã³ããææ°ã§ããããšã確èªããŠãã ãã
- ãŠãŒã¶ãŒã®ã¢ã¯ã·ã§ã³ã«é©åã«å¯Ÿå¿ãã
- ã§ããã ãå°ãªããªãœãŒã¹ãæ¶è²»ããŸãïŒiOSã¯ã倧éã®ã¡ã¢ãªãæ¶è²»ãããšãŠã£ãžã§ããã匷å¶çµäºã§ããŸãïŒ
ãŠã£ãžã§ããUIã«ã¯æ¬¡ã®å¶éãããããšã«æ³šæããŠãã ããã
- ããŒããŒãã衚瀺ããŸãã
- ãžã§ã¹ãã£ãŒã§æ©èœããã³ã³ãããŒã«ïŒUIDatePickerãªã©ïŒã¯äœ¿çšã§ããŸãã
- çŸæç¹ã§ã¯ãããã衚瀺ã§ããŸããïŒãã®é ç®ã¯æ¢ç¥ã®åé¡ã«è¡šç€ºãããŸã-Mapviewsã¯ãŠã£ãžã§ããã«ã¿ã€ã«ãããŒãããŸããïŒã
ããŒããŒãã䜿çšã§ããªãããããŠãŒã¶ãŒã¯ã³ã³ããã¢ããªã±ãŒã·ã§ã³ã§ãŠã£ãžã§ãããæ§æã§ããå¿ èŠããããŸãã
ãŠã£ãžã§ããã®çžäºäœçš
ãŠã£ãžã§ããã«ãã£ãŠè¡šç€ºãããæ å ±ãæŽæ°ããããã«ã NCWidgetControllerã¯ã©ã¹ããããŸãã
ãã®ã¯ã©ã¹ã®ã€ã³ã¹ã¿ã³ã¹ã«ã¯ãåäžã®setHasContentïŒforWidgetWithBundleIdentifierïŒã¡ãœããããããŸãããã®ã¡ãœããã¯ãæ å ±ãæŽæ°ããå¿ èŠãããããšã瀺ãã¡ãã»ãŒãžããŠã£ãžã§ããã«éä¿¡ããŸãã
次ã®ããã«äœ¿çšããŸãã
NCWidgetController.widgetController().setHasContent(true, forWidgetWithBundleIdentifier: "com.e-legion.Traffic.Widget")
ãã®ã¯ã©ã¹ã¯ããŠã£ãžã§ããããã³ã³ã³ãããŒã¢ããªã±ãŒã·ã§ã³ãã䜿çšã§ããŸãã
ã³ã³ããã¢ããªã±ãŒã·ã§ã³ãšã®ããŒã¿äº€æ
ã³ã³ãããšéä¿¡ããããã«ã UIViewControllerãææããextensionContextããããã£ãä»ããŠã¢ã¯ã»ã¹å¯èœãªNSExtensionContextãªããžã§ã¯ãã䜿çšãããŸãã
class NSExtensionContext : NSObject { // The list of input NSExtensionItems associated with the context. If the context has no input items, this array will be empty. var inputItems: AnyObject[]! { get } // Signals the host to complete the app extension request with the supplied result items. The completion handler optionally contains any work which the extension may need to perform after the request has been completed, as a background-priority task. The `expired` parameter will be YES if the system decides to prematurely terminate a previous non-expiration invocation of the completionHandler. Note: calling this method will eventually dismiss the associated view controller. func completeRequestReturningItems(items: AnyObject[]!, completionHandler: ((Bool) -> Void)!) // Signals the host to cancel the app extension request, with the supplied error, which should be non-nil. The userInfo of the NSError will contain a key NSExtensionItemsAndErrorsKey which will have as its value a dictionary of NSExtensionItems and associated NSError instances. func cancelRequestWithError(error: NSError!) // Asks the host to open an URL on the extension's behalf func openURL(URL: NSURL!, completionHandler: ((Bool) -> Void)!) }
ã€ãŸãããŠã£ãžã§ããããã³ã³ããã¢ããªã±ãŒã·ã§ã³ãéãã«ã¯ãã³ã³ãããã¹ããŒã ïŒããšãã°ããtrafficïŒ//ãïŒãç»é²ããå¿ èŠãããããŠã£ãžã§ããã³ãŒãã«è¿œå ããå¿ èŠããããŸã
self.extensionContext.openURL(NSURL(string: "traffic://"), completionHandler: nil)
ã
ããã©ã«ãã§ã¯ãiOSã»ãã¥ãªãã£ã«ãããã³ã³ããã¢ããªã±ãŒã·ã§ã³ãšæ¡åŒµæ©èœéã®ããŒã¿äº€æãé²æ¢ãããŸãã ããŒã¿äº€æãæå¹ã«ããã«ã¯ãã³ã³ããã¿ãŒã²ãããšæ¡åŒµæ©èœãåãApp Groupã«è¿œå ããå¿ èŠããããŸãã

ãã®çµæãè³æ Œãã¡ã€ã«ã«ã¯æ¬¡ã®å 容ãå«ãŸããŸãã
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>com.apple.security.application-groups</key> <array> <string>group.96GT47C53G.traffic</string> </array> </dict> </plist>
96GT47C53Gã«æ³šæããŠãã ããã ããã¯éçºããŒã IDã§ãã ãããã£ãŒã«ã§ç¢ºèªã§ããŸã ã ã·ãã¥ã¬ãŒã¿ãŒã§å®è¡ããã«ã¯ãgroup.trafficãªã©ã®ä»»æã®å€ã䜿çšã§ããŸãã
ããã§ã containerURLForSecurityApplicationGroupIdentifierã¡ãœããã䜿çšããŠãå ±æãã©ã«ããŒãžã®ãã¹ãååŸããã¢ããªã±ãŒã·ã§ã³åºæã®ããŒã¿ãããã«ä¿åã§ããŸãã
NSFileManager.defaultManager().containerURLForSecurityApplicationGroupIdentifier("group.96GT47C53G.traffic")
äŸ
ããã§ã¯ãæžæ»ã®ããå°å³ã衚瀺ãããŠã£ãžã§ãããäœæããŠã¿ãŸãããã Yandex.Maps APIã䜿çšããŠã亀éæ å ±ãåç圢åŒã§åãåããŸã ã
åçãåä¿¡ããããã®ãªã³ã¯ã®äŸïŒ http : //static-maps.yandex.ru/1.x/?ll=30.35,59.9690273&spn=0.01,0.2&size=300,250&l=map,trf
å®éã«ã¯ãlatãšlonã¯ãããã®äžå¿ã§ãããspnã¯ããã衚瀺é åã®ç¯å²ïŒåºŠåäœïŒã§ãã
ãããžã§ã¯ãã®ãœãŒã¹ã³ãŒãã¯GitHubã§å ¥æã§ããŸã ã
ã³ã³ããã¢ããªã±ãŒã·ã§ã³ãäœæãã
ã³ã³ããã®ã¿ã¹ã¯ã¯ããŠãŒã¶ãŒããŠã£ãžã§ãããèšå®ã§ããããã«ããããšã§ããã€ãŸãããããäžã®ãšãªã¢ãéžæããŸãã ãããã£ãŠãã¢ããªã±ãŒã·ã§ã³ã«ã¯ãé åãéžæããããã®MapViewãšããã®é åããŠã£ãžã§ããã«è»¢éããããã¬ãŒã ãèšå®ããã¿ã³ãå«ãŸããŸãã

ã¢ããªã±ãŒã·ã§ã³ã§ã¯ããŠãŒã¶ãŒãlatãlonãspnãã©ã¡ãŒã¿ãŒãéžæããŠãŠã£ãžã§ããã«æž¡ãããšãã§ããããã«ããå¿ èŠããããŸãã 次ã®ã³ãŒãã¯ãããè¡ããŸãã
@IBAction func updateWidgetButtonTapped(sender : AnyObject) { var dict : NSMutableDictionary = NSMutableDictionary() dict["spn"] = self.mapView.region.span.latitudeDelta dict["lat"] = self.mapView.region.center.latitude dict["lon"] = self.mapView.region.center.longitude var dictUrl : NSURL = NSFileManager.defaultManager().containerURLForSecurityApplicationGroupIdentifier("group.96GT47C53G.traffic").URLByAppendingPathComponent("settings.dict") dict.writeToFile(dictUrl.path, atomically: true) NCWidgetController.widgetController().setHasContent(true, forWidgetWithBundleIdentifier: "com.e-legion.Traffic.Widget") }
ãŠã£ãžã§ãããè¿œå
ãŠã£ãžã§ãããè¿œå ãããšãæ°ããã¿ãŒã²ãããè¿œå ãããŸãã
ããã¡ã€ã«ã->ãæ°èŠã->ãã¿ãŒã²ããããã¯ãªãã¯ãããiOSã->ãã¢ããªã±ãŒã·ã§ã³æ¡åŒµæ©èœã->ãTodayæ¡åŒµæ©èœããéžæããŸãã

ããã§ããŠã£ãžã§ããã®ã¹ã¿ãããããžã§ã¯ãã«è¿œå ãããŸããã ãã ãããŠã£ãžã§ãããä»ãã䜿çšããããšãããšãäœãæ©èœããŸããã ãŠã£ãžã§ãããã¯ã©ãã·ã¥ããŸãã ãããä¿®æ£ããã«ã¯ã次ã®ã¡ãœãããTodayViewControllerã«è¿œå ããŸãã
init(coder aDecoder: NSCoder!) { super.init(coder: aDecoder) }
ãã³ãã¬ãŒãã®Info.plistãã¡ã€ã«ã«æ³šæããŠãã ããã NSExtensionããŒãå«ãŸããŠããŸãããã®ããŒã¯ãããã€ãã®ãŠã£ãžã§ãããã©ã¡ãŒã¿ãŒãå®çŸ©ããŸãã
<key>NSExtension</key> <dict> <key>NSExtensionMainStoryboard</key> <string>MainInterface</string> <key>NSExtensionPointIdentifier</key> <string>com.apple.widget-extension</string> </dict>
NSExtensionMainStoryboardã«ã¯ããŠã£ãžã§ããã®ã³ã³ãããŒã©ãŒãæ ŒçŽããã¹ããŒãªãŒããŒãã®ååãæ ŒçŽãããŸãã NSExtensionMainStoryboardããŒãNSExtensionPrincipalClassã«çœ®ãæããå€ãšããŠã³ã³ãããŒã©ãŒã®ååã䜿çšããããšã§ãã³ã³ãããŒã©ãŒãæ瀺çã«æå®ã§ããŸãã
åãŠã£ãžã§ããã«ã¯ãããã«å·Šã·ããããããŸãã åé€ããå Žåã¯ãwidgetMarginInsetsForProposedMarginInsetsã¡ãœããã§ç®çã®UIEdgeInsetsãè¿ãå¿ èŠããããŸãã
func widgetMarginInsetsForProposedMarginInsets(defaultMarginInsets: UIEdgeInsets) -> UIEdgeInsets { return UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0) }
ãŠã£ãžã§ããã®ã³ãŒãã¯éåžžã«ç°¡åã§ãã ããããæŽæ°ããupdateMapã¡ãœããããããŸãã æŽæ°ã¯ããŠã£ãžã§ããã®è¡šç€ºïŒviewWillLoadïŒããã¿ã³ã®ã¯ãªãã¯ãwidgetPerformUpdateWithCompletionHandlerã®åŒã³åºããéå§ããããšçºçããŸãã ãŠã£ãžã§ããã¯ã containerURLForSecurityApplicationGroupIdentifierãä»ããŠè¡šç€ºãããé åã«é¢ããããŒã¿ãåãåããŸãã
ãããã«
ãŠã£ãžã§ããã¯éåžžã«ã¯ãŒã«ãªãã®ã§ããããããŸã§ã®ãšãããã¹ãŠã湿ã£ãŠããŸãã ã¯ã©ãã·ã¥ãçºçããå ŽåãããŠã£ãžã§ããã衚瀺ãããªãå Žåãªã©ããããŸãã ããããäœãããããã¥ã¡ã³ããäžè¶³ããŠããŸãã ããã«ããã¯ãã¹ãŠä¿®æ£ãããæ°ããæ©èœã䜿çšããŠã¢ããªã±ãŒã·ã§ã³ãå å®ãããããšãå¯èœã«ãªããŸãããä»ã®ãšããã¯äœãè©Šãããšãã§ããŸãã