OnTapGestureModifier.swift 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117
  1. /// A type of tap gesture.
  2. public struct TapGesture: Sendable, Hashable {
  3. @_spi(Backends) public var kind: TapGestureKind
  4. /// The idiomatic "primary" interaction for the device, such as a left-click
  5. /// with the mouse or normal tap on a touch screen.
  6. public static let primary = TapGesture(kind: .primary)
  7. /// The idiomatic "secondary" interaction for the device, such as a
  8. /// right-click with the mouse or long press on a touch screen.
  9. public static let secondary = TapGesture(kind: .secondary)
  10. /// A long press of the same interaction type as ``primary``.
  11. ///
  12. /// May be equivalent to ``secondary`` on some backends, particularly on
  13. /// mobile devices.
  14. public static let longPress = TapGesture(kind: .longPress)
  15. @_spi(Backends) public enum TapGestureKind: Hashable, Sendable {
  16. case primary
  17. case secondary
  18. case longPress
  19. }
  20. }
  21. extension View {
  22. /// Adds an action to perform when the user taps or clicks this view.
  23. ///
  24. /// Any tappable elements within the view will no longer be tappable with
  25. /// the same gesture type.
  26. ///
  27. /// - Parameters:
  28. /// - gesture: The type of gesture to listen for.
  29. /// - action: The action to perform.
  30. public func onTapGesture(
  31. gesture: TapGesture = .primary,
  32. perform action: @escaping () -> Void
  33. ) -> some View {
  34. OnTapGestureModifier(body: TupleView1(self), gesture: gesture, action: action)
  35. }
  36. /// Adds an action to run when this view is clicked. Any clickable elements
  37. /// within the view will no longer be clickable.
  38. @available(*, deprecated, renamed: "onTapGesture(gesture:perform:)")
  39. public func onClick(perform action: @escaping () -> Void) -> some View {
  40. onTapGesture(perform: action)
  41. }
  42. }
  43. struct OnTapGestureModifier<Content: View>: TypeSafeView {
  44. typealias Children = TupleView1<Content>.Children
  45. var body: TupleView1<Content>
  46. var gesture: TapGesture
  47. var action: () -> Void
  48. func children<Backend: BaseAppBackend>(
  49. backend: Backend,
  50. snapshots: [ViewGraphSnapshotter.NodeSnapshot]?,
  51. environment: EnvironmentValues
  52. ) -> Children {
  53. body.children(
  54. backend: backend,
  55. snapshots: snapshots,
  56. environment: environment
  57. )
  58. }
  59. func asWidget<Backend: BaseAppBackend>(
  60. _ children: Children,
  61. backend: Backend
  62. ) -> Backend.Widget {
  63. func castBackend_inner<NewBackend: BaseAppBackend & BackendFeatures.TapGestures>(_ backend: NewBackend) -> NewBackend.Widget {
  64. return backend.createTapGestureTarget(wrapping: children.child0.widget.into(), gesture: gesture)
  65. }
  66. guard let castedBackend = backend as? any (BaseAppBackend & BackendFeatures.TapGestures) else {
  67. fatalError("Backend does not implement BackendFeatures.TapGestures")
  68. }
  69. return castBackend_inner(castedBackend) as! Backend.Widget
  70. }
  71. func computeLayout<Backend: BaseAppBackend>(
  72. _ widget: Backend.Widget,
  73. children: Children,
  74. proposedSize: ProposedViewSize,
  75. environment: EnvironmentValues,
  76. backend: Backend
  77. ) -> ViewLayoutResult {
  78. children.child0.computeLayout(
  79. with: body.view0,
  80. proposedSize: proposedSize,
  81. environment: environment
  82. )
  83. }
  84. func commit<Backend: BaseAppBackend>(
  85. _ widget: Backend.Widget,
  86. children: TupleView1<Content>.Children,
  87. layout: ViewLayoutResult,
  88. environment: EnvironmentValues,
  89. backend: Backend
  90. ) {
  91. func castBackend_inner<NewBackend: BaseAppBackend & BackendFeatures.TapGestures>(_ backend: NewBackend) {
  92. let widget = widget as! NewBackend.Widget
  93. let size = children.child0.commit().size
  94. backend.setSize(of: widget, to: size.vector)
  95. backend.updateTapGestureTarget(
  96. widget,
  97. gesture: gesture,
  98. environment: environment,
  99. action: action
  100. )
  101. }
  102. guard let castedBackend = backend as? any (BaseAppBackend & BackendFeatures.TapGestures) else {
  103. fatalError("Backend does not implement BackendFeatures.TapGestures")
  104. }
  105. castBackend_inner(castedBackend)
  106. }
  107. }