Skip to content

Commit

Permalink
🔀️ Merge branch 'hugo/feature/Add-DnDInOrder-in-new-GEK' into develop
Browse files Browse the repository at this point in the history
  • Loading branch information
ladislas committed Dec 5, 2024
2 parents bde5d09 + 2a56bfb commit c3d4625
Show file tree
Hide file tree
Showing 13 changed files with 655 additions and 113 deletions.
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) {
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

0 comments on commit c3d4625

Please sign in to comment.