swift - 如何在 SwiftUI tvOS 中检测 'Click' 手势

标签 swift swiftui tvos

使用:

  • SwiftUI
  • swift 5
  • tvOS
  • Xcode 版本 11.2.1

  • 我只想检测下面 URLImage 上的点击手势

    JFYI 我对 Xcode、Swift 和 SwiftUI 非常陌生(不到 3 周)。
    URLImage(URL(string: channel.thumbnail)!,
                     delay: 0.25,
                     processors: [ Resize(size: CGSize(width:isFocused ?  300.0 : 225.0, height:isFocused ?  300.0 : 225.0), scale: UIScreen.main.scale) ],
                     content:  {
                        $0.image
                            .resizable()
                            .aspectRatio(contentMode: .fill)
                            .clipped()
            })
                .frame(width: isFocused ?  300.0 : 250.0, height:isFocused ?  300.0 : 250.0)
                .clipShape(Circle())
                .overlay(
                    Circle().stroke( isFocused ? Color.white : Color.black, lineWidth: 8))
                .shadow(radius:5)
                .focusable(true, onFocusChange:{ (isFocused) in
                    withAnimation(.easeInOut(duration:0.3)){
                        self.isFocused = isFocused
                    }
                    if(isFocused){
                        self.manager.bannerChannel = self.channel
                        print(self.manager.bannerChannel)
                        self.manager.loadchannelEPG(id: self.channel.id)
                    }
                })
                .padding(20)
        }
    
  • 我发现的唯一解决方法是将其包装在 NavigationLink
    或 Button 但随后可聚焦在该按钮上的按钮不会运行。
  • 我发现如果我向其添加角半径,则可聚焦在 Button/NavigationLink 上运行,但默认单击操作不会运行
  • 此外,tvOS 中不提供 TapGesture

  • 由于手势可用,也许有一种我无法弄清楚的使用手势的方法。

    或者

    如果有一种方法可以在按钮上获取焦点(尽管这是不太受欢迎的选择,因为这会改变我想要实现的外观)。

    最佳答案

    这是可能的,但不适合胆小的人。我想出了一个可能对您有所帮助的通用解决方案。我希望在下一次 swiftUI 更新中,Apple 添加了一种更好的方法来为 tvOS 附加点击事件,并且此代码可以归入它所属的垃圾箱。
    如何做到这一点的高级解释是创建一个捕获焦点和单击事件的 UIView,然后创建一个 UIViewRepresentable 以便 swiftUI 可以使用该 View 。然后 View 被添加到 ZStack 中的布局中,因此它被隐藏了,但是您可以接收焦点并响应单击事件,就好像用户真的在与您真正的 swiftUI 组件进行交互一样。
    首先,我需要制作一个 UIView 来捕获事件。

    class ClickableHackView: UIView {
        weak var delegate: ClickableHackDelegate?
        
        override init(frame: CGRect) {
            super.init(frame: frame)        
        }
    
        override func pressesEnded(_ presses: Set<UIPress>, with event: UIPressesEvent?) {
            if event?.allPresses.map({ $0.type }).contains(.select) ?? false {
                delegate?.clicked()
            } else {
                superview?.pressesEnded(presses, with: event)
            }
        }
    
        override func didUpdateFocus(in context: UIFocusUpdateContext, with coordinator: UIFocusAnimationCoordinator) {
            delegate?.focus(focused: isFocused)
        }
    
        required init?(coder: NSCoder) {
            fatalError("init(coder:) has not been implemented")
        }
    
        override var canBecomeFocused: Bool {
            return true
        }
    }
    
    ~
    可点击的委托(delegate):
    protocol ClickableHackDelegate: class {
        func focus(focused: Bool)
        func clicked()
    }
    
    然后为我的 View 做一个 swiftui 扩展
    struct ClickableHack: UIViewRepresentable {
        @Binding var focused: Bool
        let onClick: () -> Void
        
        func makeUIView(context: UIViewRepresentableContext<ClickableHack>) -> UIView {
            let clickableView = ClickableHackView()
            clickableView.delegate = context.coordinator
            return clickableView
        }
        
        func updateUIView(_ uiView: UIView, context: UIViewRepresentableContext<ClickableHack>) {
        }
        
        func makeCoordinator() -> Coordinator {
            return Coordinator(self)
        }
        
        class Coordinator: NSObject, ClickableHackDelegate {
            private let control: ClickableHack
            
            init(_ control: ClickableHack) {
                self.control = control
                super.init()
            }
            
            func focus(focused: Bool) {
                control.focused = focused
            }
            
            func clicked() {
                control.onClick()
            }
        }
    }
    
    然后我制作了一个更友好的 swiftui 包装器,这样我就可以传入任何我想要可聚焦和可点击的组件
    struct Clickable<Content>: View where Content : View {
        let focused: Binding<Bool>
        let content: () -> Content
        let onClick: () -> Void
        
        @inlinable public init(focused: Binding<Bool>, onClick: @escaping () -> Void, @ViewBuilder content: @escaping () -> Content) {
            self.content = content
            self.focused = focused
            self.onClick = onClick
        }
        
        var body: some View {
            ZStack {
                ClickableHack(focused: focused, onClick: onClick)
                content()
            }
        }
    }
    
    示例用法:
    struct ClickableTest: View {
        @State var focused1: Bool = false
        @State var focused2: Bool = false
        
        var body: some View {
            HStack {
                Clickable(focused: self.$focused1, onClick: {
                    print("clicked 1")
                }) {
                    Text("Clickable 1")
                        .foregroundColor(self.focused1 ? Color.red : Color.black)
                }
                Clickable(focused: self.$focused2, onClick: {
                    print("clicked 2")
                }) {
                    Text("Clickable 2")
                        .foregroundColor(self.focused2 ? Color.red : Color.black)
                }
            }
        }
    }
    

    关于swift - 如何在 SwiftUI tvOS 中检测 'Click' 手势,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59200396/

    相关文章:

    animation - 列表行平滑动画

    ios - SwiftUI withAnimation 完成回调

    swift - UIAlertAction 错误

    ios - 初始化程序不会在 swift 中从其父类(super class)错误中覆盖设计的初始化程序

    ios - macOS 上的 SwiftUI,如何使整行可点击?

    tvos - 如何为 tvOS 创建一个具有清晰背景颜色的 UIButton?

    ios - 在 tvOS 上对 Dropbox 进行身份验证

    ios - 嵌套的 AVPlayer 全屏?

    swift - 在循环内使用dispatch_after延迟

    ios - iPhone(iOS) 和 Apple TV (tvOS) 模拟器之间的通信