Skip to content

Commit 7779025

Browse files
authored
Merge pull request #58 from TaskarCenterAtUW/feature-ramp-grid-changes
Feature ramp grid changes
2 parents 909f42f + 1b90696 commit 7779025

File tree

8 files changed

+139
-64
lines changed

8 files changed

+139
-64
lines changed

GoInfoGame/GoInfoGame/Helpers/Extensions.swift

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,3 +41,13 @@ public class Location: EmbeddedObject {
4141
@Persisted var latitude: Double
4242
@Persisted var longitude: Double
4343
}
44+
// Extension of Array with a method to remove an element
45+
extension Array where Element: Equatable {
46+
// Check if the element exists in the array
47+
mutating func removeObject(element: Element) {
48+
if let index = firstIndex(of: element) {
49+
// If the element is found, remove it from the array
50+
remove(at: index)
51+
}
52+
}
53+
}

GoInfoGame/GoInfoGame/Helpers/LocalizedStrings.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,9 @@ enum LocalizedStrings: String {
7575
case questSurfaceValueTartan = "quest_surface_value_tartan"
7676
case questSurfaceValuePaved = "quest_surface_value_paved"
7777
case questSurfaceValueUnpaved = "quest_surface_value_unpaved"
78-
78+
case questStepsRampSeparateWheelchair = "quest_steps_ramp_separate_wheelchair"
79+
case questStepsRampSeparateWheelchairConfirm = "quest_steps_ramp_separate_wheelchair_confirm"
80+
case questStepsRampSeparateWheelchairDecline = "quest_steps_ramp_separate_wheelchair_decline"
7981

8082
var localized: String {
8183
return NSLocalizedString(rawValue, comment: "")

GoInfoGame/GoInfoGame/UI/CustomComponents/ImageGridItemView.swift

Lines changed: 72 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -20,49 +20,87 @@ struct ImageGridItemView: View {
2020
let imageData: [ImageData]
2121
let isImageRotated: Bool
2222
let isDisplayImageOnly: Bool
23-
let onTap: (String, String) -> Void
23+
let isScrollable: Bool
24+
let allowMultipleSelection: Bool // Boolean indicating whether multiple image selection is allowed
25+
let onTap: ([String]) -> Void
2426

25-
@Binding var selectedImage: String?
27+
@Binding var selectedImages: [String]
2628

2729
var body: some View {
28-
ScrollView {
29-
LazyVGrid(columns: Array(repeating: GridItem(spacing: gridCount == 2 ? 5 : 0), count: gridCount), spacing: gridCount == 2 ? 5 : 0) {
30-
ForEach(imageData) { data in
31-
VStack {
32-
Button(action: {
33-
onTap(data.type, data.tag)
34-
selectedImage = data.imageName
35-
}) {
36-
ZStack {
37-
Image(data.imageName)
38-
.resizable()
39-
.scaledToFill()
40-
.frame(minWidth: isLabelBelow ? 70 : 0, maxWidth: isLabelBelow ? 70 : .infinity, minHeight: isLabelBelow ? 70 : 150, maxHeight: isLabelBelow ? 70 : 150)
41-
.cornerRadius(10)
42-
.clipped()
43-
.rotationEffect(.degrees(isImageRotated ? 30: 0))
44-
.border(selectedImage == data.imageName ? Color.orange : Color.clear, width: selectedImage == data.imageName ? 3 : 0)
45-
if !isLabelBelow {
46-
Text(data.optionName)
47-
.font(.caption)
48-
.fontWeight(.bold)
49-
.foregroundColor(.white)
50-
.padding(20)
51-
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .bottom)
30+
// Conditionally choose between ScrollView and LazyVGrid based on isScrollable
31+
if isScrollable {
32+
ScrollView {
33+
gridWithSpacing( gridCount == 2 ? 5 : 0, count: gridCount, content: imageData)
34+
}
35+
} else {
36+
gridWithSpacing( gridCount == 2 ? 5 : 0, count: gridCount, content: imageData)
37+
}
38+
}
39+
// Function to create LazyVGrid with custom spacing and content
40+
func gridWithSpacing(_ spacing: CGFloat, count: Int, content: [ImageData]) -> some View {
41+
return LazyVGrid(columns: Array(repeating: GridItem(spacing: spacing), count: count), spacing: spacing) {
42+
ForEach(imageData) { data in
43+
VStack {
44+
Button(action: {
45+
// If multiple selection is allowed
46+
if allowMultipleSelection {
47+
/// If the image tag is "none"
48+
if data.tag == "none" {
49+
/// checking if selected tag already exists in selectedImages
50+
if selectedImages.contains(data.tag){
51+
selectedImages.removeAll() /// Clear all selections
52+
} else{
53+
selectedImages = ["none"] /// Select "none"
54+
}
55+
/// If the image tag is not "none"
56+
} else {
57+
if selectedImages.contains("none"){
58+
/// Remove "none" from selection
59+
selectedImages.removeObject(element: "none")
5260
}
61+
if selectedImages.contains(data.tag) {
62+
/// Deselect the image
63+
selectedImages.removeObject(element: data.tag)
64+
} else {
65+
/// Select the image
66+
selectedImages.append(data.tag)
67+
}
68+
}
69+
} else {
70+
// Select the image if multiple selection is not allowed
71+
selectedImages = [data.tag]
72+
}
73+
onTap(selectedImages)
74+
})
75+
{
76+
ZStack {
77+
Image(data.imageName)
78+
.resizable()
79+
.scaledToFill()
80+
.frame(minWidth: isLabelBelow ? 70 : 0, maxWidth: isLabelBelow ? 70 : .infinity, minHeight: isLabelBelow ? 70 : 150, maxHeight: isLabelBelow ? 70 : 150)
81+
.cornerRadius(10)
82+
.clipped()
83+
.rotationEffect(.degrees(isImageRotated ? 30: 0))
84+
.border(selectedImages.contains(data.tag) ? Color.orange : Color.clear, width: selectedImages.contains(data.tag) ? 3 : 0)
85+
if !isLabelBelow {
86+
Text(data.optionName)
87+
.font(.caption)
88+
.fontWeight(.bold)
89+
.foregroundColor(.white)
90+
.padding(20)
91+
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .bottom)
5392
}
54-
55-
}.disabled(isDisplayImageOnly)
56-
if isLabelBelow {
57-
Text(data.optionName)
58-
.font(.caption)
59-
.foregroundColor(.black)
60-
.multilineTextAlignment(.center)
6193
}
94+
95+
}.disabled(isDisplayImageOnly)
96+
if isLabelBelow {
97+
Text(data.optionName)
98+
.font(.caption)
99+
.foregroundColor(.black)
100+
.multilineTextAlignment(.center)
62101
}
63102
}
64103
}
65-
.padding()
66104
}
67105
}
68106
}

GoInfoGame/GoInfoGame/en.lproj/Localizable.strings

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,3 +74,7 @@
7474
"quest_surface_value_tartan" = "Tartan";
7575
"quest_surface_value_paved" = "Paved (generic)";
7676
"quest_surface_value_unpaved" = "Unpaved (generic)";
77+
"quest_steps_ramp_separate_wheelchair" = "Is the wheelchair ramp displayed as a separate way on the map?";
78+
"quest_steps_ramp_separate_wheelchair_confirm" = "separate";
79+
"quest_steps_ramp_separate_wheelchair_decline" = "not separate";
80+

GoInfoGame/GoInfoGame/quests/SideWalkValidation/SideWalkValidationForm.swift

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ struct SideWalkValidationForm: QuestForm ,View {
1313
typealias AnswerClass = SideWalkValidationAnswer
1414
@State private var showAlert = false
1515
@State private var selectedAnswer : SideWalkValidationAnswer = SideWalkValidationAnswer.noAnswerSelected
16-
@State private var selectedImage : String?
16+
@State private var selectedImage : [String] = []
1717
let SideWalksImageData: [ImageData] = [
1818
ImageData(id:SideWalkValidationAnswer.left.description,type: "yes", imageName: "select-left-side", tag: SideWalkValidationAnswer.left.description, optionName: LocalizedStrings.questSidewalkValueLeft.localized),
1919
ImageData(id:SideWalkValidationAnswer.right.description,type: "yes", imageName: "select-right-side", tag: SideWalkValidationAnswer.right.description, optionName: LocalizedStrings.questSidewalkValueRight.localized),
@@ -35,10 +35,10 @@ struct SideWalkValidationForm: QuestForm ,View {
3535
.padding(.bottom,10)
3636
VStack(alignment: .leading){
3737
Text(LocalizedStrings.select.localized).font(.caption).foregroundColor(.gray)
38-
ImageGridItemView(gridCount: 2, isLabelBelow: true, imageData: SideWalksImageData, isImageRotated: false, isDisplayImageOnly: false, onTap: { (type, tag) in
38+
ImageGridItemView(gridCount: 2, isLabelBelow: true, imageData: SideWalksImageData, isImageRotated: false, isDisplayImageOnly: false, isScrollable: false, allowMultipleSelection: false, onTap: { (selectedImage) in
3939
/// To select selected image option as SideWalkValidationAnswer
40-
selectedAnswer = SideWalkValidationAnswer.fromString(tag) ?? SideWalkValidationAnswer.none
41-
print("Clicked: \(type), Tag: \(tag)")}, selectedImage: $selectedImage)
40+
selectedAnswer = SideWalkValidationAnswer.fromString(selectedImage.first ?? "") ?? SideWalkValidationAnswer.none
41+
print("Clicked Tag: \(selectedImage)")}, selectedImages: $selectedImage)
4242
Divider()
4343
HStack() {
4444
Spacer()
@@ -74,7 +74,7 @@ struct SideWalkValidationForm: QuestForm ,View {
7474
/// To Dismiss alert when selectedButton value changes
7575
showAlert = false
7676
/// deselecting image option if any other answer is selected
77-
selectedImage = ""
77+
selectedImage = [""]
7878
print("selectedButton value", selectedOtherAnswerOption?.label ?? "")
7979
/// To select other answers option as SideWalkValidationAnswer
8080
selectedAnswer = SideWalkValidationAnswer.fromString(selectedOtherAnswerOption?.label ?? "", id: selectedOtherAnswerOption?.id) ?? SideWalkValidationAnswer.none

GoInfoGame/GoInfoGame/quests/SidewalkSurface/SidewalkSurfaceForm.swift

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ struct SidewalkSurfaceForm: QuestForm ,View {
1111
var action: ((SidewalkSurfaceAnswer) -> Void)?
1212

1313
typealias AnswerClass = SidewalkSurfaceAnswer
14-
@State private var selectedImage : String?
14+
@State private var selectedImage : [String] = []
1515
@State private var showAlert = false
1616
@State private var selectedAnswer : SidewalkSurfaceAnswer = SidewalkSurfaceAnswer(value: SurfaceAndNote(surface: Surface.none,note: ""))
1717
let imagesFromSurfaces: [ImageData] = SELECTABLE_WAY_SURFACES.compactMap { surfaceString in
@@ -44,10 +44,10 @@ struct SidewalkSurfaceForm: QuestForm ,View {
4444
VStack(alignment: .leading){
4545
Text(LocalizedStrings.select.localized).font(.caption).foregroundColor(.gray)
4646
/// Grid view for displaying selectable surfaces
47-
ImageGridItemView(gridCount: 3, isLabelBelow: true, imageData: imagesFromSurfaces, isImageRotated: false, isDisplayImageOnly: false, onTap: { (type, tag) in
48-
selectedAnswer = SidewalkSurfaceAnswer(value: SurfaceAndNote(surface: Surface(rawValue: tag),note: ""))
47+
ImageGridItemView(gridCount: 3, isLabelBelow: true, imageData: imagesFromSurfaces, isImageRotated: false, isDisplayImageOnly: false, isScrollable: true, allowMultipleSelection: false, onTap: { (selectedImage) in
48+
selectedAnswer = SidewalkSurfaceAnswer(value: SurfaceAndNote(surface: Surface(rawValue: selectedImage.first ?? ""),note: ""))
4949
print("selectedAnswer:", selectedAnswer)
50-
print("Clicked: \(type), Tag: \(tag)")}, selectedImage: $selectedImage)
50+
print("Clicked Tag: \(selectedImage)")}, selectedImages: $selectedImage)
5151
Divider()
5252
HStack() {
5353
Spacer()
@@ -80,7 +80,7 @@ struct SidewalkSurfaceForm: QuestForm ,View {
8080
/// To Dismiss alert when selectedButton value changes
8181
showAlert = false
8282
/// deselecting image option if any other answer is selected
83-
selectedImage = ""
83+
selectedImage = [""]
8484
print("selectedButton value", selectedOtherAnswerOption?.label ?? "")
8585
/// To select other answers option as SidewalkSurfaceAnswer
8686
selectedAnswer = SidewalkSurfaceAnswer(value: SurfaceAndNote(surface: Surface.none,note: selectedOtherAnswerOption?.label ?? ""))

GoInfoGame/GoInfoGame/quests/StepsIncline/StepsInclineForm.swift

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,25 +9,21 @@ import SwiftUI
99

1010
struct StepsInclineForm: View, QuestForm {
1111
var action: ((StepsInclineDirection) -> Void)?
12-
13-
1412
typealias AnswerClass = StepsInclineDirection
15-
@State private var selectedImage : String?
13+
@State private var selectedImage : [String] = []
1614
@State private var showAlert = false
1715
let imageData: [ImageData] = [
1816
ImageData(id:"UP",type: "none", imageName: "steps-incline-up", tag: "up", optionName: LocalizedStrings.questStepsInclineUp.localized),
1917
ImageData(id:"UP_REVERSED",type: "bicycle", imageName: "steps-incline-up-reversed", tag: "down", optionName: LocalizedStrings.questStepsInclineUp.localized),
2018
]
2119
var body: some View {
2220
VStack{
23-
QuestionHeader(icon: Image("steps"),
24-
title: "Which direction leads upwards for the steps",
25-
subtitle: "steps")
21+
QuestionHeader(icon: Image("steps"), title: "Which direction leads upwards for the steps", subtitle: "steps")
2622
VStack (alignment: .leading){
2723
Text(LocalizedStrings.selectOne.localized).font(.caption).foregroundColor(.gray)
28-
ImageGridItemView(gridCount: 2, isLabelBelow: true, imageData: imageData, isImageRotated: true, isDisplayImageOnly: false, onTap: { (type, tag) in
24+
ImageGridItemView(gridCount: 2, isLabelBelow: true, imageData: imageData, isImageRotated: true, isDisplayImageOnly: false, isScrollable: false, allowMultipleSelection: false, onTap: { (selectedImage) in
2925
action?(.down)
30-
}, selectedImage:$selectedImage)
26+
}, selectedImages:$selectedImage)
3127
Divider()
3228
HStack() {
3329
Spacer()

GoInfoGame/GoInfoGame/quests/StepsRamp/StepsRampForm.swift

Lines changed: 36 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -10,26 +10,24 @@ import SwiftUI
1010

1111
struct StepsRampForm: View, QuestForm {
1212
var action: ((StepsRampAnswer) -> Void)?
13-
1413
typealias AnswerClass = StepsRampAnswer
15-
@State private var selectedImage:String?
14+
@State private var selectedImage:[String] = []
1615
@State private var showAlert = false
16+
@State private var showWheelchairAlert = false
1717
let imageData: [ImageData] = [
1818
ImageData(id:"ramp_none",type: "none", imageName: "ramp_none", tag: "none", optionName: LocalizedStrings.questStepsRampNone.localized),
19-
ImageData(id:"ramp_bicycle",type: "bicycle", imageName: "ramp_bicycle", tag: "ramp:wheelchair", optionName: LocalizedStrings.questStepsRampBicycle.localized),
19+
ImageData(id:"ramp_bicycle",type: "bicycle", imageName: "ramp_bicycle", tag: "ramp:bicycle", optionName: LocalizedStrings.questStepsRampBicycle.localized),
2020
ImageData(id:"ramp_stroller",type: "stroller", imageName: "ramp_stroller", tag: "ramp:stroller", optionName: LocalizedStrings.questStepsRampStroller.localized),
21-
ImageData(id:"ramp_wheelchair",type: "wheelchair", imageName: "ramp_wheelchair", tag: "ramp:bicycle", optionName: LocalizedStrings.questStepsRampWheelchair.localized),
21+
ImageData(id:"ramp_wheelchair",type: "wheelchair", imageName: "ramp_wheelchair", tag: "ramp:wheelchair", optionName: LocalizedStrings.questStepsRampWheelchair.localized),
2222
]
2323
var body: some View {
2424
VStack (alignment: .leading){
25-
QuestionHeader(icon: Image("ic_quest_steps_ramp"), title: LocalizedStrings.questDetermineSidewalkWidth.localized, subtitle: "North 88th Street").padding(.bottom,10)
25+
QuestionHeader(icon: Image("ic_quest_steps_ramp"), title: LocalizedStrings.questStepsRampTitle.localized, subtitle: "North 88th Street").padding(.bottom,10)
2626
VStack(alignment: .leading){
2727
Text(LocalizedStrings.select.localized).font(.caption).foregroundColor(.gray)
28-
ImageGridItemView(gridCount: 2, isLabelBelow: false, imageData: imageData, isImageRotated: false, isDisplayImageOnly: false, onTap: { (type, tag) in
29-
print("Clicked: \(type), Tag: \(tag)")
30-
let answer = StepsRampAnswer(bicycleRamp: true, strollerRamp: true, wheelchairRamp: .NO)
31-
action?(answer)
32-
}, selectedImage: $selectedImage)
28+
ImageGridItemView(gridCount: 2, isLabelBelow: false, imageData: imageData, isImageRotated: false, isDisplayImageOnly: false, isScrollable: false, allowMultipleSelection: true, onTap: { (selectedImage) in
29+
print("Clicked Tag: \(selectedImage)")
30+
}, selectedImages: $selectedImage)
3331

3432
Divider()
3533
HStack() {
@@ -43,8 +41,35 @@ struct StepsRampForm: View, QuestForm {
4341
Alert(title: Text("More Questions"))
4442
}.frame(alignment: .center)
4543
Spacer()
44+
if !selectedImage.isEmpty {
45+
Button() {
46+
/// applying final selected answer
47+
if selectedImage.contains("ramp:wheelchair") {
48+
showWheelchairAlert = true
49+
}else {
50+
//TODO: none/bicycle/stroller type answer
51+
let answer = StepsRampAnswer(bicycleRamp: true, strollerRamp: true, wheelchairRamp: .NO)
52+
action?(answer)
53+
}
54+
}label: {
55+
Image(systemName: "checkmark.circle.fill")
56+
.font(Font.system(size: 40))
57+
.foregroundColor(.orange)
58+
}
59+
/// displayed only when selected answers contains wheelchair
60+
.alert(LocalizedStrings.questStepsRampSeparateWheelchair.localized, isPresented: $showWheelchairAlert) {
61+
Button(LocalizedStrings.questStepsRampSeparateWheelchairConfirm.localized.uppercased(), action: {
62+
//TODO: wheelchair type answer separate
63+
})
64+
Button(LocalizedStrings.questStepsRampSeparateWheelchairDecline.localized.uppercased(), action: {
65+
//TODO: wheelchair type answer not separate
66+
})
67+
/// no action to be taken
68+
Button(LocalizedStrings.questGenericConfirmationNo.localized, role: .cancel, action: {})
69+
}
70+
}
4671
}.padding(.top,10)
47-
} .padding()
72+
} .padding()
4873
.background(
4974
RoundedRectangle(cornerRadius: 10)
5075
.fill(Color.white)

0 commit comments

Comments
 (0)