tvOS - UICollectionView 在呈现另一个 VC 后重新加载数据时重置焦点

问题描述 投票:0回答:1

在某些情况下,在 tvOS 上,集合视图会将其焦点重置到

reloadData()
。下面是重现问题的最少代码。

import SwiftUI

@main
struct DemosApp_tvOS: App {
    var body: some Scene {
        WindowGroup {
            HomeView()
        }
    }
}


public struct HomeView: UIViewControllerRepresentable {
    public func makeUIViewController(context: Context) -> HomeViewController { .init() }

    public func updateUIViewController(_ uiViewController: HomeViewController, context: Context) { }
}

public final class HomeViewController: UIViewController {
    public override func viewDidAppear(_ animated: Bool) {
        DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) {
            self.present(CollectionViewController(collectionViewLayout: UICollectionViewFlowLayout()), animated: true)
        }
    }
}

public final class CollectionViewController: UICollectionViewController {
    public override func viewDidLoad() {
        collectionView.register(UICollectionViewCell.self, forCellWithReuseIdentifier: "Cell")
    }

    public override func pressesEnded(_ presses: Set<UIPress>, with event: UIPressesEvent?) {
        super.pressesEnded(presses, with: event)
        if presses.contains(where: { $0.type == .playPause }) {
            collectionView.reloadData()
        }
    }

    public override func numberOfSections(in collectionView: UICollectionView) -> Int { 1 }

    public override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { 7 }

    public override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath)
        cell.backgroundColor = .gray
        cell.layer.borderColor = UIColor.blue.cgColor
        return cell
    }

    public override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
        present(UIViewController(), animated: true)
    }

    public override func didUpdateFocus(in context: UIFocusUpdateContext, with coordinator: UIFocusAnimationCoordinator) {
        context.previouslyFocusedView?.layer.borderWidth = 0
        context.nextFocusedView?.layer.borderWidth = 4
    }
}

基本上需要满足以下条件:

  • 集合视图 VC 呈现在另一个 VC 之上(例如,许多应用程序具有的主屏幕)
  • 用户选择第一个项目之外的任何项目
  • 从集合视图中选择一个项目会呈现另一个 VC(我们称之为细节 VC)
  • 用户忽略了详细信息 VC
  • 返回集合视图屏幕后,某些事件会触发
    reloadData
    调用(在上面的示例中,按下远程播放/暂停按钮将重新加载集合视图)。

reloadData()
调用发生之前,焦点正确地保留在所选项目上。

但是,一旦发生

reloadData()
调用,焦点就会重置到第一个单元格。这种情况仅在从细节 VC 返回后第一次发生。

后续数据重新加载不会重置焦点,唯一有问题的调用是详细信息 VC 被关闭后的第一次重新加载。

此外,如果集合视图 VC 不是以模态方式呈现在另一个 VC 上,则不会发生重新加载问题。

有人知道这个问题的解决方案或解决方法吗?一种不涉及重新设计 UI 层次结构的方法。


附注我尝试使用

shouldUpdateFocus(in:)
indexPathForPreferredFocusedView(in:)
委托方法,但没有成功。

swift uicollectionview focus tvos
1个回答
0
投票

我就此提交了一份 Apple 报告 (FB13262879),同时我搜索了解决方法,并找到了一个。

解决方法涉及设置

collectionView.remembersLastFocusedIndexPath = false
,以及“手动”管理集合视图焦点:

private var lastFocusedIndexPath: IndexPath?
public func collectionView(_ collectionView: UICollectionView, didUpdateFocusIn context: UICollectionViewFocusUpdateContext, with coordinator: UIFocusAnimationCoordinator) {
    lastFocusedIndexPath = context.nextFocusedIndexPath
}

public func indexPathForPreferredFocusedView(in collectionView: UICollectionView) -> IndexPath? {
    return lastFocusedIndexPath
}

最后一个难题是强制集合视图在错误重置焦点后恢复焦点:

let indexPathToRestoreFocus = lastFocusedIndexPath
collectionView.reloadData()
CATransaction.setCompletionBlock { [self] in
    lastFocusedIndexPath = indexPathToRestoreFocus
    collectionView.setNeedsFocusUpdate()
    collectionView.updateFocusIfNeeded()
}

这不是一个很好的解决方法,并且将来可能会崩溃,希望 Apple 届时修复 UIKit 错误。

© www.soinside.com 2019 - 2024. All rights reserved.