Test y Ficha de identificacion completado

main
Juan David 1 month ago
parent 963dcc2103
commit d4e9431d38

@ -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)

@ -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 = "<absolute>"; };
F59EA2B32CEEC24A00E6EF4C /* ResearchKit.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = ResearchKit.xcodeproj; path = "/Users/juandavidlopezregalado/Downloads/ResearchKit-main-3/ResearchKit.xcodeproj"; sourceTree = "<absolute>"; };
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 = "<group>";
};
8E66ED762CE2605300A8F5D1 /* Frameworks */ = {
F59EA2B62CEEC24A00E6EF4C /* Products */ = {
isa = PBXGroup;
children = (
F59EA2BE2CEEC24A00E6EF4C /* ResearchKit.framework */,
F59EA2C22CEEC24A00E6EF4C /* ResearchKitUI.framework */,
F59EA2C42CEEC24A00E6EF4C /* ResearchKitActiveTask.framework */,
);
name = Frameworks;
name = Products;
sourceTree = "<group>";
};
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 */

@ -7,7 +7,7 @@
<key>RK-Journals.xcscheme_^#shared#^_</key>
<dict>
<key>orderHint</key>
<integer>0</integer>
<integer>5</integer>
</dict>
</dict>
</dict>

@ -57,17 +57,48 @@
<action selector="consentButtonTapped:" destination="JfP-LC-owM" eventType="touchUpInside" id="MFB-Vp-wFc"/>
</connections>
</button>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="tzF-nP-Ayw">
<rect key="frame" x="65" y="134" width="262" height="35"/>
<state key="normal" title="Button"/>
<buttonConfiguration key="configuration" style="plain" title="Ficha de Identificación"/>
<connections>
<action selector="fichaButtonTapped:" destination="JfP-LC-owM" eventType="touchUpInside" id="NvU-2i-qA7"/>
</connections>
</button>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="31s-HT-kAu">
<rect key="frame" x="144" y="189" width="106" height="35"/>
<state key="normal" title="Button"/>
<buttonConfiguration key="configuration" style="plain" title="Formulario"/>
<connections>
<action selector="testButtonTapped:" destination="JfP-LC-owM" eventType="touchUpInside" id="rij-tK-DyO"/>
</connections>
</button>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Kki-ZH-pLO">
<rect key="frame" x="140" y="252" width="113" height="35"/>
<state key="normal" title="Button"/>
<buttonConfiguration key="configuration" style="plain" title="Actividades"/>
</button>
</subviews>
<viewLayoutGuide key="safeArea" id="juQ-Ke-iUj"/>
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
<constraints>
<constraint firstItem="juQ-Ke-iUj" firstAttribute="trailing" secondItem="T7B-Od-def" secondAttribute="trailing" constant="98" id="3iY-qu-0Yn"/>
<constraint firstItem="T7B-Od-def" firstAttribute="top" secondItem="Kki-ZH-pLO" secondAttribute="bottom" constant="351" id="7e0-aF-Icb"/>
<constraint firstItem="juQ-Ke-iUj" firstAttribute="trailing" secondItem="Kki-ZH-pLO" secondAttribute="trailing" constant="140" id="8Kn-zW-EOh"/>
<constraint firstItem="tzF-nP-Ayw" firstAttribute="top" secondItem="juQ-Ke-iUj" secondAttribute="top" constant="75" id="FPG-DF-cA8"/>
<constraint firstItem="Kki-ZH-pLO" firstAttribute="leading" secondItem="juQ-Ke-iUj" secondAttribute="leading" constant="140" id="Fkq-4I-7rB"/>
<constraint firstItem="juQ-Ke-iUj" firstAttribute="trailing" secondItem="tzF-nP-Ayw" secondAttribute="trailing" constant="66" id="JA9-6U-TyB"/>
<constraint firstItem="i24-wX-q8O" firstAttribute="top" secondItem="juQ-Ke-iUj" secondAttribute="top" constant="643" id="LlO-qa-dTb"/>
<constraint firstItem="T7B-Od-def" firstAttribute="top" secondItem="juQ-Ke-iUj" secondAttribute="top" constant="579" id="PKj-be-ciE"/>
<constraint firstItem="tzF-nP-Ayw" firstAttribute="leading" secondItem="juQ-Ke-iUj" secondAttribute="leading" constant="65" id="U2m-Op-Zw3"/>
<constraint firstItem="Kki-ZH-pLO" firstAttribute="top" secondItem="31s-HT-kAu" secondAttribute="bottom" constant="28" id="VD6-uf-UOr"/>
<constraint firstItem="31s-HT-kAu" firstAttribute="leading" secondItem="juQ-Ke-iUj" secondAttribute="leading" constant="144" id="X7I-f9-Apy"/>
<constraint firstItem="juQ-Ke-iUj" firstAttribute="bottom" secondItem="i24-wX-q8O" secondAttribute="bottom" constant="74" id="Xs7-X3-MJu"/>
<constraint firstItem="31s-HT-kAu" firstAttribute="top" secondItem="tzF-nP-Ayw" secondAttribute="bottom" constant="20" id="a4c-d6-DQ6"/>
<constraint firstItem="T7B-Od-def" firstAttribute="leading" secondItem="juQ-Ke-iUj" secondAttribute="leading" constant="98" id="bdF-cj-wl8"/>
<constraint firstItem="juQ-Ke-iUj" firstAttribute="bottom" secondItem="T7B-Od-def" secondAttribute="bottom" constant="143" id="frs-Wr-4Bn"/>
<constraint firstItem="juQ-Ke-iUj" firstAttribute="trailing" secondItem="i24-wX-q8O" secondAttribute="trailing" constant="75" id="jlf-Py-DdG"/>
<constraint firstItem="juQ-Ke-iUj" firstAttribute="trailing" secondItem="31s-HT-kAu" secondAttribute="trailing" constant="143" id="qp4-Jn-8VD"/>
<constraint firstItem="i24-wX-q8O" firstAttribute="leading" secondItem="juQ-Ke-iUj" secondAttribute="leading" constant="74" id="r3t-fF-Avc"/>
</constraints>
</view>

@ -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])
}
}

@ -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.")
}
}
}
}

@ -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
]
)
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 289 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 234 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 757 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 317 KiB

Loading…
Cancel
Save