Skip to content

Custom UI Sample iOS

Hudson_BuildService edited this page Jan 9, 2026 · 1 revision
class ViewController: UIViewController {
    private var surveyFrame: SurveyFrame!
    private var contentLayout: UIStackView!
    private var pageLayout: UIStackView!
    private var buttonLayout: UIStackView!
    private var nextButton: UIButton!
    private var backButton: UIButton!
    private var quitButton: UIButton!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        createViews()
        
        // Binding Program Lifecycle Listener
        let serverId = ConfirmitServer.uk.serverId
        let programKey = "<Program Key>"
        TriggerSDK.setCallback(serverId: serverId, programKey: programKey, callback: self)

        // Initialize Survey Frame
        surveyFrame = SurveyFrame()
        // Binding Survey Lifecycle Listener to be notified events
        surveyFrame.delegate = self
    }
    
    private func createViews() {
        let bound = view.bounds
        contentLayout = UIStackView(frame: CGRect(x: 40, y: 40, width: bound.width - 80, height: 400))
        contentLayout.axis = .vertical
        
        pageLayout = UIStackView()
        pageLayout.axis = .vertical
        
        buttonLayout = UIStackView()
        buttonLayout.axis = .horizontal
        buttonLayout.distribution = .fillEqually
        
        nextButton = createNavButton(title: "Next", tag: 0)
        backButton = createNavButton(title: "Back", tag: 1)
        quitButton = createNavButton(title: "Quit", tag: 2)
        
        buttonLayout.addArrangedSubview(backButton)
        buttonLayout.addArrangedSubview(quitButton)
        buttonLayout.addArrangedSubview(nextButton)
        
        contentLayout.addArrangedSubview(pageLayout)
        contentLayout.addArrangedSubview(buttonLayout)
        
        view.addSubview(contentLayout)
    }
    
    @objc func onNavButtonPressed(sender: UIButton!) {
        switch sender.tag {
        case 0:
            surveyFrame.next()
        case 1:
            surveyFrame.back()
        case 2:
            surveyFrame.quit(upload: false)
        default:
            break
        }
    }
    
    private func createNavButton(title: String, tag: Int) -> UIButton {
        let button = UIButton(type: .roundedRect)
        button.setTitle(title, for: .normal)
        button.tag = tag
        button.addTarget(self, action: #selector(onNavButtonPressed(sender:)), for: .touchUpInside)
        
        return button
    }
    
    private func showAlert(message: String) {
        let alertController = UIAlertController(title: message, message: "", preferredStyle: .alert)
        alertController.addAction(UIAlertAction(title: "OK", style: .default))
        present(alertController, animated: true, completion: nil)
    }
}

extension ViewController: ProgramCallback {
    func onScenarioLoad(triggerInfo: TriggerInfo, error: Error?) {
        // On scenario script starts
    }
    
    func onScenarioError(triggerInfo: TriggerInfo, error: Error) {
        // When scenario scripting contains error
    }
    
    func onSurveyDownloadCompleted(triggerInfo: TriggerInfo, surveyId: String, error: Error?) {
        // Survey download completed
    }

    func onWebSurveyStart(surveyWebView: SurveyWebViewController) {
        // Show web survey
    }
    
    func onSurveyStart(config: SurveyFrameConfig) {
        try! surveyFrame.load(config: config) // This is example. In your project, error should be handled
        surveyFrame.start()
    }
    
    func onAppFeedback(triggerInfo: TriggerInfo, data: [String: String?]) {
        // When AppFeedback action triggered
    }
}

extension ViewController: SurveyFrameDelegate {
    func onSurveyPageReady(page: SurveyPage) {
        // Clear question views
        pageLayout.arrangedSubviews.forEach { $0.removeFromSuperview() }
        
        // Set visibilities for navigation buttons
        nextButton.isHidden = !page.showForward
        backButton.isHidden = !page.showBackward
        
        let questions = page.questions
        // Loop through question in current survey page, and generate Views
        for question in questions {
            // If question type is TEXT
            if question.nodeType == .text {
                // Question model is passed to view for:
                // 1. To display the question
                // 2. Interaction like answering question
                let textQuestion = TextQuestionLayout()
                textQuestion.setup(question: question as! TextQuestion)
                pageLayout.addArrangedSubview(textQuestion)
            }
            
            // ... More question type integrations
        }
    }
    
    func onSurveyErrored(values: [String: String?], error: Error) {
        showAlert(message: "Survey contains error")
    }
    
    func onSurveyFinished(values: [String: String?]) {
        showAlert(message: "Survey completed")
    }
    
    func onSurveyQuit(values: [String: String?]) {
        showAlert(message: "Survey quit by user")
    }
}

class TextQuestionLayout: UIView, UITextFieldDelegate {
    private var question: TextQuestion!
    
    func setup(question: TextQuestion) {
        self.question = question
        
        let label = UILabel(frame: CGRect(x: 0, y: 0, width: 100, height: 50))
        label.font = UIFont.systemFont(ofSize: 16)
        label.text = question.title.get()
        
        let textField = UITextField(frame: CGRect(x: 0, y: 70, width: 300, height: 200))
        textField.font = UIFont.systemFont(ofSize: 16)
        textField.borderStyle = .roundedRect
        textField.delegate = self
        
        addSubview(label)
        addSubview(textField)
    }
    
    func textFieldDidEndEditing(_ textField: UITextField) {
        question.setValue(value: textField.text)
    }
}

Clone this wiki locally