mirror of
https://github.com/ARMSX2/ARMSX2.git
synced 2026-08-24 16:50:16 -07:00
iOS: give the menu reveal tap a surface that works on iOS 27
The reveal tap moves onto a clear SwiftUI shape overlaid on the game view, and the dynamic input zones report their classified taps through a notification the game screen listens for. The old gesture sat on the Metal render view, which iOS 27 makes non interactive so the SwiftUI overlays own touch. On that OS the tap could never fire, and once the controller poll was gone there was no path left to bring a hidden menu button back. The zones matter for the same reason: with dynamic thumbsticks or swipe camera on they tile the whole landscape screen and eat every tap before it reaches anything below.
This commit is contained in:
@@ -118,6 +118,8 @@ struct DynamicThumbstickView: View {
|
||||
var onInteractionActivity: () -> Void = {}
|
||||
var onInteractionTap: () -> Void = {}
|
||||
var onInteractionEnded: () -> Void = {}
|
||||
// Fires on every classified tap, unlike onInteractionTap which needs tap actions on.
|
||||
var onAnyTap: () -> Void = {}
|
||||
|
||||
@State private var origin = CGPoint.zero
|
||||
@State private var dragOffset = CGSize.zero
|
||||
@@ -194,6 +196,7 @@ struct DynamicThumbstickView: View {
|
||||
let duration = value.time.timeIntervalSince(gestureStartedAt ?? value.time)
|
||||
let travel = max(maximumTravel, hypot(value.translation.width, value.translation.height))
|
||||
if duration <= maximumTapDuration && travel <= tapTravelTolerance {
|
||||
onAnyTap()
|
||||
if tapActionsEnabled {
|
||||
onInteractionTap()
|
||||
} else {
|
||||
|
||||
@@ -356,6 +356,7 @@ struct GameScreenView: View {
|
||||
.accessibilityLabel("Game display")
|
||||
.accessibilityAddTraits(.isImage)
|
||||
.accessibilityHint("VoiceOver image recognition can read on-screen text.")
|
||||
.overlay { menuRevealTapCatcher }
|
||||
AccessibilityHUDMirror()
|
||||
if effectiveVirtualPadVisible {
|
||||
VirtualControllerView(
|
||||
@@ -390,6 +391,7 @@ struct GameScreenView: View {
|
||||
.accessibilityHint("VoiceOver image recognition can read on-screen text.")
|
||||
.overlay {
|
||||
ZStack {
|
||||
menuRevealTapCatcher
|
||||
AccessibilityHUDMirror()
|
||||
dynamicCrosshairOverlay
|
||||
}
|
||||
@@ -594,6 +596,9 @@ struct GameScreenView: View {
|
||||
padRebuildToken &+= 1
|
||||
overlayRoute = .paused
|
||||
}
|
||||
.onReceive(NotificationCenter.default.publisher(for: gameplaySurfaceTapNotification)) { _ in
|
||||
revealMenuButtonBriefly()
|
||||
}
|
||||
.onReceive(NotificationCenter.default.publisher(for: retroAchievementsToastNotification)) { _ in
|
||||
consumePendingRetroAchievementsToast()
|
||||
}
|
||||
@@ -793,6 +798,14 @@ struct GameScreenView: View {
|
||||
.gameplayLaunchChrome(visible: appState.gameplayLaunchControlsVisible)
|
||||
}
|
||||
|
||||
// The render view is non-interactive on iOS 27, so the reveal tap needs a SwiftUI surface.
|
||||
private var menuRevealTapCatcher: some View {
|
||||
Color.clear
|
||||
.contentShape(Rectangle())
|
||||
.onTapGesture { revealMenuButtonBriefly() }
|
||||
.accessibilityHidden(true)
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
private var menuButtonLabel: some View {
|
||||
// Always-rendered SF Symbol mark. A loose PNG was previously loaded here, but
|
||||
|
||||
@@ -4,6 +4,10 @@
|
||||
import SwiftUI
|
||||
import UIKit
|
||||
|
||||
// Posted when a dynamic input zone classifies a touch as a tap, so the game
|
||||
// screen can reveal a hidden menu button the zones would otherwise swallow.
|
||||
let gameplaySurfaceTapNotification = Notification.Name("ARMSX2iOSGameplaySurfaceTap")
|
||||
|
||||
// Lazily-created haptic generators reused for button presses and explicitly releasable
|
||||
// when the gameplay UI is removed.
|
||||
@MainActor
|
||||
@@ -567,6 +571,7 @@ struct VirtualControllerView: View {
|
||||
}
|
||||
},
|
||||
onTap: {
|
||||
NotificationCenter.default.post(name: gameplaySurfaceTapNotification, object: nil)
|
||||
if dynamicSettings.rightThumbstickActionsEnabled {
|
||||
activeTouchActionSession.right.interactionTapped()
|
||||
}
|
||||
@@ -644,7 +649,8 @@ struct VirtualControllerView: View {
|
||||
onInteractionBegan: { actionController.interactionBegan() },
|
||||
onInteractionActivity: { actionController.interactionActivity() },
|
||||
onInteractionTap: { actionController.interactionTapped() },
|
||||
onInteractionEnded: { actionController.interactionEnded() }
|
||||
onInteractionEnded: { actionController.interactionEnded() },
|
||||
onAnyTap: { NotificationCenter.default.post(name: gameplaySurfaceTapNotification, object: nil) }
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user