### Manage Quiz Configuration and Start Source: https://context7.com/biodigital-inc/human-ios-sdk/llms.txt Configure quiz behavior such as locking, visibility of summary/explanations/submit/progress, and question range. Apply settings with `set()` and start the quiz with `enter()`. ```swift import HumanKit class QuizController { var human: HKHuman! func manageQuiz() { let quiz = human.quiz // Check if model contains a quiz if quiz.enabled { print("This model has \(quiz.questions.count) quiz questions") // Configure quiz behavior quiz.locked = false // Allow user to take quiz quiz.showSummary = true // Show results summary quiz.showExplanation = true // Show answer explanations quiz.showSubmit = true // Show submit button quiz.showProgress = true // Show progress indicator quiz.startNumber = 1 // Start at question 1 quiz.endNumber = 10 // End at question 10 quiz.set() // Apply settings // Start the quiz quiz.enter() } } // ... other methods } ``` -------------------------------- ### Initialize HumanKit SDK Source: https://context7.com/biodigital-inc/human-ios-sdk/llms.txt Use the HKServices singleton to authenticate the SDK with an API key and secret. Implement HKServicesDelegate to handle validation and model loading events. ```swift import HumanKit class AppDelegate: UIResponder, UIApplicationDelegate, HKServicesDelegate { func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { // Option 1: Setup with explicit key and secret HKServices.shared.setup(withKey: "YOUR_API_KEY", secret: "YOUR_SECRET", delegate: self) // Option 2: Setup using Info.plist values (HumanKitKey and HumanKitSecret keys) // HKServices.shared.setup(delegate: self) return true } // MARK: - HKServicesDelegate callbacks func onValidSDK() { print("SDK validated successfully - ready to load models") } func onInvalidSDK() { print("SDK validation failed - check your API key and bundle ID at developer.biodigital.com") } func modelsLoaded() { // Called when HKServices.shared.getModels() completes let models = HKServices.shared.models print("Loaded \(models.count) models from your account") } } ``` -------------------------------- ### Configure HKHuman Viewer Source: https://context7.com/biodigital-inc/human-ios-sdk/llms.txt Initialize the HKHuman viewer within a UIView container and customize UI elements using the setupUI method. ```swift import UIKit import HumanKit class AnatomyViewController: UIViewController, HKHumanDelegate { @IBOutlet weak var canvasView: UIView! var human: HKHuman! override func viewDidLoad() { super.viewDidLoad() // Initialize HKHuman with the container view human = HKHuman(view: canvasView) human.delegate = self // Configure UI options before loading human.setupUI(option: .all, value: true) // Show all UI elements human.setupUI(option: .anatomyLabels, value: true) // Show labels on selection human.setupUI(option: .animation, value: true) // Show play/pause controls human.setupUI(option: .info, value: true) // Show title/description human.setupUI(option: .labels, value: true) // Show scene labels human.setupUI(option: .layers, value: true) // Show layer controls human.setupUI(option: .tools, value: true) // Show tools panel human.setupUI(option: .draw, value: false) // Hide drawing tool human.setupUI(option: .quizAutoStart, value: false)// Don't auto-start quizzes } } ``` -------------------------------- ### Handle Scene Ready Events Source: https://github.com/biodigital-inc/human-ios-sdk/blob/master/HumanKit.xcframework/ios-arm64_x86_64-simulator/HumanKit.framework/viewerFrame.html Listens for the 'scene.ready' event to indicate that the scene has loaded. It then requests and sends scene metadata to the iOS application. ```javascript human.on('scene.ready', function () { sceneLoaded = false; window.webkit.messageHandlers.messageHandler.postMessage("scene.ready event"); human.send('scene.metadata', function(metadata) { window.webkit.messageHandlers.messageHandler.postMessage("scene.metadata event " + JSON.stringify(metadata)); window.webkit.messageHandlers.sceneMetaHandler.postMessage(metadata); }); }); ``` -------------------------------- ### Initialize Embedded Human SDK Source: https://github.com/biodigital-inc/human-ios-sdk/blob/master/HumanKit.xcframework/ios-arm64_x86_64-simulator/HumanKit.framework/viewerFrame.html Initializes the embedded Human SDK by setting the iframe source. It constructs the URL using the SDK port and URL parameters, then posts a 'ready' message to the human handler. ```javascript window.webkit.messageHandlers.messageHandler.postMessage("API object created, listeners registered params: " + window.bd_urlparams); window.webkit.messageHandlers.humanHandler.postMessage("ready"); var iframe = document.getElementById("embeddedHuman"); iframe.src = "http://localhost:" + window.sdkPort + "/?id=models/9OR" + window.bd_urlparams; ``` -------------------------------- ### Download Models for Offline Use with HumanKit Source: https://context7.com/biodigital-inc/human-ios-sdk/llms.txt Use HKServices to download multiple anatomical models for offline display. Ensure the delegate is set up to receive download progress and error callbacks. ```swift import HumanKit class OfflineController: HKServicesDelegate { func downloadModelsForOffline() { // Set delegate to receive download callbacks HKServices.shared.setup(delegate: self) // Download multiple models let modelIds = [ "production/maleAdult/heart_702", "production/maleAdult/brain_v02", "production/femaleAdult/breast_cancer_dark_skin" ] HKServices.shared.download(modelIds: modelIds) } func checkOfflineModels() { // Get list of downloaded models let offlineModels = HKServices.shared.offlineModels() print("Downloaded models: \(offlineModels)") // Check if specific model is available offline let isDownloaded = HKServices.shared.modelDownloaded(id: "production/maleAdult/heart_702") print("Heart model available offline: \(isDownloaded)") // Get raw JSON data for a downloaded model let jsonString = HKServices.shared.getJsonString(modelId: "production/maleAdult/heart_702") let jsonData = HKServices.shared.getJsonData(modelId: "production/maleAdult/heart_702") } func deleteOfflineData() { // Remove all cached model data let deletedCount = HKServices.shared.deleteStoredData() print("Deleted \(deletedCount) cached items") } // MARK: - HKServicesDelegate callbacks func modelDownloaded(modelId: String, count: Int, total: Int) { print("Downloaded \(count)/\(total): \(modelId)") } func modelDownloadError(modelId: String) { print("Failed to download: \(modelId)") } } ``` -------------------------------- ### Initialize BioDigital Human and Bridge Events Source: https://github.com/biodigital-inc/human-ios-sdk/blob/master/HumanKit.xcframework/ios-arm64/HumanKit.framework/viewerFrame.html Sets up the HumanAPI instance and maps various scene and label events to iOS native message handlers. ```javascript body { margin: 0; width:100vw; height:100vh; } #embeddedHuman { position: absolute; border: none; width: 100vw; height: 100vh; } var human = new HumanAPI("embeddedHuman"); human.on( 'labels.created', function(labelData) { window.webkit.messageHandlers.messageHandler.postMessage("labels.created event: " + JSON.stringify(labelData)); window.webkit.messageHandlers.labelCreateHandler.postMessage(labelData); }); human.on( 'labels.destroyed', function(labelData) { window.webkit.messageHandlers.messageHandler.postMessage("labels.destroyed event: " + JSON.stringify(labelData)); window.webkit.messageHandlers.labelDestroyHandler.postMessage(labelData); }); human.on( 'labels.chapterInfo', function(labelData) { window.webkit.messageHandlers.messageHandler.postMessage("labels.chapterInfo event: " + JSON.stringify(labelData)); }); human.on('labels.moved', function(labelPosition) { window.webkit.messageHandlers.messageHandler.postMessage("labels.moved event:" + JSON.stringify(labelPosition)); window.webkit.messageHandlers.labelMovedHandler.postMessage(labelPosition); }); human.on('labels.updated', function(labelData) { window.webkit.messageHandlers.messageHandler.postMessage("labels.updated event: " + JSON.stringify(labelData)); window.webkit.messageHandlers.labelUpdatedHandler.postMessage(labelData); }); human.on('labels.picked', function(labelData) { window.webkit.messageHandlers.messageHandler.postMessage("labels.picked event: " + JSON.stringify(labelData)); window.webkit.messageHandlers.labelPickedHandler.postMessage(labelData); }); human.on( 'labels.enabled' , function () { window.webkit.messageHandlers.messageHandler.postMessage("labels.enabled event"); window.webkit.messageHandlers.labelsShownHandler.postMessage(true); }); human.on( 'labels.disabled' , function () { window.webkit.messageHandlers.messageHandler.postMessage("labels.disabled event"); window.webkit.messageHandlers.labelsShownHandler.postMessage(false); }); // send a chapter transition message human.on( 'timeline.chapterTransition', function(chapterData) { window.webkit.messageHandlers.messageHandler.postMessage("timeline.chapterTransition event: " + JSON.stringify(chapterData)); window.webkit.messageHandlers.chapterHandler.postMessage(chapterData); }); // camera updated callback human.on( 'camera.updated', function(cameraData) { window.webkit.messageHandlers.messageHandler.postMessage("camera.updated event: " + JSON.stringify(cameraData)); window.webkit.messageHandlers.cameraUpdateHandler.postMessage(cameraData); }); var sceneLoaded = false var finishLoad = function(caller) { sceneLoaded = true; window.webkit.messageHandlers.messageHandler.postMessage("$$$$$$$$ ASSETS LOADED " + caller); human.send('scene.info', function(sceneData) { window.webkit.messageHandlers.sceneInfoHandler.postMessage(sceneData); }); window.webkit.messageHandlers.assetsHandler.postMessage("loaded"); } human.on('scene.loaded', function() { window.webkit.messageHandlers.messageHandler.postMessage("scene.loaded event"); if (!sceneLoaded) { finishLoad('scene.loaded'); } else { window.webkit.messageHandlers.messageHandler.postMessage("scene.loaded -- sceneLoaded is already true"); } }); human.on('scene.xrayEnabled', function() { window.webkit.messageHandlers.messageHandler.postMessage("scene.xrayEnabled event"); window.webkit.messageHandlers.xrayHandler.postMessage(true); }); human.on('native.share', function(data) { window.webkit.messageHandlers.messageHandler.postMessage("native.share event " + JSON.stringify(data.type)); window.webkit.messageHandlers.nativeShareHandler.postMessage(data); }); human.on('native.debugMessage', function(message) { window.webkit.messageHandlers.messageHandler.postMessage("$$$ got debug messsage " + JSON.stringify(message)); }); human.on('scene.xrayDisabled', function() { window.webkit.messageHandlers.messageHandler.postMessage("scene.xrayDisabled event"); window.webkit.messageHandlers.xrayHandler.postMessage(false); }); human.on('scene.restored', function() { window.webkit.messageHandlers.messageHandler.postMessage("scene.restored event"); window.webkit.messageHandlers.sceneRestoredHandler.postMessage("boo"); }); human.on( 'scene.objectsShown' , function(objects) { window.webkit.messageHandlers.messageHandler.postMessage("scene.objectsShown event" + JSON.stringify(objects)); window.webkit.messageHandlers.objectsShownHandler.postMessage(objects); }); human.on( 'scene.objectsSelected' , function(objects) { window.webkit.messageHandlers.messageHandler.postMessage("scene.objectsSelected event" + JSON.stringify(objects)); window.webkit.messageHandlers.selectHandler.postMessage(objects); lastPick = ''; }); var progressDone = false; var progress = 0.0; human.on('human.progress',function(data) { window.webkit.messageHandlers.messageHandler.postMessage("load human.progress event " + JSON.stringify(data)); progress = data.progress; if (data.progress === 1) { if (!sceneLoaded) { finishLoad('human.progress'); } else { window.webkit.messageHandlers.messageHandler.po ``` -------------------------------- ### Manage and Customize Anatomical Labels in Swift Source: https://context7.com/biodigital-inc/human-ios-sdk/llms.txt Demonstrates how to toggle label visibility, access existing labels, create custom labels, and handle label lifecycle events using HKHumanDelegate. ```swift import HumanKit class LabelsController { var human: HKHuman! func manageLabels() { // Show/hide all labels in scene human.labels.show() human.labels.hide() // Check if labels are currently shown let labelsVisible = human.labels.shown // Access existing labels let allLabels = human.labels.labels // [labelId: HKLabel] for (labelId, label) in allLabels { print("Label: \(label.title) attached to \(label.objectId)") print(" Position: \(label.position)") print(" Screen position: \(label.canvasPosition)") print(" Visible: \(label.shown)") } } func createCustomLabel() { // Create a new label attached to an object let customLabel = HKLabel(objectId: "heart_left_ventricle") customLabel.title = "Left Ventricle" customLabel.text = "The left ventricle pumps oxygenated blood to the body through the aorta." customLabel.labelOffset = [10, 20] // Offset from object customLabel.occludable = true // Hide when object is behind others // Add label to scene human.labels.create(label: customLabel) } func updateLabel(labelId: String) { // Get and modify existing label if let label = human.labels.labels[labelId] { label.title = "Updated Title" label.text = "Updated description text" label.labelOffset = [5, 10] // Apply changes human.labels.update(labelId: labelId) } } func removeLabel(labelId: String) { human.labels.destroy(labelId: labelId) } } // HKHumanDelegate callbacks for label events extension LabelsController: HKHumanDelegate { func human(_ view: HKHuman, labelCreated: String) { print("Label created with ID: \(labelCreated)") } func human(_ view: HKHuman, labelDestroyed: String) { print("Label removed: \(labelDestroyed)") } func human(_ view: HKHuman, labelMoved: HKLabel) { // Called when label position changes on screen print("Label \(labelMoved.labelId) moved to screen position: \(labelMoved.canvasPosition)") } func human(_ view: HKHuman, labelUpdated: HKLabel) { print("Label updated: \(labelUpdated.title)") } func human(_ view: HKHuman, labelPicked: String) { print("User tapped on label: \(labelPicked)") } func human(_ view: HKHuman, labelsShown: Bool) { print("Labels visibility changed: \(labelsShown ? "shown" : "hidden")") } } ``` -------------------------------- ### Handle Quiz Question Loaded Event Source: https://github.com/biodigital-inc/human-ios-sdk/blob/master/HumanKit.xcframework/ios-arm64_x86_64-simulator/HumanKit.framework/viewerFrame.html Listens for the 'quiz.questionLoaded' event, which fires when a new quiz question is ready. It passes the quiz data to the question loaded handler. ```javascript human.on('quiz.questionLoaded', function(quizData) { window.webkit.messageHandlers.messageHandler.postMessage("quiz.questionLoaded event " + JSON.stringify(quizData)); window.webkit.messageHandlers.quizQuestionLoadedHandler.postMessage(quizData); }); ``` -------------------------------- ### Navigate Quiz Questions and Access Data Source: https://context7.com/biodigital-inc/human-ios-sdk/llms.txt Move between quiz questions using `nextQuestion()` and `previousQuestion()`. Access the current question's details or iterate through all questions and their answers. ```swift func navigateQuiz() { let quiz = human.quiz // Move between questions quiz.nextQuestion() quiz.previousQuestion() // Access current question if let currentQuestion = quiz.currentQuestion { print("Question: \(currentQuestion.questionPrompt)") print("Type: \(currentQuestion.questionType)") } // Access all questions for (questionId, question) in quiz.questions { print("Q\(questionId): \(question.questionPrompt)") // Access answers if available if let answers = question.answers { for (answerId, answerText) in answers { print(" A\(answerId): \(answerText)") } } } } ``` -------------------------------- ### Reset and Exit Quiz Source: https://context7.com/biodigital-inc/human-ios-sdk/llms.txt Reset the quiz to its initial state using `reset()`. Exit the quiz interface using `exit()`. ```swift func resetQuiz() { human.quiz.reset() } func exitQuiz() { human.quiz.exit() } ``` -------------------------------- ### Set Viewer Language Source: https://context7.com/biodigital-inc/human-ios-sdk/llms.txt Configure the display language for anatomical labels and UI elements. This should be done via HKServices before loading models. Check if the current language uses Unicode. ```swift import HumanKit class LanguageController { func setLanguage() { // Available languages // .english, .spanish, .portuguese, .french, .italian, .german, .chinese, .japanese HKServices.shared.setLanguage(to: .spanish) // Check if current language uses Unicode (Chinese/Japanese) let isUnicode = HKServices.shared.isUnicodeLanguage() // Get available language names and codes for UI picker let languageNames = HKUI.languageNames // ["English", "EspaƱol", ...] let languageCodes = HKUI.languageCodes // ["en-US", "es-ES", ...] // Get current language code let currentCode = HKUI.languageCode() } } ``` -------------------------------- ### Handle Scene Picked Events Source: https://github.com/biodigital-inc/human-ios-sdk/blob/master/HumanKit.xcframework/ios-arm64_x86_64-simulator/HumanKit.framework/viewerFrame.html Listens for the 'scene.picked' event, which is triggered when a user selects an object in the scene. It sends the event details and the object's position to the pick handler if a new object is selected. ```javascript var lastPick = ''; human.on( 'scene.picked' , function (event) { window.webkit.messageHandlers.messageHandler.postMessage("scene.picked event " + JSON.stringify(event)); if (event.position !== undefined && event.objectId !== lastPick) { lastPick = event.objectId; window.webkit.messageHandlers.pickHandler.postMessage(event); } }); ``` -------------------------------- ### Configure UI Element Visibility Source: https://context7.com/biodigital-inc/human-ios-sdk/llms.txt Control the visibility of individual UI elements or all elements at once. Also, check for the availability of animation-related UI controls. ```swift human.ui.setOption(option: .all, value: true) // Individual UI element control human.ui.setOption(option: .anatomyLabels, value: true) // Object selection labels human.ui.setOption(option: .animation, value: true) // Play/pause controls human.ui.setOption(option: .audio, value: true) // Audio controls human.ui.setOption(option: .draw, value: false) // Drawing tool human.ui.setOption(option: .help, value: true) // Help button human.ui.setOption(option: .info, value: true) // Title/description human.ui.setOption(option: .labels, value: true) // Scene labels human.ui.setOption(option: .labelList, value: true) // Label list in info panel human.ui.setOption(option: .layers, value: true) // Layer controls human.ui.setOption(option: .nav, value: true) // Camera navigation UI human.ui.setOption(option: .objectTree, value: false) // Object tree browser human.ui.setOption(option: .onDemand, value: false) // On-demand loading human.ui.setOption(option: .quizAutoStart, value: false) // Auto-start quizzes human.ui.setOption(option: .quizLock, value: false) // Lock quiz access human.ui.setOption(option: .reset, value: true) // Reset button human.ui.setOption(option: .tools, value: true) // Tools panel human.ui.setOption(option: .tour, value: true) // Chapter navigation human.ui.setOption(option: .tutorial, value: false) // First-use tutorial // Check animation UI availability let hasPlayPause = human.ui.hasPlayPause let hasScrubber = human.ui.hasScrubber ``` -------------------------------- ### HKHumanDelegate Callbacks for Quiz Events Source: https://context7.com/biodigital-inc/human-ios-sdk/llms.txt Implement `HKHumanDelegate` methods to respond to quiz events such as availability, entry, question loading, answer selection/submission, completion, and exit. ```swift // HKHumanDelegate callbacks for quiz events extension QuizController: HKHumanDelegate { func human(_ view: HKHuman, quizEnabled: Bool) { if quizEnabled { print("Quiz is available - \(view.quiz.questions.count) questions") // Optionally auto-start the quiz view.quiz.enter() } } func human(_ view: HKHuman, quizEntered: Bool) { print("Quiz started") } func human(_ view: HKHuman, quizQuestionLoaded: String) { if let question = view.quiz.questions[quizQuestionLoaded] { print("Question loaded: \(question.questionPrompt)") } } func human(_ view: HKHuman, quizAnswerSelected: [String]) { print("User selected answers: \(quizAnswerSelected)") } func human(_ view: HKHuman, quizAnswerSubmitted: HKQuizSubmission?) { if let submission = quizAnswerSubmitted { let result = submission.correct ? "Correct!" : "Incorrect" print("Answer submitted: \(result)") } } func human(_ view: HKHuman, quizCompleted: Double) { print("Quiz completed with score: \(quizCompleted)%") } func human(_ view: HKHuman, quizExited: Bool) { print("Quiz exited") } } ``` -------------------------------- ### Create a ResearchKit 3D Anatomy Step with HumanKit Source: https://context7.com/biodigital-inc/human-ios-sdk/llms.txt Integrate 3D anatomy views into ResearchKit studies using ORKBioDigitalModelManager. Configure model appearance, add annotations, and set selection behaviors. ```swift import ResearchKit import HumanKit class ResearchKitIntegration { func createAnatomyStep() -> ORK3DModelStep { // Create the model manager let modelManager = ORKBioDigitalModelManager() // Load a predefined model type modelManager.load(modelType: .heart) // Or load by model ID // modelManager.load(modelId: "production/maleAdult/brain_v02") // Configure appearance modelManager.xray = false // X-ray mode modelManager.grayscale = false // Grayscale mode modelManager.objectsToHide = ["skin_male_adult"] // Objects to hide // Add custom annotations modelManager.annotate( objectId: "heart_left_ventricle", title: "Left Ventricle", text: "Please identify this structure" ) // Create the ResearchKit step let step = ORK3DModelStep( identifier: "anatomyStep", modelManager: modelManager ) step.title = "Identify the Structure" step.text = "Tap on the left ventricle of the heart" // Configure selection behavior modelManager.allowsSelection = true modelManager.identifiersOfObjectsToHighlight = ["heart_left_ventricle"] modelManager.highlightColor = UIColor.green return step } func createTask() -> ORKOrderedTask { let anatomyStep = createAnatomyStep() let completionStep = ORKCompletionStep(identifier: "completion") completionStep.title = "Complete" completionStep.text = "Thank you for participating" return ORKOrderedTask(identifier: "anatomyTask", steps: [anatomyStep, completionStep]) } } ``` -------------------------------- ### Customize Viewer Background Colors Source: https://context7.com/biodigital-inc/human-ios-sdk/llms.txt Set background colors using radial or linear gradients. You can also read the current background color settings. ```swift import HumanKit import UIKit class UICustomizer { var human: HKHuman! func customizeBackground() { // Set background colors with radial gradient (default) human.ui.presetBackgroundColor( top: UIColor(red: 0.2, green: 0.3, blue: 0.4, alpha: 1.0), bottom: UIColor(red: 0.1, green: 0.15, blue: 0.2, alpha: 1.0), type: .radial ) // Set linear gradient background human.ui.setBackgroundColor( top: UIColor.white, bottom: UIColor.lightGray, type: .linear ) // Read current background settings let topColor = human.ui.topColor let bottomColor = human.ui.bottomColor let bgType = human.ui.bgType // .radial or .linear } func configureUIOptions() { // Show/hide all UI elements human.ui.setOption(option: .all, value: true) // Individual UI element control human.ui.setOption(option: .anatomyLabels, value: true) // Object selection labels human.ui.setOption(option: .animation, value: true) // Play/pause controls human.ui.setOption(option: .audio, value: true) // Audio controls human.ui.setOption(option: .draw, value: false) // Drawing tool human.ui.setOption(option: .help, value: true) // Help button human.ui.setOption(option: .info, value: true) // Title/description human.ui.setOption(option: .labels, value: true) // Scene labels human.ui.setOption(option: .labelList, value: true) // Label list in info panel human.ui.setOption(option: .layers, value: true) // Layer controls human.ui.setOption(option: .nav, value: true) // Camera navigation UI human.ui.setOption(option: .objectTree, value: false) // Object tree browser human.ui.setOption(option: .onDemand, value: false) // On-demand loading human.ui.setOption(option: .quizAutoStart, value: false) // Auto-start quizzes human.ui.setOption(option: .quizLock, value: false) // Lock quiz access human.ui.setOption(option: .reset, value: true) // Reset button human.ui.setOption(option: .tools, value: true) // Tools panel human.ui.setOption(option: .tour, value: true) // Chapter navigation human.ui.setOption(option: .tutorial, value: false) // First-use tutorial // Check animation UI availability let hasPlayPause = human.ui.hasPlayPause let hasScrubber = human.ui.hasScrubber } } ``` -------------------------------- ### Search for Anatomical Models with HumanKit Source: https://context7.com/biodigital-inc/human-ios-sdk/llms.txt Use HKServices to search for anatomical models by ICD code or fetch all models associated with your developer account. Implement the HKServicesDelegate to handle model loading callbacks. ```swift import HumanKit class ModelDiscovery: HKServicesDelegate { func searchModels() { HKServices.shared.setup(delegate: self) // Fetch all models associated with your account HKServices.shared.getModels() // Search by ICD code HKServices.shared.findModel(ICD: "I25.1") // Coronary artery disease } // MARK: - HKServicesDelegate func modelsLoaded() { let models = HKServices.shared.models for model in models { print("Model: \(model.title)") print(" ID: \(model.modelId)") print(" Description: \(model.text)") print(" Thumbnail: \(model.thumbnail)") } } } ``` -------------------------------- ### Handle Quiz Answer Selected Event Source: https://github.com/biodigital-inc/human-ios-sdk/blob/master/HumanKit.xcframework/ios-arm64_x86_64-simulator/HumanKit.framework/viewerFrame.html Listens for the 'quiz.answerSelected' event when a user selects an answer. It forwards the quiz data to the answer selected handler. ```javascript human.on('quiz.answerSelected', function(quizData) { window.webkit.messageHandlers.messageHandler.postMessage("quiz.answerSelected event " + JSON.stringify(quizData)); window.webkit.messageHandlers.quizAnswerSelectedHandler.postMessage(quizData); }); ``` -------------------------------- ### Manipulate Object Visibility and State Source: https://context7.com/biodigital-inc/human-ios-sdk/llms.txt Control the visibility of objects in the scene, enable X-ray mode, and adjust layer visibility. Requires importing HumanKit and UIKit. ```swift import HumanKit import UIKit class SceneController { var human: HKHuman! func manipulateObjects() { // Get all visible object IDs let objectIds = human.scene.objectIds // Get object display names (objectId -> displayName) let objects = human.scene.objects for (id, name) in objects { print("Object: \(name) (ID: \(id))") } // Hide specific objects human.scene.hide(objectIds: ["skin_male_adult", "fat_tissue"]) // Show specific objects human.scene.show(objectIds: ["skeleton_system", "muscular_system"]) // Show or hide multiple objects at once human.scene.showOrHide(objectIds: [ "heart": true, // Show "lungs": true, // Show "skin_male_adult": false // Hide ]) // Isolate objects (hide everything except specified) human.scene.isolate(objectIds: ["cardiovascular_system"]) // Reset scene to original state human.scene.reset() // Enable/disable X-ray mode human.scene.xray(true) // Set layer visibility (0-100%) if human.scene.hasLayers { human.scene.setLayers(percent: 50) // Remove 50% of outer layers } } func colorObjects() { // Create a color configuration let redColor = HKColor() redColor.tint = UIColor.red redColor.opacity = 1.0 redColor.saturation = 0.0 // -1.0 to 1.0 redColor.brightness = 0.0 // -1.0 to 1.0 redColor.contrast = 0.0 // -1.0 to 1.0 // Apply color to an object human.scene.color(objectId: "heart_left_ventricle", color: redColor) // Create semi-transparent blue color let blueTransparent = HKColor() blueTransparent.tint = UIColor.blue blueTransparent.opacity = 0.5 human.scene.color(objectId: "lungs", color: blueTransparent) // Remove custom coloring human.scene.uncolor(objectId: "heart_left_ventricle") // Get current color of an object (result via delegate callback) human.scene.getColor(objectId: "heart") } func transformObjects() { // Translate object in 3D space [x, y, z] human.scene.translate(objectId: "heart", translate: [10.0, 0.0, 0.0]) // Rotate object [x, y, z] degrees human.scene.rotate(objectId: "heart", rotate: [0.0, 45.0, 0.0]) // Scale object [x, y, z] human.scene.scale(objectId: "heart", scale: [1.5, 1.5, 1.5]) // Combined transform human.scene.transform( objectId: "heart", translate: [10.0, 5.0, 0.0], rotate: [0.0, 90.0, 0.0], scale: [1.2, 1.2, 1.2], pivot: [0.0, 0.0, 0.0] ) } func selectionAndHighlight() { // Programmatically select objects human.scene.select(objectIds: ["heart", "lungs"]) // Deselect all objects human.scene.undoSelections() // Change highlight color human.scene.setHighlightColor(color: UIColor.orange) human.scene.resetHighlightColor() // Disable/enable highlighting human.scene.disableHighlight() human.scene.enableHighlight() // Highlight objects without user interaction human.scene.highlight(objectIds: ["heart"]) human.scene.unhighlight(objectId: "heart") // Disable/enable object picking (selection by touch) human.scene.disablePicking() human.scene.enablePicking() } func captureAndRestore() { // Capture current scene state human.scene.capture() // Make changes... human.scene.hide(objectIds: ["skin"]) human.scene.xray(true) // Restore to captured state (triggers sceneRestored callback) human.scene.restore() } func takeScreenshot() { // Capture screenshot (triggers screenshot delegate callback) human.scene.screenshot() // Share current view using iOS share sheet human.scene.share(from: CGRect(x: 100, y: 100, width: 50, height: 50)) } } // HKHumanDelegate callbacks for scene events extension SceneController: HKHumanDelegate { func human(_ view: HKHuman, objectPicked: String, position: [Double]) { if let displayName = human.scene.objects[objectPicked] { print("User tapped: \(displayName) at position \(position)") } } func human(_ view: HKHuman, objectSelected: String) { print("Object selected: \(objectSelected)") } func human(_ view: HKHuman, objectDeselected: String) { print("Object deselected: \(objectDeselected)") } func human(_ view: HKHuman, objectsShown: [String: Bool]) { ``` -------------------------------- ### Handle Authentication Redirect Source: https://github.com/biodigital-inc/human-ios-sdk/blob/master/HumanKit.framework/human-studio-next/auth.html This JavaScript snippet handles the redirection after authentication, extracting the 'id_token' and redirecting to the biodigital:// URL or the host URL if no token is found. ```javascript const url = window.location.href; const startPos = url.indexOf('id_token'); if (startPos > 0) { const googledata = url.substring(startPos); window.location.href = 'biodigital://' + googledata; } else { window.location.href = 'https://' + window.location.host; } ``` -------------------------------- ### Handle Quiz Entered Event Source: https://github.com/biodigital-inc/human-ios-sdk/blob/master/HumanKit.xcframework/ios-arm64_x86_64-simulator/HumanKit.framework/viewerFrame.html Listens for the 'quiz.entered' event, which is triggered when a quiz session begins. It forwards the quiz data to the designated handler. ```javascript human.on('quiz.entered', function(quizData) { window.webkit.messageHandlers.messageHandler.postMessage("quiz.entered event " + JSON.stringify(quizData)); window.webkit.messageHandlers.quizEnteredHandler.postMessage(quizData); }); ``` -------------------------------- ### Handle Quiz Completed Event Source: https://github.com/biodigital-inc/human-ios-sdk/blob/master/HumanKit.xcframework/ios-arm64_x86_64-simulator/HumanKit.framework/viewerFrame.html Listens for the 'quiz.completed' event when a quiz is finished. It forwards the final quiz data to the quiz completed handler. ```javascript human.on('quiz.completed', function(quizData) { window.webkit.messageHandlers.messageHandler.postMessage("quiz.completed event " + JSON.stringify(quizData)); window.webkit.messageHandlers.quizCompletedHandler.postMessage(quizData); }); ``` -------------------------------- ### Control Animation Playback Source: https://context7.com/biodigital-inc/human-ios-sdk/llms.txt Use HKTimeline to play, pause, unpause, and seek animations. Check if the model has animation before controlling it. ```swift import HumanKit class TimelineController { var human: HKHuman! var animationTimer: Timer? func controlAnimation() { // Check if model has animation if human.timeline.playing { print("Animation is playing") } // Play animation from beginning human.timeline.play() // Pause animation human.timeline.pause() // Resume paused animation human.timeline.unpause() // Get animation duration and current time let duration = human.timeline.duration let currentTime = human.timeline.currentTime print("Animation: \(currentTime)/\(duration) seconds") // Jump to specific time human.timeline.moveToTime(time: 5.0) } func navigateChapters() { // Get list of chapters let chapterIds = human.timeline.chapterList let chapters = human.timeline.chapters for chapterId in chapterIds { if let chapter = chapters[chapterId] { print("Chapter \(chapter.index): \(chapter.title)") print(" Description: \(chapter.text)") print(" Animated: \(chapter.animated)") print(" Duration: \(chapter.duration) seconds") print(" Loops: \(chapter.loops)") } } // Navigate to next/previous chapter human.timeline.nextChapter() human.timeline.prevChapter() // Jump to specific chapter by ID if let firstChapterId = chapterIds.first { human.timeline.moveToChapter(chapterID: firstChapterId) } // Loop a specific chapter's animation if let chapterId = chapterIds.first { human.timeline.loop(chapterId: chapterId) } // Access current chapter if let currentChapter = human.timeline.currentChapter { print("Current chapter: \(currentChapter.title)") } } func setupAnimationSlider(slider: UISlider) { // Configure slider based on animation duration slider.minimumValue = 0 slider.maximumValue = human.timeline.duration slider.value = human.timeline.currentTime // Update slider periodically during playback animationTimer = Timer.scheduledTimer(withTimeInterval: 0.03, repeats: true) { _ in slider.value = self.human.timeline.currentTime } } @objc func sliderValueChanged(_ slider: UISlider) { human.timeline.pause() human.timeline.moveToTime(time: slider.value) } } // HKHumanDelegate callbacks for timeline events extension TimelineController: HKHumanDelegate { func human(_ view: HKHuman, chapterTransition: String) { if let chapter = human.timeline.chapters[chapterTransition] { print("Entered chapter: \(chapter.title)") // Update UI based on chapter properties if chapter.animated { // Show animation controls } } } func human(_ view: HKHuman, animationComplete: Bool) { print("Animation finished playing") animationTimer?.invalidate() } func human(_ view: HKHuman, timelineUpdated: HKTimeline) { print("Timeline updated - current time: \(timelineUpdated.currentTime)") } } ``` -------------------------------- ### Submit Quiz Answers Source: https://context7.com/biodigital-inc/human-ios-sdk/llms.txt Submit the user's selected answers for the current question using `submit(questionId:answerIds:)`. Retrieve the user's selections from `currentAnswers`. ```swift func submitAnswer() { let quiz = human.quiz // Submit answer(s) for current question let selectedAnswerIds = quiz.currentAnswers // User's selected answers quiz.submit(questionId: quiz.activeQuestionId, answerIds: selectedAnswerIds) } ``` -------------------------------- ### Retrieve Quiz Results Source: https://context7.com/biodigital-inc/human-ios-sdk/llms.txt Check if the quiz is completed using `completed`. Access overall scores and review individual submissions, including correct answers and user's selections. ```swift func getQuizResults() { let quiz = human.quiz if quiz.completed { print("Quiz completed!") print("Score: \(quiz.score)%") print("Correct: \(quiz.correctTotal)/\(quiz.scorableTotal)") print("Incorrect: \(quiz.incorrectTotal)") // Review submissions for submission in quiz.submissions { print("Question \(submission.questionId)") print(" Your answers: \(submission.answerIds)") print(" Correct answers: \(submission.correctAnswerIds)") print(" Result: \(submission.correct ? "Correct" : "Incorrect")") } } } ``` -------------------------------- ### Registering BioDigital Human Event Listeners Source: https://github.com/biodigital-inc/human-ios-sdk/blob/master/HumanKit.xcframework/ios-arm64/HumanKit.framework/viewerFrame.html Registers various event listeners for timeline, scene, and quiz interactions, forwarding them to native iOS handlers. ```javascript stMessage("not calling scene.info"); } window.webkit.messageHandlers.messageHandler.postMessage("$$$$$$$$ load human.progress DONE " + JSON.stringify(data)); } }); // send an animation update message human.on('timeline.playing', function(animationTime) { window.webkit.messageHandlers.timelineHandler.postMessage(animationTime.time); }); human.on('timeline.completed', function() { window.webkit.messageHandlers.messageHandler.postMessage("timeline.completed event "); window.webkit.messageHandlers.timelineHandler.postMessage('done'); }); var lastPick = ''; human.on( 'scene.picked' , function (event) { window.webkit.messageHandlers.messageHandler.postMessage("scene.picked event " + JSON.stringify(event)); if (event.position !== undefined && event.objectId !== lastPick) { lastPick = event.objectId; window.webkit.messageHandlers.pickHandler.postMessage(event); } }); human.on('scene.ready', function () { sceneLoaded = false; window.webkit.messageHandlers.messageHandler.postMessage("scene.ready event"); human.send('scene.metadata', function(metadata) { window.webkit.messageHandlers.messageHandler.postMessage("scene.metadata event " + JSON.stringify(metadata)); window.webkit.messageHandlers.sceneMetaHandler.postMessage(metadata); }); }); human.on('quiz.entered', function(quizData) { window.webkit.messageHandlers.messageHandler.postMessage("quiz.entered event " + JSON.stringify(quizData)); window.webkit.messageHandlers.quizEnteredHandler.postMessage(quizData); }); human.on('quiz.exited', function() { window.webkit.messageHandlers.messageHandler.postMessage("quiz.exited event"); window.webkit.messageHandlers.quizExitedHandler.postMessage(true); }); human.on('quiz.questionLoaded', function(quizData) { window.webkit.messageHandlers.messageHandler.postMessage("quiz.questionLoaded event " + JSON.stringify(quizData)); window.webkit.messageHandlers.quizQuestionLoadedHandler.postMessage(quizData); }); human.on('quiz.answerSelected', function(quizData) { window.webkit.messageHandlers.messageHandler.postMessage("quiz.answerSelected event " + JSON.stringify(quizData)); window.webkit.messageHandlers.quizAnswerSelectedHandler.postMessage(quizData); }); human.on('quiz.answerSubmitted', function(quizData) { window.webkit.messageHandlers.messageHandler.postMessage("quiz.answerSubmitted event " + JSON.stringify(quizData)); window.webkit.messageHandlers.quizAnswerSubmittedHandler.postMessage(quizData); }); human.on('quiz.completed', function(quizData) { window.webkit.messageHandlers.messageHandler.postMessage("quiz.completed event " + JSON.stringify(quizData)); window.webkit.messageHandlers.quizCompletedHandler.postMessage(quizData); }); window.webkit.messageHandlers.messageHandler.postMessage("API object created, listeners registered params: " + window.bd_urlparams); window.webkit.messageHandlers.humanHandler.postMessage("ready"); var iframe = document.getElementById("embeddedHuman"); iframe.src = "http://localhost:" + window.sdkPort + "/?id=models/9OR" + window.bd_urlparams; ```