我正在尝试为iOS和tvOS共享单个swiftUI按钮视图。对于iOS来说,一切都像预期的那样工作,但是tvOS没有触发按钮动作。
var body: some View {
Button(action: tapEvent) {
HStack {
if let image = icon, let uiimage = UIImage(named: image) {
Image(uiImage: uiimage)
.resizable()
.renderingMode(.template)
.foregroundColor(iconColorForState(active: active, buttonMode: mode))
.frame(width: 20, height: 20, alignment: .center)
}
if let title = label {
Text(title)
.lineLimit(1)
}
}
}
.buttonStyle(PlainButtonStyle())
.foregroundColor(foregroundColorForState(active: active, buttonMode: mode))
.padding()
.disabled(disabled)
.background(self.focused ? focussedBackgroundColorFor(active: active, mode: mode) : backgroundColorFor(active: active, mode: mode))
.frame(height: heightForButtonSize(size: size ?? .Base))
.cornerRadius(8)
.modifier(FocusableModifier { focused in
self.focused = focused
})
}FocusableModifier (允许在iOS和tvOS中构建按钮类):
struct FocusableModifier: ViewModifier {
private let isFocusable: Bool
private let onFocusChange: (Bool) -> Void
init (_ isFocusable: Bool = true, onFocusChange: @escaping (Bool) -> Void = { _ in }) {
self.isFocusable = isFocusable
self.onFocusChange = onFocusChange
}
@ViewBuilder
func body(content: Content) -> some View {
#if os(tvOS)
content.focusable(isFocusable, onFocusChange: onFocusChange)
#else
content
#endif
}
}然后我在我的iOS和tvOs视图中像这样使用这个按钮:
ButtonView(label: "Primary", mode: .Primary, tapEvent: { print("Tapped") })问题是,在iOS上,按钮操作是在轻敲时执行的,但在tvOS上,按钮轻敲事件不会执行。
更新:我发现在删除cornerRadius和修饰符时,按钮事件确实会被触发。如果按钮上存在cornerRadius或modifier,则tvOS不再希望响应按钮轻击。不过,我仍然需要修饰符和cornerRadius。
发布于 2021-09-06 13:23:54
代码中的问题是focusable修饰符阻塞了点击。
为了解决这个问题,你可以从头开始重新实现按钮。我受this answer的启发创建了CustomButton:它将是iOS上的一个普通按钮,在苹果电视上是一个自定义按钮:
struct CustomButton<Content>: View where Content : View {
@State
private var focused = false
@State
private var pressed = false
let action: () -> Void
@ViewBuilder
let content: () -> Content
var body: some View {
contentView
.background(focused ? Color.green : .yellow)
.cornerRadius(20)
.scaleEffect(pressed ? 1.1 : 1)
.animation(.default, value: pressed)
}
var contentView: some View {
#if os(tvOS)
ZStack {
ClickableHack(focused: $focused, pressed: $pressed, action: action)
content()
.padding()
.layoutPriority(1)
}
#else
Button(action: action, label: content)
#endif
}
}
class ClickableHackView: UIView {
weak var delegate: ClickableHackDelegate?
override init(frame: CGRect) {
super.init(frame: frame)
}
override func pressesBegan(_ presses: Set<UIPress>, with event: UIPressesEvent?) {
if validatePress(event: event) {
delegate?.pressesBegan()
} else {
super.pressesBegan(presses, with: event)
}
}
override func pressesEnded(_ presses: Set<UIPress>, with event: UIPressesEvent?) {
if validatePress(event: event) {
delegate?.pressesEnded()
} else {
super.pressesEnded(presses, with: event)
}
}
override func pressesCancelled(_ presses: Set<UIPress>, with event: UIPressesEvent?) {
if validatePress(event: event) {
delegate?.pressesEnded()
} else {
super.pressesCancelled(presses, with: event)
}
}
private func validatePress(event: UIPressesEvent?) -> Bool {
event?.allPresses.map({ $0.type }).contains(.select) ?? false
}
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
}
}
protocol ClickableHackDelegate: AnyObject {
func focus(focused: Bool)
func pressesBegan()
func pressesEnded()
}
struct ClickableHack: UIViewRepresentable {
@Binding var focused: Bool
@Binding var pressed: Bool
let action: () -> 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 pressesBegan() {
control.pressed = true
}
func pressesEnded() {
control.pressed = false
control.action()
}
}
}用法:
CustomButton(action: {
print("clicked 1")
}) {
Text("Clickable 1")
}
CustomButton(action: {
print("clicked 2")
}) {
Text("Clickable 2")
}结果:

https://stackoverflow.com/questions/69074961
复制相似问题