티스토리 뷰

728x90
반응형

 

[1] Focus System 

 

 

UIFocusItem 프로토콜은 item이 focused 될 수 있음을 의미합니다.

UIFocusEnvironments 프로토콜은 focusable items들 간의 hierarchy를 정의합니다. 

 

UIView는 이 두개의 프로토콜을 채택하고 있습니다. 

즉 모든 뷰는 focused 될 수 있으며,  focused 될 수 있는 subviews를 가질 수도 있습니다. 

 

UIViewController는 오직 UIFocusEnvironments 프로토콜만 채택하고 있습니다. 

 

 

[2] Focusability 


canBecomeFocused 는 현재 뷰가 being focused 될 수 있는 지 여부를 나타내는 프로퍼티 입니다.

이 프로퍼티를 통해 focus engine 에게 view의 focusability를 알려줄 수 있습니다.

 

default 값은 false입니다. 

만약 이 값이 true 더라도 아래의 경우에는 view가 focusable 하지 않을 수 있습니다. 

 

 

이 프로퍼티를 오버라이딩하여서 true를 리턴해주면 

뷰를 focusable 하게 만들 수 있습니다. 

 

 

 

[3] Debugging Focusability

 

focusability를 디버깅할 수 도 있습니다. 

왜 focusable 하지 못하는지 설명도 같이 알려준다고 하네요

 

 

[4] 뷰 사이의 포커스 이동 

 

위에서 말한 대로 코드를 작성하고 테스트 해봅시다.

 

import UIKit
class FocusableView: UIView {
override var canBecomeFocused: Bool {
return true
}
override init(frame: CGRect) {
super.init(frame: frame)
self.isUserInteractionEnabled = true
}
required init?(coder: NSCoder) {
super.init(coder: coder)
self.isUserInteractionEnabled = true
}
override func didUpdateFocus(in context: UIFocusUpdateContext, with coordinator: UIFocusAnimationCoordinator) {
super.didUpdateFocus(in: context, with: coordinator)
if context.nextFocusedView == self {
self.layer.borderColor = UIColor.red.cgColor
self.layer.borderWidth = 2
} else if context.previouslyFocusedView == self {
self.layer.borderColor = UIColor.clear.cgColor
self.layer.borderWidth = 0
}
}
}
class ViewController: UIViewController {
@IBOutlet weak var yellowView: FocusableView!
@IBOutlet weak var blueView: FocusableView!
@IBOutlet weak var purpleView: FocusableView!
override func viewDidLoad() {
super.viewDidLoad()
}
}

 

## 1. 

canBecomeFocused 를 true로 오버라이딩 해줍니다.

 

 

## 2.

 isUserInteractionEnabled를 true로 해줍니다.

위에서 이게 false면 동작안할 수 도 있다고 해서 true로 했는데 안해도 동작 잘하더라구요

 

## 3.

didUpdateFocus(in:with:) 은 시스템이 다음 뷰로 포커스를 이동할 때 불리는 메소드입니다. 

여기서 다음 이동이 자신이라면 (nextFocusedView가 자신) 빨간 테두리를 표시하고 

나에게서 다른 뷰로 이동하려고 한다면 (previouslyFocusedView가 자신) 빨간 테두리를 지우게 해봅니다. 

 

 

 

키보드의 arrow down / up 키를 누르면 포커스가 잘 이동하는 것을 볼 수 있습니다. 

 

 

[5] TableView / CollectionView Cell 사이의 포커스 이동 

 

iOS 15에서 TableView와 CollectionView에 allowsFocus가 추가되었습니다. 🎉

 

https://developer.apple.com/documentation/uikit/uitableview

 

https://developer.apple.com/documentation/uikit/uicollectionview

 

 

collectionView나 tableview에 allowsFoucs를 true로 설정해주면

모든 cell이 focusable 해집니다! (따로 서브클래싱 해줄 필요없음!!)

그리고 Sidebar는 allowsFocus가 디폴트로 true라고 합니다. 

 

 

 

만약 cell의 focusability를 각각 컨트롤 하고 싶으면 

collectionView(_:canFocusItemAt:) 을 사용할 수 있습니다. (이 메소드는 iOS 9부터 쓸 수 있네요)

이것은 canBecomeFocused를 오버라이딩하지 않은 셀에 대해서만 잘 적용된다고 합니다. 

 

 

 

코드로 테스트해봅시다!

(allowsFocused는 iOS 15부터 쓸 수 있기 때문에 XCode 13 beta 버전을 받으셔야합니다.)

 

import UIKit
class ViewController: UIViewController {
@IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
self.setupTableView()
}
private func setupTableView() {
self.tableView.dataSource = self
self.tableView.allowsFocus = true
}
}
extension ViewController: UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 20
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "SampleTableViewCell") ?? UITableViewCell()
return cell
}
}

 

키보드 arrow down & up 했을 때 포커스 이동이 잘 동작합니다.

포커스된 cell의 색깔은 tableView의 tintColor 색깔로 설정됩니다. 

cell highlight appearance 관련해서는 background and content configuration 을 살펴보라고 하시네요

(참고: Modern cell configuration)

 

 

 

그리고 iOS 15에서는 allowsFocused 안하고

아래처럼 canBecomeFocused true로 cell 서브클래싱해주면 동일하게 동작합니다!

하지만 iOS 15 하위버전은 동작안하더라구요 ㅠㅠ 

(키보드 arrow 눌러도 cell에서 오버라이딩한 didUpdateFocus가 안들어옴)

 

import UIKit
class SampleTableViewCell: UITableViewCell {
override var canBecomeFocused: Bool {
return true
}
}
class ViewController: UIViewController {
@IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
self.setupTableView()
}
private func setupTableView() {
self.tableView.dataSource = self
}
}
extension ViewController: UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 20
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "SampleTableViewCell") ?? UITableViewCell()
return cell
}
}

 

UITableViewDelegate 의 이 메소드들을 테스트해봐도 키보드로 포커스 이동이 안됨,, 

 

- tableView(_:canFocusRowAt:)

- tableView(_:didUpdateFocusIn:with:) 

 

iOS 15 하위버전은 어떻게 대응할 수 있는 지 모르겠음,, 

 

 

[6] 테스트해보니 불가능한 것 

 

1. 텍스트뷰 끼리 이동 불가능.

아래 사진처럼 키보드 arrow up / down 은 텍스트뷰 내에서 줄이동만 할 수 있지

첫줄과 마지막 줄이라고 다음 텍스트뷰로 포커스 이동은 안됨.

 

 

 

2. focus update 중에 becomeFirstResponder를 부를 수 없음

 

예를들어 cell안에 textView가 있을 때  cell로 포커스이동 되자마자 cell 안의 텍스트뷰로 포커스 이동시키고 싶음
하지만 cell의 didUpdateFocus에서 textview.becomeFirstResponder를 부를 수 없음 

(WWDC에서도 becomeFirstResponder를 명시적으로 콜하지 말라고 하고 코드로 해봐도 크래쉬 남)

 

 

 

Reference

 

https://developer.apple.com/videos/play/wwdc2021/10260/

 

Focus on iPad keyboard navigation - WWDC21 - Videos - Apple Developer

Improve the keyboard experience in your iPad and Mac Catalyst app. Discover how you can accelerate access to key features with the...

developer.apple.com

 

https://bignerdranch.com/blog/10-tips-for-mastering-the-focus-engine-on-tvos/

 

10 Tips for Mastering the Focus Engine on tvOS - Digital product development agency | Big Nerd Ranch

Check out our blog post 10 Tips for Mastering the Focus Engine on tvOS from Big Nerd Ranch. Learn more and read it now!

bignerdranch.com

 

반응형
댓글

eungding님의
글이 좋았다면 응원을 보내주세요!