※ まだ正式リリースされていない情報なので変更の可能性があります⚠️
今回KeynoteではAmazon Prime Videoの話しかされなかったtvOSですが、API Referenceを見るとそれなりに変更があったのでWhat's New in tvOSをまとめてみました👏 ※iOSと被っているところは省いています🙇
Focus Update Notifications
- Focusに対して更新処理が走る時に通知を受け取れるようになった
- Focusの移動に失敗した場合の通知も追加されている
static let UIFocusDidUpdate: NSNotification.Name
static let UIFocusMovementDidFail: NSNotification.Name
Protocol Extensions
UIFocusItem
プロトコルが拡張されて、自身が今Focusされているか確認できます。
var isFocused: Bool { get }
UIFocusEnvironment
プロトコルが拡張されて、自身が他のFocusEnvirionmentに含まれているか確認できます。
func contains(_ environment: UIFocusEnvironment) -> Bool
Focus Animations
Focusの移動時にアニメーションを定義できます。
// for focusing item
func addCoordinatedFocusingAnimations(_ animations: ((UIFocusAnimationContext) -> Void)?,
completion: (() -> Void)? = nil)
// for unfocusing item
func addCoordinatedUnfocusingAnimations(_ animations: ((UIFocusAnimationContext) -> Void)?,
completion: (() -> Void)? = nil)
Runs the specified set of animations together with the system animations for adding focus to an item.
今まで下記のようにやっていた処理はひとつのaddCoordinatedAnimations:completion
の中にフォーカス時とアンフォーカス時のアニメーションが入っていたので滑らかなアニメーションができませんでした😭
func collectionView(
_ collectionView: UICollectionView,
didUpdateFocusIn context: UICollectionViewFocusUpdateContext,
with coordinator: UIFocusAnimationCoordinator) {
coordinator.addCoordinatedAnimations({
if let indexPath = context.nextFocusedIndexPath {
let cell = collectionView.cellForItem(at: indexPath) as? NewCell
cell?.imageView.transform = CGAffineTransform(rotationAngle: 90.0)
}
if let indexPath = context.previouslyFocusedIndexPath {
let cell = collectionView.cellForItem(at: indexPath) as? NewCell
cell?.imageView.transform = .identity
}
})
}
今回はフォーカス時とアンフォーカス時のアニメーションブロックが別で定義されている為、それぞれのアニメーションが滑らかに動きます。また、Focusの移動スピードに合わせてアニメーションの早さも調節されます👏(シミュレーターのcmd+t
で見てみるとわかりやすいです)
if let indexPath = context.nextFocusedIndexPath {
let cell = collectionView.cellForItem(at: indexPath) as? NewCell
coordinator.addCoordinatedFocusingAnimations({ animationContext in
cell?.imageView.transform = CGAffineTransform(rotationAngle: 90.0)
})
}
if let indexPath = context.previouslyFocusedIndexPath {
let cell = collectionView.cellForItem(at: indexPath) as? NewCell
coordinator.addCoordinatedUnfocusingAnimations({ animationContext in
cell?.imageView.transform = .identity
})
}
Custom Focus Sounds
- フォーカス移動時に流れる音をカスタマイズできるようになった
-
UIFocusSystem
というクラスが追加されたことによって可能になった
UIFocusSystem
現在のユーザーインタフェース上にあるフォーカスの状態を管理するクラス。
今まではupdateFocusIfNeeded
のように自身のフォーカスの更新しかできませんでしたが、このクラスは全てのフォーカスの更新ができます。
func requestFocusUpdate(to environment: UIFocusEnvironment)
func updateFocusIfNeeded()
また、現在フォーカスされているオブジェクトやどのFocusEnvirionmentに含まれているかもチェックもできます。
weak var focusedItem: UIFocusItem? { get }
class func environment(_ environment: UIFocusEnvironment,
contains otherEnvironment: UIFocusEnvironment) -> Bool
その中にひとつだけフォーカス時の音を変更するメソッドがあります。
ローカルのサウンドファイルのURLと識別子を指定するだけでアプリ内にグローバルに反映されます。ただし、サウンドファイルは30秒未満でなくてはいけません。
class func register(_ soundFileURL: URL,
forSoundIdentifier identifier: UIFocusSoundIdentifier)
let url = Bundle.main.url(forResource: "a", withExtension: "m4a")!
let identifier = UIFocusSoundIdentifier(rawValue: "newSounds")
UIFocusSystem.register(url, forSoundIdentifier: identifier)
下記のようなサウンドの調整も行われるようです。
- スピードに合わせて音量が変更される
- フォーカスの移動方向(左右)によっても異なる
また、オブジェクトがフォーカスを更新するタイミングでカスタムとデフォルトとなしの切り替えもできます。例えばサイズの異なるオブジェクトになる場合は音を変えるというのは良いプラクティスだそうです👌
// UIFocusEnvironment Protocol
optional func soundIdentifierForFocusUpdate(in context: UIFocusUpdateContext) -> UIFocusSoundIdentifier?
override func soundIdentifierForFocusUpdate(
in context: UIFocusUpdateContext) -> UIFocusSoundIdentifier? {
return identifier
}
Support for SceneKit
SceneKitとSpriteKitでもフォーカスを扱えるようになった
UIFocusItem
の継承クラスにSKNode
とSCNode
が追加されている。
Focus Update Logging
-UIFocusUpdateLoggingEnabled=YES
の設定をXcode上で行うとフォーカスに関するログを吐く。素晴らしい。
The result of the focus update was determined from the following preferred focus search:
|
| Starting preferred focus search:
| |--> Searching <UIFocusSystem 0x60800008c1c0>...
| |--> Searching <UIScreen 0x6000001c2850>...
| |--> Searching <UIWindow 0x7fb5d1401c90>...
UIFocusDebugger
フォーカスをデバッグするためのクラスが追加されました。
po UIFocusDebugger.foo()
でFocusの状態を見たり検証したりできます。
-
status()
: フォーカスされているオブジェクトに関する情報 -
simulateFocusUpdateRequest(from: _)
: 特定のFocusEnvirionmentからフォーカスの更新を試せる -
checkFocusability(for: _)
: Focusできるかチェックできる(できない場合は理由も分かる) -
help()
: 使用できるコマンド(メソッド)全部出してくれる
(lldb) po UIFocusDebugger.status()
<TVOSample.SomeCell 0x7f80f5508340> is currently focused.
結構やりたかったことや実装が面倒だった部分が解消されている気がします。何より捉えにくいフォーカスをデバッグしやすくなったのは嬉しい🎉
サンプルコードもあげました。
おしまい👏