在 tableView.remembersLastFocusedIndexPath = false 時候,列表會出現失去焦點時候,自動往上滾動的問題。
在 tableView.remembersLastFocusedIndexPath = true 時候,會出現 SwiftUI 繪製的,無法更改 UITableView 的焦點問題。
先來觀看 Bug 的效果
當設置為 tableView.remembersLastFocusedIndexPath = false 時候,焦點會自行往上移動。
Simulator Screen Recording - tvOS 18.2 - 2025-01-13 at 04.11.21.mp4
@ViewBuilder func CocoaListView() -> some View {
CocoaList(data.epsList) { ep in
CocoaListEpView(ep)
}
.introspectTableView { tableView in
self.tableView = tableView
tableView.delegate = delegateHandler
tableView.allowsFocus = true
tableView.allowsSelection = true
tableView.isPrefetchingEnabled = true
tableView.remembersLastFocusedIndexPath = false // 導致焦點自動移動
tableView.rowHeight = UITableView.automaticDimension
tableView.estimatedRowHeight = 342
tableView.contentInsetAdjustmentBehavior = .never
tableView.sectionHeaderTopPadding = 0
}
當設置為 tableView.remembersLastFocusedIndexPath = true 時候,問題不存在。但是從 SwiftUI 區域進行外部焦點控制會失效。
Simulator Screen Recording - tvOS 18.2 - 2025-01-13 at 04.15.00.mp4
@ViewBuilder func CocoaListView() -> some View {
CocoaList(data.epsList) { ep in
CocoaListEpView(ep)
}
.introspectTableView { tableView in
self.tableView = tableView
tableView.delegate = delegateHandler
tableView.allowsFocus = true
tableView.allowsSelection = true
tableView.isPrefetchingEnabled = true
tableView.remembersLastFocusedIndexPath = true // 導致焦點自動移動
tableView.rowHeight = UITableView.automaticDimension
tableView.estimatedRowHeight = 342
tableView.contentInsetAdjustmentBehavior = .never
tableView.sectionHeaderTopPadding = 0
if data.epsList.count > 3 {
// Add footer view with BackTopBar
let footerView = UIHostingController(rootView:
ListBackTopBar { scrollTo(ep: data.epsList.first) { } }
)
footerView.view.backgroundColor = .clear
footerView.view.frame = CGRect(
x: 0,
y: 0,
width: tableView.frame.width,
height: appSetting.playlistIssueFocus ? 650 : 50
)
tableView.tableFooterView = footerView.view
}
}
//來自 SwiftUI 位置的
ButtonCard(symbol: "eyeglasses") {
scrollTo(ep: lastWatchedEp) { }
}
func scrollTo(ep: UniversalEpisodes.ep?, completionHandler: @escaping () -> Void) {
if data.epsList.isEmpty {
print(#function, "list is empty")
completionHandler()
return
}
guard
let ep = ep,
let epIndex = data.epsList.firstIndex(of: ep)
else {
print(#function, "ep is nil")
completionHandler()
return
}
if delegateHandler.focusedIndex == epIndex {
print(#function, "focusedIndex == epIndex")
completionHandler()
return
}
// Start ...
var row: Int = epIndex
if row >= data.epsList.count - 1 {
row = data.epsList.count - 1
}
if row < 0 {
row = 0
}
let indexPath = IndexPath(row: row, section: 0)
DispatchQueue.main.async {
tableView?.remembersLastFocusedIndexPath = false
// 强制更新布局
self.tableView?.setNeedsLayout()
self.tableView?.layoutIfNeeded()
tableView?.scrollToRow(at: indexPath, at: .top, animated: false)
// 直接计算目标位置
if let rect = tableView?.rectForRow(at: indexPath) {
// 偏移列表到指定位置
let targetOffset = CGPoint(x: 0, y: rect.origin.y)
tableView?.setContentOffset(targetOffset, animated: false)
}
if focusedField != .equalsID("CocoaListEpView") {
focusedField = .equalsID("CocoaListEpView")
}
DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) {
tableView?.becomeFirstResponder()
tableView?.setNeedsFocusUpdate()
tableView?.updateConstraintsIfNeeded()
if let cell = tableView?.cellForRow(at: indexPath) {
cell.setNeedsFocusUpdate()
cell.updateFocusIfNeeded()
} else {
print(#function, "not found cell")
}
print(#function, "callback", focusedField ?? "focus-nil")
tableView?.remembersLastFocusedIndexPath = true
completionHandler()
}
}
}
是可以正常使用 scrollTo 到列表頂部成員。
是無法使用 ScrollTo 函數到達指定列表成員。