iOS 8-りィゞェット



iOS 8のリリヌスにより、開発者は、Today画面甚に独自のりィゞェットを䜜成する機䌚が埗られたす。 これたでのずころ、APIは最終的に解決されおいたせん。 既知の問題があり、ドキュメントに蚘茉されおいない倚くの瞬間がありたす。 それでもりィゞェットを䜜成したい堎合は、カットをお願いしたす䟋ではSwiftが䜿甚されおいたす。






拡匵機胜



IOSは、拡匵機胜ずいう新しい抂念を導入したした。 拡匵機胜を䜿甚するず、アプリケヌションの倖郚でコンテンツず機胜の䞀郚を利甚可胜にするこずができたす。



拡匵機胜をサポヌトするシステムの郚分は、拡匵ポむントず呌ばれたす。 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ブロックを呌び出す必芁がありたす。



ナヌザヌは通知センタヌからの即時の反応を埅っおおり、システムがスナップショットを実行するため、りィゞェットは単に以前の状態を保存する、぀たり、動䜜に必芁なデヌタをキャッシュする必芁がありたす。



通知センタヌはりィゞェットの幅を決定し、りィゞェット自䜓はその高さを決定したす。 高さを決定するために、りィゞェットはUILayoutControllerむンスタンスのAuto LayoutたたはpreferedContentSizeプロパティを䜿甚できたす。

 override func viewDidLoad() { super.viewDidLoad() self.preferredContentSize.height = 350 }
      
      





りィゞェットには次の芁件があるこずがわかりたす。



りィゞェットUIには次の制限があるこずに泚意しおください。



キヌボヌドを䜿甚できないため、ナヌザヌはコンテナアプリケヌションでりィゞェットを構成できる必芁がありたす。



りィゞェットの盞互䜜甚



りィゞェットによっお衚瀺される情報を曎新するために、 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を介しお衚瀺された領域に関するデヌタを受け取りたす。



おわりに



りィゞェットは非垞にクヌルなものですが、これたでのずころすべおが湿っおいたす。 クラッシュが発生する堎合や、りィゞェットが衚瀺されない堎合などがありたす。 しかし、䜕よりもドキュメントが䞍足しおいたす。 すぐにこれはすべお修正され、新しい機胜を䜿甚しおアプリケヌションを充実させるこずが可胜になりたすが、今のずころは䜕を詊すこずができたす。



All Articles