Creating custom ORKStep with WKWebView

2020-07-24 04:43发布

问题:

I'm relatively new to iOS development, and currently using Swift to develop a prototype ResearchKit app. One of the requirements is to embed a WKWebView in an ORKTask consisting of three steps: ORKQuestionStep, ORKWebViewStep, ORKCompletionStep. I can't seem to find much information on how to subclass ORKStep and ORKStepViewController using Swift to create a custom step. Can someone guide me in the right direction for how to subclass ORKStep and ORKStepViewController to display a WKWebView using Swift?

Thank you in advance!

回答1:

Yuan Zhu's answer in ResearchKit-Users mailing list:

I guess you can:

  1. Create an ORKActiveStep and configure it properly.
  2. Implement the - (void)taskViewController:(ORKTaskViewController *)taskViewController stepViewControllerWillAppear:(ORKStepViewController *)stepViewController delegate method.
  3. Inject your webView into the ORKActiveStepViewController through its customView property.


回答2:

I have build my own ORKWebView as follows. I am happy about comments on how to improve that.

1. Subclass an ORKActiveStep

class Javascript_Step : ORKActiveStep {
    static func stepViewControllerClass ( ) -> Javascript_Step_View_Controller.Type {
        return Javascript_Step_View_Controller.self
    }    
}

2. Subclass ORKActiveStepViewController

Here you will modify your WebView and it's components. For quitting the WebView and passing the result, I use Javascript.

import Foundation
import ResearchKit
import WebKit

class Javascript_Step_View_Controller : ORKActiveStepViewController, WKScriptMessageHandler {
    weak var webView: WKWebView!
    var html : String = "<!DOCTYPE html><html><head>  <meta charset=\"UTF-8\">  <title>JS Widget</title>  <style type=\"text/css\">    h1 {      color: red    }  </style></head><body>  <h1>Test</h1>  <input type=\"button\" value=\"Say hello\" onClick=\"Finish_This_Widget('Result is String')\" />  <script type=\"text/javascript\">    function Finish_This_Widget(string) {      App.Finish_Widget(string)    }  </script></body></html>"

    // Here the result message posted by Javascript can be handled
    func userContentController(userContentController: WKUserContentController, didReceiveScriptMessage message: WKScriptMessage) {
        if let result = message.body as? String {
            print("userContentController: \(result)")
            // With the incoming reult of your webview, the ORKActiveStep timer will be started
            start()
        }
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        // Here you set the function name to call from javascript
        let controller = WKUserContentController()
        controller.addScriptMessageHandler(self, name: "Finish_Widget")

        let config = WKWebViewConfiguration()
        config.userContentController = controller

        let frame = CGRectMake(20, 20, 200, 200)
        let webView = WKWebView(frame: frame, configuration: config)
        self.customView = webView

        // Set the view constraints (warning message with following unproper settings)
        self.customView?.superview!.translatesAutoresizingMaskIntoConstraints = false
        view.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|-[demoView]-|", options: NSLayoutFormatOptions(rawValue: 0), metrics: nil, views: ["demoView": webView]))        view.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|-[demoView]-|", options: NSLayoutFormatOptions(rawValue: 0), metrics: nil, views: ["demoView": webView]))

        // Load the html string
        webView.loadHTMLString(html, baseURL: NSBundle.mainBundle().bundleURL)

        webView.translatesAutoresizingMaskIntoConstraints = false
        self.webView = webView
    }

}

3. Load the task with your WebViewStep

In step 2 we saw the start()-function call we need be able to finish the step and to switch to the next one. That's why we want that the timer is finished immediately after we started it.

// This functions gives you the Step you can work with finanlly
func Javascript_Widget_Step ( identifier : String, title : String, text : String ) -> ORKActiveStep {
    let active_Step = Javascript_Step(identifier: identifier)

    // Set title and text for the step (which is optional)
    active_Step.title = title
    active_Step.text = text

    // set stepduration to a minimum -> after javascript function call, step will be finished
    active_Step.stepDuration = 0.001

    // if this is false, it will not switch to the next step after the javascript call automatically
    active_Step.shouldContinueOnFinish = true
    return active_Step
}

Open Question

How can I inject the html? If I don't want a static WebView with always the same html, at which point can I set the html string from outside?