Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

hugo/feature/Add DnDInOrder in new GEK #1619

Merged
merged 17 commits into from
Dec 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -36,14 +36,14 @@ repos:
exclude: '(\.vscode/settings\.json|\.jtd\.json$|.*\.xcassets/.*|.*\.colorset/.*|\.animation\.lottie\.json$)'

- repo: https://github.com/realm/SwiftLint
rev: 0.57.0
rev: 0.57.1
hooks:
- id: swiftlint
entry: swiftlint
args: ["--use-alternative-excluding"]

- repo: https://github.com/nicklockwood/SwiftFormat
rev: 0.54.5
rev: 0.55.3
hooks:
- id: swiftformat

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,19 @@ struct ContentView: View {
.buttonStyle(.borderedProminent)
.frame(maxWidth: .infinity)

NavigationLink("Drag & Drop One To One In Right Order", destination: {
let gameplay = NewGameplayFindTheRightOrder(choices: NewGameplayFindTheRightOrder.kDefaultImageChoicesWithZones)
let coordinator = DnDOneToOneCoordinatorFindTheRightOrder(gameplay: gameplay)
let viewModel = DnDOneToOneViewModel(coordinator: coordinator)

return DnDOneToOneView(viewModel: viewModel)
.navigationTitle("Drag & Drop One To One In Right Order")
.navigationBarTitleDisplayMode(.large)
})
.tint(.red)
.buttonStyle(.borderedProminent)
.frame(maxWidth: .infinity)

Spacer()
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,20 @@ extension SKSpriteNode {
let newY = max(dropZoneFrame.minY + size.height / 2,
min(position.y, dropZoneFrame.maxY - size.height / 2))

position = CGPoint(x: newX, y: newY)
let targetPosition = CGPoint(x: newX, y: newY)

let moveToCenter = SKAction.move(to: targetPosition, duration: 0.2)
moveToCenter.timingMode = .easeInEaseOut

run(
moveToCenter,
completion: {
self.position = targetPosition
self.zRotation = 0
self.zPosition = 10
self.removeAllActions()
}
)
}

func fullyContains(bounds: CGRect) -> Bool {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,11 @@ import Foundation
public struct NewGameplayFindTheRightOrderChoice: Identifiable, Equatable {
// MARK: Lifecycle

public init(id: String = UUID().uuidString, value: String, type: ChoiceType = .text) {
public init(id: String = UUID().uuidString, value: String, type: ChoiceType = .text, alreadyOrdered: Bool = false) {
HPezz marked this conversation as resolved.
Show resolved Hide resolved
self.id = id
self.value = value
self.type = type
self.alreadyOrdered = alreadyOrdered
}

// MARK: Public
Expand All @@ -26,8 +27,11 @@ public struct NewGameplayFindTheRightOrderChoice: Identifiable, Equatable {

// MARK: Internal

static let zero = NewGameplayFindTheRightOrderChoice(value: "")

let value: String
let type: ChoiceType
let alreadyOrdered: Bool
}

// MARK: - NewGameplayFindTheRightOrder
Expand Down Expand Up @@ -56,7 +60,7 @@ public class NewGameplayFindTheRightOrder: GameplayProtocol {
}

return self.orderedChoices.enumerated().map { index, choice in
if self.orderedChoices[index] == choice {
if choices[index] == choice {
(choice: choice, correctPosition: true)
} else {
(choice: choice, correctPosition: false)
Expand All @@ -74,4 +78,12 @@ public extension NewGameplayFindTheRightOrder {
NewGameplayFindTheRightOrderChoice(value: "5th choice"),
NewGameplayFindTheRightOrderChoice(value: "6th choice"),
]

static let kDefaultImageChoicesWithZones: [NewGameplayFindTheRightOrderChoice] = [
NewGameplayFindTheRightOrderChoice(value: "sequencing_dressing_up_1", type: .image, alreadyOrdered: true),
NewGameplayFindTheRightOrderChoice(value: "sequencing_dressing_up_2", type: .image, alreadyOrdered: false),
NewGameplayFindTheRightOrderChoice(value: "sequencing_dressing_up_3", type: .image, alreadyOrdered: false),
NewGameplayFindTheRightOrderChoice(value: "sequencing_dressing_up_4", type: .image, alreadyOrdered: true),
NewGameplayFindTheRightOrderChoice(value: "sequencing_dressing_up_5", type: .image, alreadyOrdered: false),
]
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ public class DnDGridCoordinatorAssociateCategories: DnDGridGameplayCoordinatorPr
self.gameplay = gameplay

self.uiChoices.value.choices = gameplay.choices.map { choice in
DnDAnswerNode(id: choice.id, value: choice.value, type: choice.type, size: self.uiChoices.value.choiceSize)
DnDAnswerNode(id: choice.id, value: choice.value, type: choice.type, size: self.uiChoices.value.choiceSize(for: gameplay.choices.count))
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ public class DnDGridWithZonesCoordinatorAssociateCategories: DnDGridWithZonesGam
}

self.uiChoices.value.choices = gameplay.choices[2...].map { choice in
DnDAnswerNode(id: choice.id, value: choice.value, type: choice.type, size: self.uiChoices.value.choiceSize)
DnDAnswerNode(id: choice.id, value: choice.value, type: choice.type, size: self.uiChoices.value.choiceSize(for: gameplay.choices.count))
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
// Leka - iOS Monorepo
// Copyright APF France handicap
// SPDX-License-Identifier: Apache-2.0

import ContentKit
import SpriteKit
import SwiftUI

class DnDOneToOneBaseScene: SKScene {
// MARK: Lifecycle

init(viewModel: DnDOneToOneViewModel) {
self.viewModel = viewModel
super.init(size: CGSize.zero)
}

@available(*, unavailable)
required init?(coder _: NSCoder) {
fatalError("init(coder:) has not been implemented")
}

// MARK: Internal

override func didMove(to _: SKView) {
self.reset()
}

// MARK: - Touch Interaction

override func touchesBegan(_ touches: Set<UITouch>, with _: UIEvent?) {
guard let touch = touches.first else {
return
}
let location = touch.location(in: self)
if let node = atPoint(location) as? DnDAnswerNode {
for choice in self.viewModel.choices where node.id == choice.id && node.isDraggable {
selectedNodes[touch] = node
self.viewModel.onTouch(.began, choice: node)
}
}
}

override func touchesMoved(_ touches: Set<UITouch>, with _: UIEvent?) {
guard let touch = touches.first else {
return
}
let location = touch.location(in: self)
if let node = selectedNodes[touch] {
node.position = location
}
}

override func touchesEnded(_ touches: Set<UITouch>, with _: UIEvent?) {
guard let touch = touches.first,
let playedNode = self.selectedNodes[touch]
else {
return
}

self.viewModel.onTouch(.ended, choice: playedNode, destination: self.dropZonesNodes.first(where: {
$0.frame.contains(touch.location(in: self))
}))
}

func reset() {
backgroundColor = .clear
removeAllChildren()
removeAllActions()

self.spacer = size.width / CGFloat(self.viewModel.choices.count + 1)

self.layoutChoices()
self.layoutDropzones()
self.viewModel.setAlreadyOrderedNodes()
}

func exerciseCompletedBehavior() {
for node in self.answerNodes {
node.isDraggable = false
}
}

func layoutChoices() {
for (index, choice) in self.viewModel.choices.enumerated() {
choice.initialPosition = self.setInitialPosition(index)
choice.position = choice.initialPosition!
self.answerNodes.append(choice)

let shadowChoice = DnDShadowNode(node: choice)
addChild(shadowChoice)
addChild(choice)
}
}

func layoutDropzones() {
for (index, dropzone) in self.viewModel.dropzones.enumerated() {
dropzone.position = self.setInitialDropZonePosition(index)
self.dropZonesNodes.append(dropzone)
addChild(dropzone)
}
}

func setInitialPosition(_ index: Int) -> CGPoint {
CGPoint(x: self.spacer * CGFloat(index + 1), y: size.height - self.viewModel.choices[0].size.height * 0.8)
}

func setInitialDropZonePosition(_ index: Int) -> CGPoint {
CGPoint(x: self.spacer * CGFloat(index + 1), y: self.viewModel.choices[0].size.height * 0.8)
}

// MARK: Private

private var spacer: CGFloat = .zero
private var viewModel: DnDOneToOneViewModel
private var expectedItemsNodes: [String: [SKSpriteNode]] = [:]
private var selectedNodes: [UITouch: DnDAnswerNode] = [:]
private var dropZonesNodes: [DnDDropZoneNode] = []
private var answerNodes: [DnDAnswerNode] = []
private var playedNode: DnDAnswerNode?
}
Loading
Loading