[Mobile Lab] Week 02: Lockscreen

The Assignment Description:

  • Create original UI design
  • Create a new “Single View App” Xcode project for assignment.
  • Needs to have 6 sequential inputs
  • Needs to display status messages when appropriate, e.g. “Unlocked”, “Try Again”, etc.
  • Only 3 attempts possible before app needs to be reset/restarted

This time, I also designed the UI with Sketch:

I also needed piano key sounds, so I recorded them myself using GarageBand.

I didn’t know that there was a feature that I can use for rotating the canvase horizontally in xCode, so I worked on vertical canvas.

Here is the code:

[ViewController.swift]

import UIKit
import AVFoundation

class ViewController: UIViewController {

    var player: AVAudioPlayer?
    
    @IBOutlet weak var key_b3: UIButton!
    @IBOutlet weak var key_c4: UIButton!
    @IBOutlet weak var key_c4s: UIButton!
    @IBOutlet weak var key_d4: UIButton!
    @IBOutlet weak var key_d4s: UIButton!
    @IBOutlet weak var key_e4: UIButton!
    @IBOutlet weak var key_f4: UIButton!
    @IBOutlet weak var key_f4s: UIButton!
    @IBOutlet weak var key_g4: UIButton!
    @IBOutlet weak var key_g4s: UIButton!
    @IBOutlet weak var key_a4: UIButton!
    @IBOutlet weak var key_a4s: UIButton!
    @IBOutlet weak var key_b4: UIButton!
    @IBOutlet weak var key_c5: UIButton!
    
    @IBOutlet weak var lbl: UIImageView!
    @IBOutlet weak var btn_reset: UIButton!
    
    // UNLOCK SEQEUNCE.
    // Used to compare to input pattern.
    let lockPattern = [6, 10, 6, 1, 1, 13]
    
    // Array to capture input from button taps.
    var inputPattern = [Int]()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        resetScreen()
    }

    func resetScreen() {
        // Initialize status label.
        lbl.isHidden = false
        lbl.image = UIImage(named: "label_normal.png")
        
        // Reset button is initially hidden.
        btn_reset.isHidden = true
        
        // Flush input pattern.
        inputPattern.removeAll()
    }
    
    // Logic for different stages of the input sequence.
    func processInputPattern(inputNumber: Int) {
        
        // Append passed in inputNumber into input pattern array.
        inputPattern.append(inputNumber)
        for input in inputPattern {
            print(input)
        }
        
        // Check where we are in the sequence by inspecting array count.
        if inputPattern.count == 6 {
            
            // Check if pattern matches or need to try again.
            if inputPattern == lockPattern {
                
                // Update status message.
                lbl.isHidden = true
                btn_reset.isHidden = false
            } else {
                // Update status message.
                lbl.image = UIImage(named: "label_fail.png")
                
                // Flush input pattern.
                inputPattern.removeAll()
            }
        }
        
    }

    @IBAction func pressB3(_ sender: UIButton) {
        key_b3.setImage(UIImage(named: "whitep.png"), for: UIControlState.normal)
        playSoundMP3(filename: "b3")
        lbl.image = UIImage(named: "label_b3.png")
    }
    @IBAction func releaseB3(_ sender: UIButton) {
        key_b3.setImage(UIImage(named: "white.png"), for: UIControlState.normal)
        processInputPattern(inputNumber: 0)
    }
    
    @IBAction func pressC4(_ sender: UIButton) {
        key_c4.setImage(UIImage(named: "whitep.png"), for: UIControlState.normal)
        playSoundMP3(filename: "c4")
        lbl.image = UIImage(named: "label_c4.png")
    }
    @IBAction func releaseC4(_ sender: UIButton) {
        key_c4.setImage(UIImage(named: "white.png"), for: UIControlState.normal)
        processInputPattern(inputNumber: 1)
    }
    
    @IBAction func pressC4s(_ sender: UIButton) {
        key_c4s.setImage(UIImage(named: "blackp.png"), for: UIControlState.normal)
        playSoundMP3(filename: "c4s")
        lbl.image = UIImage(named: "label_c4s.png")
    }
    @IBAction func releaseC4s(_ sender: UIButton) {
        key_c4s.setImage(UIImage(named: "black.png"), for: UIControlState.normal)
        processInputPattern(inputNumber: 2)
    }
    
    @IBAction func pressD4(_ sender: UIButton) {
        key_d4.setImage(UIImage(named: "whitep.png"), for: UIControlState.normal)
        playSoundMP3(filename: "d4")
        lbl.image = UIImage(named: "label_d4.png")
    }
    @IBAction func releaseD4(_ sender: UIButton) {
        key_d4.setImage(UIImage(named: "white.png"), for: UIControlState.normal)
        processInputPattern(inputNumber: 3)
    }
    
    @IBAction func pressD4s(_ sender: UIButton) {
        key_d4s.setImage(UIImage(named: "blackp.png"), for: UIControlState.normal)
        playSoundMP3(filename: "d4s")
        lbl.image = UIImage(named: "label_d4s.png")
    }
    @IBAction func releaseD4s(_ sender: UIButton) {
        key_d4s.setImage(UIImage(named: "black.png"), for: UIControlState.normal)
        processInputPattern(inputNumber: 4)
    }
    
    @IBAction func pressE4(_ sender: UIButton) {
        key_e4.setImage(UIImage(named: "whitep.png"), for: UIControlState.normal)
        playSoundMP3(filename: "e4")
        lbl.image = UIImage(named: "label_e4.png")
    }
    @IBAction func releaseE4(_ sender: UIButton) {
        key_e4.setImage(UIImage(named: "white.png"), for: UIControlState.normal)
        processInputPattern(inputNumber: 5)
    }
    
    @IBAction func pressF4(_ sender: UIButton) {
        key_f4.setImage(UIImage(named: "whitep.png"), for: UIControlState.normal)
        playSoundMP3(filename: "f4")
        lbl.image = UIImage(named: "label_f4.png")
    }
    @IBAction func releaseF4(_ sender: UIButton) {
        key_f4.setImage(UIImage(named: "white.png"), for: UIControlState.normal)
        processInputPattern(inputNumber: 6)
    }
    
    @IBAction func pressF4s(_ sender: UIButton) {
        key_f4s.setImage(UIImage(named: "blackp.png"), for: UIControlState.normal)
        playSoundMP3(filename: "f4s")
        lbl.image = UIImage(named: "label_f4s.png")
    }
    @IBAction func releaseF4s(_ sender: UIButton) {
        key_f4s.setImage(UIImage(named: "black.png"), for: UIControlState.normal)
        processInputPattern(inputNumber: 7)
    }
    
    @IBAction func pressG4(_ sender: UIButton) {
        key_g4.setImage(UIImage(named: "whitep.png"), for: UIControlState.normal)
        playSoundMP3(filename: "g4")
        lbl.image = UIImage(named: "label_g4.png")
    }
    @IBAction func releaseG4(_ sender: UIButton) {
        key_g4.setImage(UIImage(named: "white.png"), for: UIControlState.normal)
        processInputPattern(inputNumber: 8)
    }
    
    @IBAction func pressG4s(_ sender: UIButton) {
        key_g4s.setImage(UIImage(named: "blackp.png"), for: UIControlState.normal)
        playSoundMP3(filename: "g4s")
        lbl.image = UIImage(named: "label_g4s.png")
    }
    @IBAction func releaseG4s(_ sender: UIButton) {
        key_g4s.setImage(UIImage(named: "black.png"), for: UIControlState.normal)
        processInputPattern(inputNumber: 9)
    }
    
    @IBAction func pressA4(_ sender: UIButton) {
        key_a4.setImage(UIImage(named: "whitep.png"), for: UIControlState.normal)
        playSoundMP3(filename: "a4")
        lbl.image = UIImage(named: "label_a4.png")
    }
    @IBAction func releaseA4(_ sender: UIButton) {
        key_a4.setImage(UIImage(named: "white.png"), for: UIControlState.normal)
        processInputPattern(inputNumber: 10)
    }
    
    @IBAction func pressA4s(_ sender: UIButton) {
        key_a4s.setImage(UIImage(named: "blackp.png"), for: UIControlState.normal)
        playSoundMP3(filename: "a4s")
        lbl.image = UIImage(named: "label_a4s.png")
    }
    @IBAction func releaseA4s(_ sender: UIButton) {
        key_a4s.setImage(UIImage(named: "black.png"), for: UIControlState.normal)
        processInputPattern(inputNumber: 11)
    }
    
    @IBAction func pressB4(_ sender: UIButton) {
        key_b4.setImage(UIImage(named: "whitep.png"), for: UIControlState.normal)
        playSoundMP3(filename: "b4")
        lbl.image = UIImage(named: "label_b4.png")
    }
    @IBAction func releaseB4(_ sender: UIButton) {
        key_b4.setImage(UIImage(named: "white.png"), for: UIControlState.normal)
        processInputPattern(inputNumber: 12)
    }
    
    @IBAction func pressC5(_ sender: UIButton) {
        key_c5.setImage(UIImage(named: "whitep.png"), for: UIControlState.normal)
        playSoundMP3(filename: "c5")
        lbl.image = UIImage(named: "label_c5.png")
    }
    @IBAction func releaseC5(_ sender: UIButton) {
        key_c5.setImage(UIImage(named: "white.png"), for: UIControlState.normal)
        processInputPattern(inputNumber: 13)
    }
    
    @IBAction func handle_reset(_ sender: UIButton) {
        resetScreen()
    }
    
    // Play a mp3 sound file.
    func playSoundMP3(filename: String) {
        guard let url = Bundle.main.url(forResource: filename, withExtension: "mp3") else { return }
        
        do {
            try AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayback)
            try AVAudioSession.sharedInstance().setActive(true)
            
            player = try AVAudioPlayer(contentsOf: url, fileTypeHint: AVFileType.mp3.rawValue)
            
            guard let player = player else { return }
            player.play()
        } catch let error {
            print(error.localizedDescription)
        }
    }
    
}

 

Follows are the simulation results:

There were some issues:

  • The value of margins/widths/heights of the images were all set in absolute. Therefore, the app’s ratio appears as I intended only on my phone (iPhone 7 Plus)
    I wonder if there is a way that I can set the constraints with relative values.
  • Although I didn’t change the colour of the pressed keys, they darkened themselves. I found that is is a default transition effect for ‘mouse down’ action.
    I would like to know how I can off this default transition.
  • Since I worked on vertical canvas, I had to rotate all the assets in -90º degrees. It was possible to prepare rotated the image assets, but I couldn’t rotate the Label UI element in XCode.
    For this reason, I had to change all the label elements to image elements. I haven’t yet figured out how to rotate the elements.

There are the project file and all the image/sound assets in my Github repo.

Leave a Reply

Your email address will not be published. Required fields are marked *