Skip to content

Commit

Permalink
✨ (GEK): Add updated InstructionView
Browse files Browse the repository at this point in the history
  • Loading branch information
macteuts authored and ladislas committed Nov 3, 2023
1 parent 3e8ed5d commit da4f0dc
Show file tree
Hide file tree
Showing 2 changed files with 132 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,8 @@ public struct ActivityView: View {
VStack(spacing: 15) {
ActivityProgressBar(viewModel: viewModel)

Text(viewModel.currentExercise.instructions)
.font(.title)
.padding(40)
.background(.gray)
.cornerRadius(10)
ExerciseInstructionsButton(instructions: viewModel.currentExercise.instructions)
.id(viewModel.currentExerciseIndexInSequence)
}

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

import AVFoundation
import DesignKit
import SwiftUI

// TODO(@ladislas): refactor speech synth into own class

Check failure on line 9 in Modules/GameEngineKit/Sources/_NewSystem/Views/ExerciseInstructionsButton.swift

View workflow job for this annotation

GitHub Actions / lint

TODOs should be resolved ((@ladislas): refactor speech s...) (todo)
class SpeakerViewModel: NSObject, ObservableObject, AVSpeechSynthesizerDelegate {
@Published var isSpeaking = false

private var synthesizer = AVSpeechSynthesizer()
override init() {
super.init()
synthesizer.delegate = self
}

deinit {
synthesizer.delegate = nil
}

func speak(sentence: String) {
let utterance = AVSpeechUtterance(string: sentence)
utterance.rate = 0.40
// TODO(@ladislas): handle different locales

Check failure on line 26 in Modules/GameEngineKit/Sources/_NewSystem/Views/ExerciseInstructionsButton.swift

View workflow job for this annotation

GitHub Actions / lint

TODOs should be resolved ((@ladislas): handle different ...) (todo)
utterance.voice = AVSpeechSynthesisVoice(language: "fr-FR")
synthesizer.speak(utterance)
}

// MARK: AVSpeechSynthesizerDelegate
internal func speechSynthesizer(_ synthesizer: AVSpeechSynthesizer, didStart utterance: AVSpeechUtterance) {
self.isSpeaking = true
}

internal func speechSynthesizer(_ synthesizer: AVSpeechSynthesizer, didFinish utterance: AVSpeechUtterance) {
self.isSpeaking = false
}
}

struct ExerciseInstructionsButton: View {

@StateObject var speaker = SpeakerViewModel()
@State var instructions: String

var body: some View {
Button(instructions) {
speaker.speak(sentence: instructions)
}
.buttonStyle(StepInstructions_ButtonStyle(isSpeaking: $speaker.isSpeaking))
}
}

struct StepInstructions_ButtonStyle: ButtonStyle {

@Binding var isSpeaking: Bool

func makeBody(configuration: Self.Configuration) -> some View {
HStack(spacing: 0) {
Spacer()
configuration.label
.foregroundColor(DesignKitAsset.Colors.darkGray.swiftUIColor)
.font(.system(size: 22, weight: .regular))
.multilineTextAlignment(.center)
.padding(.horizontal, 85)
Spacer()
}
.frame(maxWidth: 640)
.frame(height: 85, alignment: .center)
.background(backgroundGradient)
.overlay(buttonStroke)
.overlay(speachIndicator)
.clipShape(
RoundedRectangle(
cornerRadius: 10,
style: .circular
)
)
.shadow(
color: .black.opacity(0.1),
radius: isSpeaking ? 0 : 4, x: 0, y: isSpeaking ? 1 : 4
)
.scaleEffect(isSpeaking ? 0.98 : 1)
.disabled(isSpeaking)
.animation(.easeOut(duration: 0.2), value: isSpeaking)
}

private var backgroundGradient: some View {
ZStack {
Color.white
LinearGradient(
gradient: Gradient(colors: [.black.opacity(0.1), .black.opacity(0.0), .black.opacity(0.0)]),
startPoint: .top, endPoint: .center
)
.opacity(isSpeaking ? 1 : 0)
}
}

private var buttonStroke: some View {
RoundedRectangle(
cornerRadius: 10,
style: .circular
)
.fill(
.clear,
strokeBorder: LinearGradient(
gradient: Gradient(colors: [.black.opacity(0.2), .black.opacity(0.05)]),
startPoint: .bottom,
endPoint: .top
),
lineWidth: 4
)
.opacity(isSpeaking ? 0.5 : 0)
}

private var speachIndicator: some View {
HStack {
Spacer()
DesignKitAsset.Images.personTalking.swiftUIImage
.resizable()
.renderingMode(.template)
.aspectRatio(contentMode: .fit)
.foregroundColor(
isSpeaking
? DesignKitAsset.Colors.lekaDarkBlue.swiftUIColor : DesignKitAsset.Colors.darkGray.swiftUIColor
)
.padding(10)
}
}
}

0 comments on commit da4f0dc

Please sign in to comment.