首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >SwiftUI按钮tvOS+iOS操作对iOS有效,而不是在tvOS上

SwiftUI按钮tvOS+iOS操作对iOS有效,而不是在tvOS上
EN

Stack Overflow用户
提问于 2021-09-06 12:58:43
回答 1查看 119关注 0票数 1

我正在尝试为iOS和tvOS共享单个swiftUI按钮视图。对于iOS来说,一切都像预期的那样工作,但是tvOS没有触发按钮动作。

代码语言:javascript
复制
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中构建按钮类):

代码语言:javascript
复制
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视图中像这样使用这个按钮:

代码语言:javascript
复制
ButtonView(label: "Primary", mode: .Primary, tapEvent: { print("Tapped") })

问题是,在iOS上,按钮操作是在轻敲时执行的,但在tvOS上,按钮轻敲事件不会执行。

更新:我发现在删除cornerRadius和修饰符时,按钮事件确实会被触发。如果按钮上存在cornerRadius或modifier,则tvOS不再希望响应按钮轻击。不过,我仍然需要修饰符和cornerRadius。

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2021-09-06 13:23:54

代码中的问题是focusable修饰符阻塞了点击。

为了解决这个问题,你可以从头开始重新实现按钮。我受this answer的启发创建了CustomButton:它将是iOS上的一个普通按钮,在苹果电视上是一个自定义按钮:

代码语言:javascript
复制
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()
        }
    }
}

用法:

代码语言:javascript
复制
CustomButton(action: {
    print("clicked 1")
}) {
    Text("Clickable 1")
}
CustomButton(action: {
    print("clicked 2")
}) {
    Text("Clickable 2")
}

结果:

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/69074961

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档