tvOS上的SwiftUI:如何知道在列表中选择了哪个项目

时间:2019-10-06 01:20:18

标签: swiftui tvos

在列表中,我需要知道选择了哪个项目,并且该项目必须是可单击的。

这是我尝试做的事情:

| item1 | info of the item3 (selected) |
| item2 |                              |
|*item3*|                              |
| item4 |                              |

我可以使用.focusable()来实现它,但是它不可点击。

ButtonNavigationLink可以使用,但是我无法选择当前项目。

当您使用ButtonNavigationLink .focusable时,请不要再打了。

所以我的问题是:

我如何选择当前项目(以便我可以显示有关此项目的更多信息)并使之可单击以显示下一个视图?

示例代码1 :可聚焦的作品,但是.onTap在tvOS上不存在

import SwiftUI

struct TestList: Identifiable {
    var id: Int
    var name: String
}

let testData = [Int](0..<50).map { TestList(id: $0, name: "Row \($0)")  }


struct SwiftUIView : View {
    var testList: [TestList]

    var body: some View {
        List {
            ForEach(testList) { txt in
                TestRow(row: txt)
            }
        }
    }
}

struct TestRow: View {
    var row: TestList

    @State private var backgroundColor = Color.clear

    var body: some View {
        Text(row.name)
        .focusable(true) { isFocused in
            self.backgroundColor = isFocused ? Color.green : Color.blue
            if isFocused {
                print(self.row.name)
            }
        }
        .background(self.backgroundColor)
    }
}

示例代码2 :可通过NavigationLink单击项目,但无法获取所选项目,并且不再调用.focusable

import SwiftUI

struct TestList: Identifiable {
    var id: Int
    var name: String
}

let testData = [Int](0..<50).map { TestList(id: $0, name: "Row \($0)")  }


struct SwiftUIView : View {
    var testList: [TestList]

    var body: some View {
        NavigationView {
            List {
                ForEach(testList) { txt in
                    NavigationLink(destination: Text("Destination")) {
                        TestRow(row: txt)
                    }
                }
            }
        }
    }
}

struct TestRow: View {
    var row: TestList

    @State private var backgroundColor = Color.clear

    var body: some View {
        Text(row.name)
        .focusable(true) { isFocused in
            self.backgroundColor = isFocused ? Color.green : Color.blue
            if isFocused {
                print(self.row.name)
            }
        }
        .background(self.backgroundColor)
    }
}

2 个答案:

答案 0 :(得分:1)

在我看来,您似乎无法在swiftui中为tvos附加click事件,这似乎是一个主要的现场活动。我提出了一个可让您将大多数swiftui组件设为可选择和可点击的hack方法。希望对您有所帮助。

首先,我需要制作一个捕获事件的UIView。

echo "A* B*" |xargs -n 1 find -name | xargs grep "whatImLookingFor" <placeholder_for_stdin> -r

可点击的委托人:

echo "A* B*" |xargs -n 1 find -name | xargs someCommand withOptions <stdin_here> andSomeOtherOptions

然后为我的视图添加swiftui扩展名

    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
        }
    }

然后,我制作一个友好的swiftui包装器,这样我就可以传入我想使其可聚焦和可单击的任何类型的组件

protocol ClickableHackDelegate: class {
    func focus(focused: Bool)
    func clicked()
}

用法示例:

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()
        }
    }
}

答案 1 :(得分:0)

不幸的是,我对tvOS上的自定义按钮样式不太满意。

但是,要在tvOS上的SwiftUI中创建可聚焦的,可选的自定义视图,可以将按钮样式设置为普通样式。这样,当您提供目标位置和自定义布局时,就可以保留系统提供的精美焦点和选择动画。只需将.buttonStyle(PlainButtonStyle())修饰符添加到您的NavigationLink

struct VideoCard: View {
    var body: some View {
        NavigationLink(
            destination: Text("Video player")
        ) {
            VStack(alignment: .leading, spacing: .zero) {
                Image(systemName: "film")
                    .frame(width: 356, height: 200)
                    .background(Color.white)
                Text("Video Title")
                    .foregroundColor(.white)
                    .padding(10)
            }
            .background(Color.primary)
            .frame(maxWidth: 400)
        }
        .buttonStyle(PlainButtonStyle())
    }
}

Here's a screenshot of what it looks like in the simulator.

单击Siri遥控器上的按钮,或者按Enter或键盘,应该可以正常工作。