2021 SwiftUI & UI Frameworks
WWDC21 · 20 min · SwiftUI & UI Frameworks
Focus on iPad keyboard navigation
Improve the keyboard experience in your iPad and Mac Catalyst app. Discover how you can accelerate access to key features with the hardware keyboard, and navigate through your views and view controllers. Learn how to customize which elements are keyboard navigable, as well as how to customize the tab loop.
Watch at developer.apple.com ↗Code shown on screen · 15 snippets
canBecomeFocused
override var canBecomeFocused: Bool { true } allowsFocus
class MyViewController: UICollectionViewController {
override func viewDidLoad() {
super.viewDidLoad()
self.collectionView.allowsFocus = true
}
} canFocusItemAtIndexPath
class MyCollectionViewDelegate: NSObject, UICollectionViewDelegate {
func collectionView(_ collectionView: UICollectionView,
canFocusItemAt indexPath: IndexPath) -> Bool {
return true
}
} UIFocusDebugger checkFocusability(for:)
po UIFocusDebugger.checkFocusability(for:) UIFocusHaloEffect
let focusEffect = UIFocusHaloEffect(roundedRect: self.bounds, cornerRadius: self.layer.cornerRadius, curve: .continuous)
self.focusEffect = focusEffect ReferenceView and ContainerView
let focusEffect = UIFocusHaloEffect(roundedRect: self.bounds, cornerRadius: self.layer.cornerRadius, curve: .continuous)
// make sure the effect is added right above the image view
focusEffect.referenceView = self.imageView
// make sure the effect is added to our scroll view
focusEffect.containerView = self.scrollView
self.focusEffect = focusEffect Custom focus effects
init(frame: CGRect) {
super.init(frame: frame)
self.focusEffect = nil
}
func didUpdateFocus(in context: UIFocusUpdateContext, withAnimationCoordinator coordinator: UIFocusAnimationCoordinator) {
if context.nextFocusedItem == self {
// This view is focused. Customize its appearance.
}
else if context.previouslyFocusedItem == self {
// This view was focused.
}
} Selection Follows Focus
var selectionFollowsFocus: Bool Selection Follows Focus for Item at Index Path
func collectionView(_ collectionView: UICollectionView, selectionFollowsFocusForItemAt indexPath: IndexPath) -> Bool {
return self.action(for: indexPath).type != .showAlert
} Focus Group Identifier
self.focusGroupIdentifier = "com.myapp.groups.sidebar" UIFocusGroupPriority
extension UIFocusGroupPriority {
public static let ignored: UIFocusGroupPriority // 0
public static let previouslyFocused: UIFocusGroupPriority // 1000
public static let prioritized: UIFocusGroupPriority // 2000
public static let currentlyFocused: UIFocusGroupPriority // NSIntegerMax
} Focus Group Priority on a cell
// Customizing an item’s focus group priority
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = ...
if self.isCallToActionCell(at: indexPath) {
// This cell is not as important as a selected cell but should
// be chosen over the last focused cell in this group.
cell.focusGroupPriority = .previouslyFocused + 10
}
return cell
} UIFocusDebugger checkFocusGroupTree(for:)
po UIFocusDebugger.checkFocusGroupTree(for:) wantsPriorityOverSystemBehavior
keyCommand.wantsPriorityOverSystemBehavior = true pressesBegan
override func pressesBegan(_ presses: Set<UIPress>, with event: UIPressesEvent?) {
if (/* check presses of interest */) {
// handle the press
}
else {
super.pressesBegan(presses, with: event)
}
} Resources
Related sessions
-
24 min -
23 min -
36 min -
27 min -
17 min -
30 min