如何在 SwiftUI tvOS 中检测 'Click' 手势
How to detect a 'Click' gesture in 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: {
[=11=].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 上的可聚焦 运行 如果我向其添加圆角半径,但默认的点击操作不会 运行
- 此外,TapGesture 在 tvOS 中不可用
既然可以使用手势,也许有一种我无法弄清楚的使用手势的方法。
或
如果有一种方法可以在按钮上实现可聚焦(尽管这是不太受欢迎的选择,因为这会改变我想要实现的外观)。
这是可能的,但不适合胆小的人。我想出了一个可能对您有所帮助的通用解决方案。我希望在下一个 swiftUI 更新中,Apple 可以添加一种更好的方法来为 tvOS 附加点击事件,并且这段代码可以归入它所属的垃圾箱。
如何执行此操作的高级解释是创建一个 UIView 来捕获焦点和单击事件,然后创建一个 UIViewRepresentable,以便 swiftUI 可以使用该视图。然后视图被添加到 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({ [=10=].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
}
}
~
可点击代表:
protocol ClickableHackDelegate: class {
func focus(focused: Bool)
func clicked()
}
然后为我的视图做一个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)
}
}
}
}
如果您想避免 UIKit
,您可以通过 Long Press Gesture
设置非常短的按压持续时间来实现所需的解决方案。
1。只有 Press
:
如果你只需要处理按压动作,完全不需要长按
ContentView()
.onLongPressGesture(minimumDuration: 0.01, pressing: { _ in }) {
print("pressed")
}
2。 Press
和 Long press
:
如果你需要同时处理按下和长按。
var longPress: some Gesture {
LongPressGesture(minimumDuration: 0.5)
.onEnded { _ in
print("longpress")
}
}
ContentView()
.highPriorityGesture(longPress)
.onLongPressGesture(minimumDuration: 0.01, pressing: { _ in }) {
print("press")
}
使用:
- 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: {
[=11=].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 上的可聚焦 运行 如果我向其添加圆角半径,但默认的点击操作不会 运行
- 此外,TapGesture 在 tvOS 中不可用
既然可以使用手势,也许有一种我无法弄清楚的使用手势的方法。
或
如果有一种方法可以在按钮上实现可聚焦(尽管这是不太受欢迎的选择,因为这会改变我想要实现的外观)。
这是可能的,但不适合胆小的人。我想出了一个可能对您有所帮助的通用解决方案。我希望在下一个 swiftUI 更新中,Apple 可以添加一种更好的方法来为 tvOS 附加点击事件,并且这段代码可以归入它所属的垃圾箱。
如何执行此操作的高级解释是创建一个 UIView 来捕获焦点和单击事件,然后创建一个 UIViewRepresentable,以便 swiftUI 可以使用该视图。然后视图被添加到 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({ [=10=].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
}
}
~ 可点击代表:
protocol ClickableHackDelegate: class {
func focus(focused: Bool)
func clicked()
}
然后为我的视图做一个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)
}
}
}
}
如果您想避免 UIKit
,您可以通过 Long Press Gesture
设置非常短的按压持续时间来实现所需的解决方案。
1。只有 Press
:
如果你只需要处理按压动作,完全不需要长按
ContentView()
.onLongPressGesture(minimumDuration: 0.01, pressing: { _ in }) {
print("pressed")
}
2。 Press
和 Long press
:
如果你需要同时处理按下和长按。
var longPress: some Gesture {
LongPressGesture(minimumDuration: 0.5)
.onEnded { _ in
print("longpress")
}
}
ContentView()
.highPriorityGesture(longPress)
.onLongPressGesture(minimumDuration: 0.01, pressing: { _ in }) {
print("press")
}