首页 > 解决方案 > 如何在 SwiftUI 中使 UIViewRepresentable 在 tvOS 上具有焦点?

问题描述

标题说明了一切 - 我无法专注UIViewRepresentable于 tvOS 上的 SwiftUI。任何符合View协议的视图都可以毫无问题地聚焦,但既UIViewRepresentable不能也UIViewControllerRepresentable不能。

有任何想法吗?

此代码有效:

struct ContentView: View {
    var body: some View {
        Text("Hello, World!")
            .focusable()
            .onExitCommand {
                print("Menu button pressed")
        }
    }
}

这个没有:

struct ContentView: View {
    var body: some View {
        ViewRepresentable()
            .focusable()
            .onExitCommand {
                print("Menu button pressed")
        }
    }
}

struct ViewRepresentable: UIViewRepresentable {
    func makeUIView(context: Context) -> UIView {
        let view = UIView()
        view.backgroundColor = .red

        return view
    }

    func updateUIView(_ uiView: UIView, context: Context) {

    }
}

谢谢!

标签: swiftuitvosapple-tvuiviewrepresentable

解决方案


您需要创建一个覆盖 canBecomeFocused 的 UIView 子类以返回 true。如果您想响应焦点,您可能还想覆盖 didUpdateFocus(in:with:) 。

作为参考,我制作了一个实用程序类,让我拥有可聚焦的 UIView,并响应远程滑动和点击。

import UIKit

@available (iOS, unavailable)
@available (macCatalyst, unavailable)
class UserInputView: UIView {
    weak var pressDelegate: PressableDelegate?
    weak var touchDelegate: TouchableDelegate?
    var touchStarted: CGFloat = 0
    var lastTouch: CGFloat = 0
    
    override init(frame: CGRect) {
        super.init(frame: frame)
    }

    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        touchStarted = touches.first?.location(in: nil).x ?? 0
        lastTouch = touchStarted
        touchDelegate?.touchDown()
    }
    
    override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
        let thisTouch = touches.first?.location(in: nil).x ?? 0
        touchDelegate?.touchMove(amount: Double((thisTouch - lastTouch)/(touches.first?.window?.frame.width ?? 1920)))
        lastTouch = touches.first?.location(in: nil).x ?? 0
    }
    
    override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
        touchDelegate?.touchUp()
    }

    override func pressesBegan(_ presses: Set<UIPress>, with event: UIPressesEvent?) {
        if isClick(presses) {
            pressDelegate?.press(pressed: true)
        } else {
            superview?.pressesBegan(presses, with: event)
        }
    }
    
    override func pressesEnded(_ presses: Set<UIPress>, with event: UIPressesEvent?) {
        if isClick(presses) {
            pressDelegate?.press(pressed: false)
            pressDelegate?.clicked()
        } else {
            superview?.pressesEnded(presses, with: event)
        }
    }
    
    private func isClick(_ presses: Set<UIPress>?) -> Bool {
        return presses?.map({ $0.type }).contains(.select) ?? false
    }
    
    override func didUpdateFocus(in context: UIFocusUpdateContext, with coordinator: UIFocusAnimationCoordinator) {
        pressDelegate?.focus(focused: isFocused)
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    override var canBecomeFocused: Bool {
        return true
    }
}

推荐阅读