diff --git a/README.md b/README.md index 9432458..408e4af 100644 --- a/README.md +++ b/README.md @@ -567,4 +567,168 @@ Para comprobar el funcionameinto, se procede a realizar el consentimiento y al t ![](imagenes/pdfC.png) +Ahora para crear distintos formularios, test, historias clinicas, actividades, entre otras, es necesario crear diferentes botones con la acción a realizar, las cuales serán activadas desde el **TaskViewController** ya que ahí es donde se alojarán los botones. Por lo tanto, se crearán para 3 distintas actividades, una Ficha de Identificación, un Formulario, y algunas actividades como de medicion de pasos, de memoria, reacción, voz, etc. + +![](imagenes/botonesFFA.png) + +Primero se realizará la ficha de identificación de los pacientes o los usuarios. Por lo tanto, se crea un nuevo archivo Swift File con el nombre **FichaManager** que es donde se pondrá todas las preguntas con relación a la Ficha de identificación, y con distintos tipos de respuesta, como de opcion multiple, de opcion unica, abiertas, entre otras. + +El código para la Ficha de identificación queda de la siguiente manera: + +``` +import Foundation +import ResearchKit + +class FichaManager { + static let shared = FichaManager() + + func createFichaTask() -> ORKTask { + + let bienvenida = ORKInstructionStep(identifier: "bienvenida") + bienvenida.title = "Ficha Identificación" + bienvenida.iconImage = UIImage(systemName: "person.text.rectangle") + bienvenida.detailText = "Por favor, llena la siguiente ficha de identificación con tus datos" + + // Nombre + let textQuestionStepTitle = "Nombre" + let textAnswerFormat = ORKTextAnswerFormat(maximumLength: 30) + textAnswerFormat.multipleLines = false + let textQuestionStep = ORKQuestionStep(identifier: "Nombre", title: textQuestionStepTitle, question: "¿Cuál es tu nombre?", answer: textAnswerFormat) + textQuestionStep.isOptional = false + + // Sexo + let multiGenderChoice = "Sexo" + let gendChoices = [ + ORKTextChoice(text: "Hombre", value: "hombre" as NSString), + ORKTextChoice(text: "Mujer", value: "mujer" as NSString), + ORKTextChoice(text: "Prefiero no decirlo", value: "Inf" as NSString)] + + let genderChoiceFormat = ORKTextChoiceAnswerFormat(style: .singleChoice, textChoices: gendChoices) + let multiGenderQuestionStep = ORKQuestionStep(identifier: "Sexo", title: multiGenderChoice,question: "¿Cuál es tu sexo?", answer: genderChoiceFormat) + multiGenderQuestionStep.isOptional = false + + // Edad + var ageChoices: [ORKTextChoice] = [] + for age in 16...50 { + let ageChoice = ORKTextChoice(text: "\(age) años", value: "\(age)" as NSString) + ageChoices.append(ageChoice) + } + + let ageQuestionStep = ORKQuestionStep( + identifier: "ageQuestionStep", + title: "Pregunta sobre Edad", + question: "¿Qué edad tienes?", + answer: ORKValuePickerAnswerFormat(textChoices: ageChoices) + ) + ageQuestionStep.isOptional = false // Hacemos que esta pregunta sea obligatoria + + // Peso + let pesoQuestionStep = ORKQuestionStep(identifier: "peso", title: "Peso", question: "¿Cuál es tu peso en kilogramos?", answer: ORKNumericAnswerFormat(style: .integer, unit: "kg")) + pesoQuestionStep.isOptional = false + + // Altura + let alturaQuestionStep = ORKQuestionStep(identifier: "altura", title: "Altura", question: "¿Cuál es tu altura en metros?", answer: ORKNumericAnswerFormat(style: .decimal, unit: "m")) + alturaQuestionStep.isOptional = false + + + // Sintomas + let symptomsChoices = [ + ORKTextChoice(text: "Dolor de cabeza", value: "dolor_de_cabeza" as NSString), + ORKTextChoice(text: "Fiebre", value: "fiebre" as NSString), + ORKTextChoice(text: "Tos", value: "tos" as NSString), + ORKTextChoice(text: "Dificultad para respirar", value: "dificultad_respirar" as NSString), + ORKTextChoice(text: "Dolor en el pecho", value: "dolor_pecho" as NSString), + ORKTextChoice(text: "Fatiga", value: "fatiga" as NSString), + ORKTextChoice(text: "Náuseas", value: "nauseas" as NSString), + ORKTextChoice(text: "Otros", value: "otros" as NSString) + ] + + // Crear la pregunta de síntomas + let symptomsStep = ORKQuestionStep( + identifier: "sintomas", + title: "¿Qué síntomas estás presentando?", + answer: ORKAnswerFormat.choiceAnswerFormat(with: .multipleChoice, textChoices: symptomsChoices) + ) + symptomsStep.isOptional = false + + let termino = ORKInstructionStep(identifier: "termino") + termino.title = "Gracias" + termino.iconImage = UIImage(systemName: "checkmark.circle.fill") + termino.detailText = "Gracias por llenar la ficha de identificación" + + return ORKOrderedTask(identifier: "FichaIdentificacion", steps: [bienvenida, textQuestionStep, multiGenderQuestionStep, ageQuestionStep, pesoQuestionStep, alturaQuestionStep, symptomsStep, termino]) + } +} +``` + +En donde encontramos preguntas muy simples y personales a las que se les puede agregar muchas más preguntas que en lugar de hacer una Ficha de Identificación se puede hacer una Historia clínica. + +De manera similar a los otros botones, es necesario que del botón de Ficha se cree un buttonAction en el TasksViewController, para ahi poner la accion que es abrir la Ficha. + +![](imagenes/fichaBA.png) + +donde agregaremos el siguiente código: + +``` + let taskViewController = ORKTaskViewController(task: FichaManager.shared.createFichaTask(), taskRun: nil) + taskViewController.delegate = self + taskViewController.modalPresentationStyle = .fullScreen + present(taskViewController, animated: true, completion: nil) +``` + +Esto ya abriría la ficha de identificación con las preguntas propuestas. + +![](imagenes/ficha1ex.png) + +sin embargo, las respuestas no son conocidas, para ello, se requiere que en el **TasksViewController** se haga una funcion en donde se podran visualizar y manipular para hacer algunos procesos que se necesiten, como calculo de IMC, entre otros, la función para este momento de mostrara todas esta de la siguiente manera: + +``` +func taskViewController(_ taskViewController: ORKTaskViewController, didFinishWith reason: ORKTaskFinishReason, error: Error?) { + // 1. Dismiss the task view controller + taskViewController.dismiss(animated: true, completion: nil) + + // 2. Get the task result from the taskViewController + let taskResult = taskViewController.result + print("Estructura completa de los resultados: \(taskResult)") + + + // 3. Verificar que los resultados estén presentes + if let stepResults = taskResult.results as? [ORKStepResult] { + // 4. Imprimir los resultados de los pasos + for stepResult in stepResults { + print("Identificador del paso: \(stepResult.identifier)") + + // Dependiendo del tipo de resultado, extraemos la información adecuada + if let textResult = stepResult.results?.first as? ORKTextQuestionResult { + // Resultado de una pregunta de texto + print("Respuesta de \(stepResult.identifier): \(textResult.textAnswer ?? "Sin respuesta")") + } else if let choiceResult = stepResult.results?.first as? ORKChoiceQuestionResult { + // Resultado de una pregunta de opción múltiple + print("Respuesta de \(stepResult.identifier): \(choiceResult.choiceAnswers ?? [])") + } else if let numericResult = stepResult.results?.first as? ORKNumericQuestionResult { + // Resultado de una pregunta numérica + print("Respuesta de \(stepResult.identifier): \(numericResult.numericAnswer ?? 0)") + } + } + } +``` + +Esto mostraria todas las respuestas, pero para seleccionar solo una, por ejemplo el nombre, se puede hacer de manera individual con el siguiente código: + +``` + if let nombreResult = taskResult.result(forIdentifier: "Nombre") as? ORKTextQuestionResult { + if let nombre = nombreResult.textAnswer { + print("Nombre: \(nombre)") // Debería imprimir "David" + } else { + print("No se encontró respuesta para el nombre.") + } + } +``` + +Para la creación de tests más largos y elaborados es de la misma manera, por ello, no se colocará el código, ya que tiene la misma estructura, solo cambiarían las preguntas, y la extension de ellas. + +El test que se realizó es acerca del estado de animo y ansiedad, el cuál luce de la siguiente manera: + +![](imagenes/testAn.png) + diff --git a/RK-Journals/RK-Journals.xcodeproj/project.pbxproj b/RK-Journals/RK-Journals.xcodeproj/project.pbxproj index da9d95a..6f93374 100644 --- a/RK-Journals/RK-Journals.xcodeproj/project.pbxproj +++ b/RK-Journals/RK-Journals.xcodeproj/project.pbxproj @@ -7,32 +7,32 @@ objects = { /* Begin PBXBuildFile section */ - 8E66ED772CE2605300A8F5D1 /* ResearchKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8E66ED6D2CE25EF600A8F5D1 /* ResearchKit.framework */; }; - 8E66ED782CE2605300A8F5D1 /* ResearchKit.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 8E66ED6D2CE25EF600A8F5D1 /* ResearchKit.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - 8E66ED792CE2605300A8F5D1 /* ResearchKitActiveTask.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8E66ED732CE25EF600A8F5D1 /* ResearchKitActiveTask.framework */; }; - 8E66ED7A2CE2605300A8F5D1 /* ResearchKitActiveTask.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 8E66ED732CE25EF600A8F5D1 /* ResearchKitActiveTask.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - 8E66ED7B2CE2605300A8F5D1 /* ResearchKitUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8E66ED712CE25EF600A8F5D1 /* ResearchKitUI.framework */; }; - 8E66ED7C2CE2605300A8F5D1 /* ResearchKitUI.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 8E66ED712CE25EF600A8F5D1 /* ResearchKitUI.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + F59EA2C72CEEC25C00E6EF4C /* ResearchKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F59EA2BE2CEEC24A00E6EF4C /* ResearchKit.framework */; }; + F59EA2C82CEEC25C00E6EF4C /* ResearchKit.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = F59EA2BE2CEEC24A00E6EF4C /* ResearchKit.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + F59EA2C92CEEC25C00E6EF4C /* ResearchKitActiveTask.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F59EA2C42CEEC24A00E6EF4C /* ResearchKitActiveTask.framework */; }; + F59EA2CA2CEEC25C00E6EF4C /* ResearchKitActiveTask.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = F59EA2C42CEEC24A00E6EF4C /* ResearchKitActiveTask.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + F59EA2CB2CEEC25C00E6EF4C /* ResearchKitUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F59EA2C22CEEC24A00E6EF4C /* ResearchKitUI.framework */; }; + F59EA2CC2CEEC25C00E6EF4C /* ResearchKitUI.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = F59EA2C22CEEC24A00E6EF4C /* ResearchKitUI.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ - 8E66ED6C2CE25EF600A8F5D1 /* PBXContainerItemProxy */ = { + F59EA2BD2CEEC24A00E6EF4C /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; - containerPortal = 8E66ED622CE25EF600A8F5D1 /* ResearchKit.xcodeproj */; + containerPortal = F59EA2B32CEEC24A00E6EF4C /* ResearchKit.xcodeproj */; proxyType = 2; remoteGlobalIDString = B183A5951A8535D100C76870; remoteInfo = ResearchKit; }; - 8E66ED702CE25EF600A8F5D1 /* PBXContainerItemProxy */ = { + F59EA2C12CEEC24A00E6EF4C /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; - containerPortal = 8E66ED622CE25EF600A8F5D1 /* ResearchKit.xcodeproj */; + containerPortal = F59EA2B32CEEC24A00E6EF4C /* ResearchKit.xcodeproj */; proxyType = 2; remoteGlobalIDString = CA1C7A5A288B0C68004DAB3A; remoteInfo = ResearchKitUI; }; - 8E66ED722CE25EF600A8F5D1 /* PBXContainerItemProxy */ = { + F59EA2C32CEEC24A00E6EF4C /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; - containerPortal = 8E66ED622CE25EF600A8F5D1 /* ResearchKit.xcodeproj */; + containerPortal = F59EA2B32CEEC24A00E6EF4C /* ResearchKit.xcodeproj */; proxyType = 2; remoteGlobalIDString = CAD08967289DD747007B2A98; remoteInfo = ResearchKitActiveTask; @@ -46,9 +46,9 @@ dstPath = ""; dstSubfolderSpec = 10; files = ( - 8E66ED7C2CE2605300A8F5D1 /* ResearchKitUI.framework in Embed Frameworks */, - 8E66ED782CE2605300A8F5D1 /* ResearchKit.framework in Embed Frameworks */, - 8E66ED7A2CE2605300A8F5D1 /* ResearchKitActiveTask.framework in Embed Frameworks */, + F59EA2CC2CEEC25C00E6EF4C /* ResearchKitUI.framework in Embed Frameworks */, + F59EA2C82CEEC25C00E6EF4C /* ResearchKit.framework in Embed Frameworks */, + F59EA2CA2CEEC25C00E6EF4C /* ResearchKitActiveTask.framework in Embed Frameworks */, ); name = "Embed Frameworks"; runOnlyForDeploymentPostprocessing = 0; @@ -56,7 +56,7 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ - 8E66ED622CE25EF600A8F5D1 /* ResearchKit.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = ResearchKit.xcodeproj; path = "/Users/JD2207/Downloads/ResearchKit-main/ResearchKit.xcodeproj"; sourceTree = ""; }; + F59EA2B32CEEC24A00E6EF4C /* ResearchKit.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = ResearchKit.xcodeproj; path = "/Users/juandavidlopezregalado/Downloads/ResearchKit-main-3/ResearchKit.xcodeproj"; sourceTree = ""; }; F5DB42F02CDD93D100121599 /* RK-Journals.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "RK-Journals.app"; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ @@ -86,36 +86,36 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 8E66ED7B2CE2605300A8F5D1 /* ResearchKitUI.framework in Frameworks */, - 8E66ED772CE2605300A8F5D1 /* ResearchKit.framework in Frameworks */, - 8E66ED792CE2605300A8F5D1 /* ResearchKitActiveTask.framework in Frameworks */, + F59EA2CB2CEEC25C00E6EF4C /* ResearchKitUI.framework in Frameworks */, + F59EA2C72CEEC25C00E6EF4C /* ResearchKit.framework in Frameworks */, + F59EA2C92CEEC25C00E6EF4C /* ResearchKitActiveTask.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ - 8E66ED652CE25EF600A8F5D1 /* Products */ = { + 8E66ED762CE2605300A8F5D1 /* Frameworks */ = { isa = PBXGroup; children = ( - 8E66ED6D2CE25EF600A8F5D1 /* ResearchKit.framework */, - 8E66ED712CE25EF600A8F5D1 /* ResearchKitUI.framework */, - 8E66ED732CE25EF600A8F5D1 /* ResearchKitActiveTask.framework */, ); - name = Products; + name = Frameworks; sourceTree = ""; }; - 8E66ED762CE2605300A8F5D1 /* Frameworks */ = { + F59EA2B62CEEC24A00E6EF4C /* Products */ = { isa = PBXGroup; children = ( + F59EA2BE2CEEC24A00E6EF4C /* ResearchKit.framework */, + F59EA2C22CEEC24A00E6EF4C /* ResearchKitUI.framework */, + F59EA2C42CEEC24A00E6EF4C /* ResearchKitActiveTask.framework */, ); - name = Frameworks; + name = Products; sourceTree = ""; }; F5DB42E72CDD93D100121599 = { isa = PBXGroup; children = ( - 8E66ED622CE25EF600A8F5D1 /* ResearchKit.xcodeproj */, + F59EA2B32CEEC24A00E6EF4C /* ResearchKit.xcodeproj */, F5DB42F22CDD93D100121599 /* RK-Journals */, 8E66ED762CE2605300A8F5D1 /* Frameworks */, F5DB42F12CDD93D100121599 /* Products */, @@ -185,8 +185,8 @@ projectDirPath = ""; projectReferences = ( { - ProductGroup = 8E66ED652CE25EF600A8F5D1 /* Products */; - ProjectRef = 8E66ED622CE25EF600A8F5D1 /* ResearchKit.xcodeproj */; + ProductGroup = F59EA2B62CEEC24A00E6EF4C /* Products */; + ProjectRef = F59EA2B32CEEC24A00E6EF4C /* ResearchKit.xcodeproj */; }, ); projectRoot = ""; @@ -197,25 +197,25 @@ /* End PBXProject section */ /* Begin PBXReferenceProxy section */ - 8E66ED6D2CE25EF600A8F5D1 /* ResearchKit.framework */ = { + F59EA2BE2CEEC24A00E6EF4C /* ResearchKit.framework */ = { isa = PBXReferenceProxy; fileType = wrapper.framework; path = ResearchKit.framework; - remoteRef = 8E66ED6C2CE25EF600A8F5D1 /* PBXContainerItemProxy */; + remoteRef = F59EA2BD2CEEC24A00E6EF4C /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; - 8E66ED712CE25EF600A8F5D1 /* ResearchKitUI.framework */ = { + F59EA2C22CEEC24A00E6EF4C /* ResearchKitUI.framework */ = { isa = PBXReferenceProxy; fileType = wrapper.framework; path = ResearchKitUI.framework; - remoteRef = 8E66ED702CE25EF600A8F5D1 /* PBXContainerItemProxy */; + remoteRef = F59EA2C12CEEC24A00E6EF4C /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; - 8E66ED732CE25EF600A8F5D1 /* ResearchKitActiveTask.framework */ = { + F59EA2C42CEEC24A00E6EF4C /* ResearchKitActiveTask.framework */ = { isa = PBXReferenceProxy; fileType = wrapper.framework; path = ResearchKitActiveTask.framework; - remoteRef = 8E66ED722CE25EF600A8F5D1 /* PBXContainerItemProxy */; + remoteRef = F59EA2C32CEEC24A00E6EF4C /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXReferenceProxy section */ diff --git a/RK-Journals/RK-Journals.xcodeproj/project.xcworkspace/xcuserdata/juandavidlopezregalado.xcuserdatad/UserInterfaceState.xcuserstate b/RK-Journals/RK-Journals.xcodeproj/project.xcworkspace/xcuserdata/juandavidlopezregalado.xcuserdatad/UserInterfaceState.xcuserstate index 2f39e6e..1d72159 100644 Binary files a/RK-Journals/RK-Journals.xcodeproj/project.xcworkspace/xcuserdata/juandavidlopezregalado.xcuserdatad/UserInterfaceState.xcuserstate and b/RK-Journals/RK-Journals.xcodeproj/project.xcworkspace/xcuserdata/juandavidlopezregalado.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/RK-Journals/RK-Journals.xcodeproj/xcuserdata/juandavidlopezregalado.xcuserdatad/xcschemes/xcschememanagement.plist b/RK-Journals/RK-Journals.xcodeproj/xcuserdata/juandavidlopezregalado.xcuserdatad/xcschemes/xcschememanagement.plist index 6c387e3..b4df92d 100644 --- a/RK-Journals/RK-Journals.xcodeproj/xcuserdata/juandavidlopezregalado.xcuserdatad/xcschemes/xcschememanagement.plist +++ b/RK-Journals/RK-Journals.xcodeproj/xcuserdata/juandavidlopezregalado.xcuserdatad/xcschemes/xcschememanagement.plist @@ -7,7 +7,7 @@ RK-Journals.xcscheme_^#shared#^_ orderHint - 0 + 5 diff --git a/RK-Journals/RK-Journals/Base.lproj/Main.storyboard b/RK-Journals/RK-Journals/Base.lproj/Main.storyboard index 1cfc608..d87c107 100644 --- a/RK-Journals/RK-Journals/Base.lproj/Main.storyboard +++ b/RK-Journals/RK-Journals/Base.lproj/Main.storyboard @@ -57,17 +57,48 @@ + + + + + + + + + + + + + diff --git a/RK-Journals/RK-Journals/FichaManager.swift b/RK-Journals/RK-Journals/FichaManager.swift new file mode 100644 index 0000000..5b527d4 --- /dev/null +++ b/RK-Journals/RK-Journals/FichaManager.swift @@ -0,0 +1,90 @@ +// +// FichaManager.swift +// RK-Journals +// +// Created by Juan David López Regalado on 20/11/24. +// + +import Foundation +import ResearchKit + +class FichaManager { + static let shared = FichaManager() + + func createFichaTask() -> ORKTask { + + let bienvenida = ORKInstructionStep(identifier: "bienvenida") + bienvenida.title = "Ficha Identificación" + bienvenida.iconImage = UIImage(systemName: "person.text.rectangle") + bienvenida.detailText = "Por favor, llena la siguiente ficha de identificación con tus datos" + + // Nombre + let textQuestionStepTitle = "Nombre" + let textAnswerFormat = ORKTextAnswerFormat(maximumLength: 30) + textAnswerFormat.multipleLines = false + let textQuestionStep = ORKQuestionStep(identifier: "Nombre", title: textQuestionStepTitle, question: "¿Cuál es tu nombre?", answer: textAnswerFormat) + textQuestionStep.isOptional = false + + // Sexo + let multiGenderChoice = "Sexo" + let gendChoices = [ + ORKTextChoice(text: "Hombre", value: "hombre" as NSString), + ORKTextChoice(text: "Mujer", value: "mujer" as NSString), + ORKTextChoice(text: "Prefiero no decirlo", value: "Inf" as NSString)] + + let genderChoiceFormat = ORKTextChoiceAnswerFormat(style: .singleChoice, textChoices: gendChoices) + let multiGenderQuestionStep = ORKQuestionStep(identifier: "Sexo", title: multiGenderChoice,question: "¿Cuál es tu sexo?", answer: genderChoiceFormat) + multiGenderQuestionStep.isOptional = false + + // Edad + var ageChoices: [ORKTextChoice] = [] + for age in 16...50 { + let ageChoice = ORKTextChoice(text: "\(age) años", value: "\(age)" as NSString) + ageChoices.append(ageChoice) + } + + let ageQuestionStep = ORKQuestionStep( + identifier: "ageQuestionStep", + title: "Pregunta sobre Edad", + question: "¿Qué edad tienes?", + answer: ORKValuePickerAnswerFormat(textChoices: ageChoices) + ) + ageQuestionStep.isOptional = false // Hacemos que esta pregunta sea obligatoria + + // Peso + let pesoQuestionStep = ORKQuestionStep(identifier: "peso", title: "Peso", question: "¿Cuál es tu peso en kilogramos?", answer: ORKNumericAnswerFormat(style: .integer, unit: "kg")) + pesoQuestionStep.isOptional = false + + // Altura + let alturaQuestionStep = ORKQuestionStep(identifier: "altura", title: "Altura", question: "¿Cuál es tu altura en metros?", answer: ORKNumericAnswerFormat(style: .decimal, unit: "m")) + alturaQuestionStep.isOptional = false + + + // Sintomas + let symptomsChoices = [ + ORKTextChoice(text: "Dolor de cabeza", value: "dolor_de_cabeza" as NSString), + ORKTextChoice(text: "Fiebre", value: "fiebre" as NSString), + ORKTextChoice(text: "Tos", value: "tos" as NSString), + ORKTextChoice(text: "Dificultad para respirar", value: "dificultad_respirar" as NSString), + ORKTextChoice(text: "Dolor en el pecho", value: "dolor_pecho" as NSString), + ORKTextChoice(text: "Fatiga", value: "fatiga" as NSString), + ORKTextChoice(text: "Náuseas", value: "nauseas" as NSString), + ORKTextChoice(text: "Otros", value: "otros" as NSString) + ] + + // Crear la pregunta de síntomas + let symptomsStep = ORKQuestionStep( + identifier: "sintomas", + title: "¿Qué síntomas estás presentando?", + answer: ORKAnswerFormat.choiceAnswerFormat(with: .multipleChoice, textChoices: symptomsChoices) + ) + symptomsStep.isOptional = false + + let termino = ORKInstructionStep(identifier: "termino") + termino.title = "Gracias" + termino.iconImage = UIImage(systemName: "checkmark.circle.fill") + termino.detailText = "Gracias por llenar la ficha de identificación" + + return ORKOrderedTask(identifier: "FichaIdentificacion", steps: [bienvenida, textQuestionStep, multiGenderQuestionStep, ageQuestionStep, pesoQuestionStep, alturaQuestionStep, symptomsStep, termino]) + } +} diff --git a/RK-Journals/RK-Journals/TasksViewController.swift b/RK-Journals/RK-Journals/TasksViewController.swift index adecaac..a0f77e3 100644 --- a/RK-Journals/RK-Journals/TasksViewController.swift +++ b/RK-Journals/RK-Journals/TasksViewController.swift @@ -9,35 +9,39 @@ import UIKit import ResearchKitUI class TasksViewController: UIViewController, ORKTaskViewControllerDelegate { - + + override func viewDidLoad() { super.viewDidLoad() - // Do any additional setup after loading the view. } - - /* - // MARK: - Navigation - - // In a storyboard-based application, you will often want to do a little preparation before navigation - override func prepare(for segue: UIStoryboardSegue, sender: Any?) { - // Get the new view controller using segue.destination. - // Pass the selected object to the new view controller. - } - */ - + + @IBAction func leaveButtonTapped(_ sender: UIButton) { ORKPasscodeViewController.removePasscodeFromKeychain() performSegue(withIdentifier: "returnToConsent", sender: nil) - + } @IBAction func consentButtonTapped(_ sender: UIButton) { let taskViewController = ORKTaskViewController(task: consentPDFViewerTask(), taskRun: nil) taskViewController.delegate = self taskViewController.modalPresentationStyle = .fullScreen present(taskViewController, animated: true, completion: nil) - + + } + @IBAction func fichaButtonTapped(_ sender: UIButton) { + let taskViewController = ORKTaskViewController(task: FichaManager.shared.createFichaTask(), taskRun: nil) + taskViewController.delegate = self + taskViewController.modalPresentationStyle = .fullScreen + present(taskViewController, animated: true, completion: nil) + } + + @IBAction func testButtonTapped(_ sender: UIButton) { + let taskViewController = ORKTaskViewController(task: TestManager.shared.createTestTask(), taskRun: nil) + taskViewController.delegate = self + taskViewController.modalPresentationStyle = .fullScreen + present(taskViewController, animated: true, completion: nil) } func consentPDFViewerTask() -> ORKOrderedTask{ @@ -48,7 +52,43 @@ class TasksViewController: UIViewController, ORKTaskViewControllerDelegate { return ORKOrderedTask(identifier: String("ConsentPDF"), steps: [PDFViewerStep]) } - @objc(taskViewController:didFinishWithReason:error:) func taskViewController(_ taskViewController: ORKTaskViewController, didFinishWith reason: ORKTaskFinishReason, error: Error?) { - taskViewController.dismiss(animated: true, completion: nil) + + func taskViewController(_ taskViewController: ORKTaskViewController, didFinishWith reason: ORKTaskFinishReason, error: Error?) { + // 1. Dismiss the task view controller + taskViewController.dismiss(animated: true, completion: nil) + + // 2. Get the task result from the taskViewController + let taskResult = taskViewController.result + print("Estructura completa de los resultados: \(taskResult)") + + + // 3. Verificar que los resultados estén presentes + if let stepResults = taskResult.results as? [ORKStepResult] { + // 4. Imprimir los resultados de los pasos + for stepResult in stepResults { + print("Identificador del paso: \(stepResult.identifier)") + + // Dependiendo del tipo de resultado, extraemos la información adecuada + if let textResult = stepResult.results?.first as? ORKTextQuestionResult { + // Resultado de una pregunta de texto + print("Respuesta de \(stepResult.identifier): \(textResult.textAnswer ?? "Sin respuesta")") + } else if let choiceResult = stepResult.results?.first as? ORKChoiceQuestionResult { + // Resultado de una pregunta de opción múltiple + print("Respuesta de \(stepResult.identifier): \(choiceResult.choiceAnswers ?? [])") + } else if let numericResult = stepResult.results?.first as? ORKNumericQuestionResult { + // Resultado de una pregunta numérica + print("Respuesta de \(stepResult.identifier): \(numericResult.numericAnswer ?? 0)") + } + } } + + // 4. Ahora intentamos extraer los resultados específicos de cada pregunta usando el identificador + if let nombreResult = taskResult.result(forIdentifier: "Nombre") as? ORKTextQuestionResult { + if let nombre = nombreResult.textAnswer { + print("Nombre: \(nombre)") // Debería imprimir "David" + } else { + print("No se encontró respuesta para el nombre.") + } + } + } } diff --git a/RK-Journals/RK-Journals/TestManager.swift b/RK-Journals/RK-Journals/TestManager.swift new file mode 100644 index 0000000..eaa30d9 --- /dev/null +++ b/RK-Journals/RK-Journals/TestManager.swift @@ -0,0 +1,129 @@ +import Foundation +import ResearchKit + +class TestManager { + static let shared = TestManager() + + func createTestTask() -> ORKTask { + + // Paso 1: Bienvenida + let bienvenidaStep = ORKInstructionStep(identifier: "bienvenida") + bienvenidaStep.title = "Evaluación de Ansiedad y Estado de Ánimo" + bienvenidaStep.text = "Este test nos ayudará a evaluar cómo te sientes actualmente. Responde con sinceridad." + + // Paso 2: Nivel de ansiedad (escala del 1 al 10) + let ansiedadPregunta = ORKQuestionStep( + identifier: "nivelAnsiedad", + title: "¿Qué tan ansioso/a te sientes en una escala del 1 al 10?", + answer: ORKScaleAnswerFormat( + maximumValue: 10, + minimumValue: 1, + defaultValue: 5, + step: 1 + ) + ) + ansiedadPregunta.isOptional = false + + // Paso 3: Estado de ánimo general (rueda de opciones) + let estadoAnimoChoices = [ + ORKTextChoice(text: "Muy feliz", value: "muy_feliz" as NSString), + ORKTextChoice(text: "Feliz", value: "feliz" as NSString), + ORKTextChoice(text: "Neutral", value: "neutral" as NSString), + ORKTextChoice(text: "Triste", value: "triste" as NSString), + ORKTextChoice(text: "Muy triste", value: "muy_triste" as NSString) + ] + let estadoAnimoPregunta = ORKQuestionStep( + identifier: "estadoAnimo", + title: "¿Cómo describirías tu estado de ánimo actual?", + answer: ORKValuePickerAnswerFormat(textChoices: estadoAnimoChoices) + ) + estadoAnimoPregunta.isOptional = false + + + // Paso 4: Frecuencia de síntomas (escala Likert) + let frecuenciaSintomasPregunta = ORKQuestionStep( + identifier: "frecuenciaSintomas", + title: "En la última semana, ¿con qué frecuencia has experimentado estos síntomas?", + answer: ORKAnswerFormat.textScale( + with: [ + ORKTextChoice(text: "Nunca", value: "nunca" as NSString), + ORKTextChoice(text: "Rara vez", value: "rara_vez" as NSString), + ORKTextChoice(text: "A veces", value: "a_veces" as NSString), + ORKTextChoice(text: "Frecuentemente", value: "frecuentemente" as NSString), + ORKTextChoice(text: "Todo el tiempo", value: "todo_el_tiempo" as NSString) + ], + defaultIndex: 2, // Neutral ("A veces") + vertical: true + ) + ) + frecuenciaSintomasPregunta.isOptional = false + + // Paso 5: Situaciones específicas (múltiples opciones) + let situacionesChoices = [ + ORKTextChoice(text: "Dificultad para concentrarte", value: "concentracion" as NSString), + ORKTextChoice(text: "Pérdida de interés en actividades", value: "interes" as NSString), + ORKTextChoice(text: "Sensación de nerviosismo", value: "nerviosismo" as NSString), + ORKTextChoice(text: "Problemas para dormir", value: "sueño" as NSString), + ORKTextChoice(text: "Cambios en el apetito", value: "apetito" as NSString), + ORKTextChoice(text: "Ninguno de los anteriores", value: "ninguno" as NSString) + ] + let situacionesPregunta = ORKQuestionStep( + identifier: "situaciones", + title: "¿Cuáles de las siguientes situaciones has experimentado recientemente?", + answer: ORKAnswerFormat.choiceAnswerFormat(with: .multipleChoice, textChoices: situacionesChoices) + ) + + situacionesPregunta.isOptional = false + + // Paso 6: Tiempo al aire libre (respuesta numérica) + let tiempoAireLibrePregunta = ORKQuestionStep( + identifier: "tiempoAireLibre", + title: "¿Cuántas horas pasas al aire libre por día?", + answer: ORKNumericAnswerFormat(style: .integer, unit: "horas") + ) + tiempoAireLibrePregunta.isOptional = false + + + // Paso 7: Ejercicio (texto corto) + let ejercicioPregunta = ORKQuestionStep( + identifier: "ejercicio", + title: "¿Qué tipo de ejercicio realizas regularmente?", + answer: ORKTextAnswerFormat(maximumLength: 100) + ) + ejercicioPregunta.isOptional = false + + // Paso 8: Comentarios adicionales (texto largo) + let comentariosPregunta = ORKQuestionStep( + identifier: "comentarios", + title: "¿Hay algo más que quieras compartir sobre cómo te sientes?", + answer: { + let formatoTexto = ORKTextAnswerFormat(maximumLength: 500) + formatoTexto.multipleLines = true // Permite que el cuadro sea más grande + return formatoTexto + }() + ) + + comentariosPregunta.isOptional = false + + // Paso final: Agradecimiento + let finStep = ORKInstructionStep(identifier: "fin") + finStep.title = "¡Gracias por completar el test!" + finStep.text = "Tus respuestas serán analizadas para ofrecerte mejores recomendaciones." + + // Crear el test con todos los pasos + return ORKOrderedTask( + identifier: "AnxietyMoodTest", + steps: [ + bienvenidaStep, + ansiedadPregunta, + estadoAnimoPregunta, + frecuenciaSintomasPregunta, + situacionesPregunta, + tiempoAireLibrePregunta, + ejercicioPregunta, + comentariosPregunta, + finStep + ] + ) + } +} diff --git a/imagenes/botonesFFA.png b/imagenes/botonesFFA.png new file mode 100644 index 0000000..0256adc Binary files /dev/null and b/imagenes/botonesFFA.png differ diff --git a/imagenes/ficha1ex.png b/imagenes/ficha1ex.png new file mode 100644 index 0000000..10e03e7 Binary files /dev/null and b/imagenes/ficha1ex.png differ diff --git a/imagenes/fichaBA.png b/imagenes/fichaBA.png new file mode 100644 index 0000000..5930878 Binary files /dev/null and b/imagenes/fichaBA.png differ diff --git a/imagenes/testAn.png b/imagenes/testAn.png new file mode 100644 index 0000000..804e440 Binary files /dev/null and b/imagenes/testAn.png differ