crossmate

A collaborative crossword app for iOS
Log | Files | Refs | LICENSE

commit 46de4d3cfe45b6a28d8d18ca2ef83021b7b7060e
parent 3d26ea3bfed761a0051896e17bdb0a80044432e2
Author: Michael Camilleri <[email protected]>
Date:   Fri, 17 Apr 2026 18:42:44 +0900

Fix clue sliding

Clues are meant to slide horizontally in a manner that responds to the
forward or backward buttons that the user presses. This commit is an
attempt to eliminate animation artifacts.

Co-Authored-By: Claude Opus 4.7 <[email protected]>

Diffstat:
MCrossmate/Views/PuzzleView.swift | 46+++++++++++++++++++++++-----------------------
1 file changed, 23 insertions(+), 23 deletions(-)

diff --git a/Crossmate/Views/PuzzleView.swift b/Crossmate/Views/PuzzleView.swift @@ -183,17 +183,17 @@ private struct ClueKey: Hashable { private struct ClueBar: View { @Bindable var session: PlayerSession @Environment(PlayerPreferences.self) private var preferences - @State private var previousKey: ClueKey? + @State private var slideEdge: Edge = .trailing private var playerColor: PlayerColor { preferences.color } var body: some View { let clue = session.currentClue() let currentKey = clue.map { ClueKey(direction: session.direction, number: $0.number) } - let slideEdge = slideEdge(from: previousKey, to: currentKey) HStack(alignment: .clueCenter, spacing: 12) { Button { + slideEdge = .leading session.goToPreviousClue() } label: { Image(systemName: "chevron.left") @@ -207,21 +207,30 @@ private struct ClueBar: View { .font(.caption) .textCase(.uppercase) .foregroundStyle(.secondary) - Text(clue?.text ?? "—") - .font(.headline) - .lineLimit(2) - .multilineTextAlignment(.leading) - .alignmentGuide(.clueCenter) { d in d[VerticalAlignment.center] } - .id(currentKey) - .transition(.asymmetric( - insertion: .move(edge: slideEdge), - removal: .move(edge: slideEdge == .trailing ? .leading : .trailing) - )) + ZStack(alignment: .leading) { + Text("X\nX") + .font(.headline) + .lineLimit(2) + .hidden() + .accessibilityHidden(true) + Text(clue?.text ?? "—") + .font(.headline) + .lineLimit(2) + .multilineTextAlignment(.leading) + .frame(maxWidth: .infinity, alignment: .leading) + .id(currentKey) + .transition(.asymmetric( + insertion: .move(edge: slideEdge), + removal: .move(edge: slideEdge == .trailing ? .leading : .trailing) + )) + } + .alignmentGuide(.clueCenter) { d in d[VerticalAlignment.center] } + .frame(maxWidth: .infinity, alignment: .leading) + .clipped() } - .frame(maxWidth: .infinity, alignment: .leading) - .clipped() Button { + slideEdge = .trailing session.goToNextClue() } label: { Image(systemName: "chevron.right") @@ -234,15 +243,6 @@ private struct ClueBar: View { .padding(.vertical, 12) .background(playerColor.highlightFill) .animation(.smooth(duration: 0.22), value: currentKey) - .onChange(of: currentKey) { _, newValue in - previousKey = newValue - } - } - - private func slideEdge(from prev: ClueKey?, to curr: ClueKey?) -> Edge { - guard let prev, let curr else { return .trailing } - if prev.direction != curr.direction { return .trailing } - return curr.number > prev.number ? .trailing : .leading } private func label(for clue: Puzzle.Clue?) -> String {