궁금한 내용을 검색해보세요!
이 포스팅은 쿠팡 파트너스 활동의 일환으로, 이에 따른 일정액의 수수료를 제공받습니다.
서근 개발노트
티스토리에 팔로잉
SWIFT/Udemy iOS

[Udemy] 섹션8: Egg Timer / ProgressView ( Control Flow and Optionals )

서근
QUOTE THE DAY

-
Written by SeogunSEOGUN

반응형

 

목표 : Timer 사용, Optional의 이해, ProgressBar/View 사용

https://github.com/appbrewery/EggTimer-iOS13

Egg Timer Project

main storyboard에서 View Controller Scene을 보면 우리는 아래에서 위로 본다고 생각하면 된다. 이미지를 보면 Image ViewButton 보다 아래 있으므로 ButtonImage View에 의해 숨겨지게 된다. 

Minimum Font Size

화면 사이즈가 각 아이폰 별로 다를 때 width/height의 크기가 줄어듬에 따라 label의 텍스트가 잘리는 경우

 

  -  두 가지 방법

1. InspectorLines를 0으로 설정

2. AutoshrinkMinimum Font Size로 설정 후, 최솟값 설정(Font Size)

IBAction 연결

main storyboard의 이미지를 View Controller로 연결해주겠음!

ButtonAction type으로 설정하고 TypeAny가 아닌 UIButton으로 수정해준다. 그리고 모든 이미지를 @IBAction에 모두 연결해준다.

class ViewController: UIViewController {

    @IBAction func pressedTimer(_ sender: UIButton) {
        print(sender.currentTitle!)
    }
}

버튼을 눌렀을 때 정확한 title 이름이 나오게 하려면 sender.currentTitle을 사용하면 아래와 같이 디버그 창에 정확한 이름이 나오게 된다.

If, else if, else  /  Switch / Dictionary / Optional

If, else if, else 

if문을 사용해서 각각의 이미지를 클릭했을 때 time이 디버그에 표시되려면 아래와 같이 코드를 구현하면 된다.

class ViewController: UIViewController {
    
    let softTime = 5
    let mediumTime = 7
    let HardTime = 12

    @IBAction func pressedTimer(_ sender: UIButton) {
        
        let hardness = sender.currentTitle!
        
        if hardness == "Soft" {
            print(softTime)
        } else if hardness == "Medium" {
            print(mediumTime)
        } else {
            print(HardTime)
        }
    }
}

Switch

if문을 switch문으로 바꾸면 아래와 같다.

        switch hardness {
        case "Soft":
            print(softTime)
        case "Medium":
            print(mediumTime)
        case "Hard":
            print(HardTime)
        default:
            print("아무것도 선택되지 않았습니다.")
        }

Dictionary

이번에 할 Dictionary Challenge는 sotockTickers에 아래와 같은 Dictionary를 추가해야 한다

"Work": "Slack Technologies Inc"
"Boom": "DMC Global Inc"
func exercise() {

    //Don't change this
    var stockTickers: [String: String] = [
        "APPL" : "Apple Inc", 
    	"HOG": "Harley-Davidson Inc", 
    	"BOOM": "Dynamic Materials", 
    	"HEINY": "Heineken", 
    	"BEN": "Franklin Resources Inc"
    ]
    
    //Write your code here.


     //Don't modify this
    print(stockTickers["WORK"]!)
    print(stockTickers["BOOM"]!)
}

완성

    //Write your code here.
    stockTickers["WORK"] = "Slack Technologies Inc"
    stockTickers["BOOM"] = "DMC Global Inc"

Timer

swift에서 timer의 사용 해려고 한다. 구글링을 통해 아래와 같은 솔루션을 찾았다. ( countdown timer swift stack overflow )

첫 번째 솔루션

var secondsRemaining = 30

override func viewDidLoad() {
    super.viewDidLoad()

    Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(updateCounter), userInfo: nil, repeats: true)
}

@objc func updateCounter() {
    //example functionality
    if secondsRemaining > 0 {
        print("\(counter)초")
        secondsRemaining -= 1
    }
}

 

1. secondsRemaining 변수 생성 

2. TimertimeInterval은 1초마다 업데이트를 원하기 때문에 1.0으로 설정

3. selectorObjective-C에서 사용하던 것을 가져왔기 때문에 함수에 @objc를 사용한다.

4. updateCounter() 함수는 secondsRemaining가 0보다 크면 secondsRemaining를 -1 하면서 print 한다.

두 번째 솔루션

var secondsRemaining = 30
    
@IBAction func startTimer(_ sender: UIButton) {
        
    Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { (Timer) in
        if self.secondsRemaining > 0 {
            print ("\(self.secondsRemaining)초")
            self.secondsRemaining -= 1
        } else {
            Timer.invalidate()
        }
    }
            
}

 

1. secondsRemaining 변수 생성

2. TimertimeInterval은 1초마다 업데이트를 원하기 때문에 1.0으로 설정

3. if문을 Timer안에 사용하여 secondsRemaining가 0보다 크면 secondsRemaining를 -1 하면서 print 한다. 설정한 time이 끝나면 else문으로 가서 Timerinvalidate() 되게 한다. ( invalidate = 타이머를 멈춤/취소시킴 )

Timer 사용

class ViewController: UIViewController {
    
    let eggTimes = ["Soft": 5, "Medium": 7, "HardTime": 12]
    var secondsRemaining = 0
    
    @IBAction func pressedTimer(_ sender: UIButton) {

        let hardness = sender.currentTitle!
        secondsRemaining = eggTimes[hardness]!
        
        print("시작")
        // 타이머 구현부
        Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { (Timer) in
            if self.secondsRemaining > 0 {
                print ("\(self.secondsRemaining)초")
                self.secondsRemaining -= 1
            } else {
                Timer.invalidate()
                print("완성")
            }
        }
    }
}

문제점

문제점 : 타이머가 끝나기 전에 다른 버튼을 연속해서 클릭할 때 타이머가 취소되고 재 실행되는 것이 아닌 중복으로 실행이 된다.

 

해결방법 : 새로운 timer 가변 코드를 생성하여 타이머와 동일하게 설정해주고 클릭 시 취소되는 코드를 넣어줌

TIP
 
 

invalidate?
타이머가 다시 실행되는 것을 중지하고 런 루프에서 제거를 요청한다. 이 메서드는 개체 에서 타이머를 제거하는 유일한 방법 이다.

class ViewController: UIViewController {
    
    let eggTimes = ["Soft": 5, "Medium": 7, "HardTime": 12]
    var secondsRemaining = 0
    var timer = Timer()
    
    @IBAction func pressedTimer(_ sender: UIButton) {
        
        // 버튼을 클릭했을때 취소시킴
        timer.invalidate()
        
        let hardness = sender.currentTitle!
        secondsRemaining = eggTimes[hardness]!
        
        print("시작")
        // 타이머 구현부 ( tiemr = )
        timer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { (Timer) in
            if self.secondsRemaining > 0 {
                print ("\(self.secondsRemaining)초")
                self.secondsRemaining -= 1
            } else {
                Timer.invalidate()
                print("완성")
            }
        }
    }
}

타이머 종료 후 label text 변경

타이머가 종료되면 Label Text가 변경되도록 해줄 수 도 있다.

        // 타이머 구현부 ( tiemr = )
        timer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { (Timer) in
            if self.secondsRemaining > 0 {
                print ("\(self.secondsRemaining)초")
                self.secondsRemaining -= 1
            } else {
                Timer.invalidate()
                self.timerLabel.text = "계란이 다 삶아졌습니다!"
            }

ProgressView

main storyboardprogress View를 추가하고 containersheight, tint color를 추가해준다.

이것을 ViewController에 연결해서 progressView를 꽉 채워보겠음!

@IBOutlet으로 연결 > progressBar.progress = 1.0

    @IBOutlet weak var progressBar: UIProgressView!
    @IBOutlet weak var timerLabel: UILabel!
    @IBAction func pressedTimer(_ sender: UIButton) {
        
        progressBar.progress = 1.0
        
        ...
        
     }

progressViewTimer와 연결하려면 백분율을 사용해야 함.

import UIKit

class ViewController: UIViewController {
    
    let eggTimes = ["Soft": 5, "Medium": 7, "HardTime": 12]
    var secondsPassed = 0
    var totalTime = 0
    var timer = Timer()
    
    @IBOutlet weak var progressBar: UIProgressView!
    @IBOutlet weak var timerLabel: UILabel!
    @IBAction func pressedTimer(_ sender: UIButton) {
        
        // 버튼을 클릭했을때 취소시킴
        timer.invalidate()
        
        let hardness = sender.currentTitle!
        totalTime = eggTimes[hardness]!
        
        // 재실행 됐을때 초기화
        progressBar.progress = 0.0
        secondsPassed = 0
        timerLabel.text = hardness
        
        
        // 타이머 구현부 ( tiemr = )
        timer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { (Timer) in
            // secondsPassed = 0 이고 totalTime이 hardness(3) 이므로
            if self.secondsPassed < self.totalTime {
                // secondsPassed + 1 하고 이것이 hardness까지 온다면
                self.secondsPassed += 1
                // secondsPassed / totalTime
                self.progressBar.progress = Float(self.secondsPassed)/Float(self.totalTime)
                
            } else {
                Timer.invalidate()
                self.timerLabel.text = "계란이 다 삶아졌습니다!"
            }
        }
    }
}

진행시간 표시

1. ProgressView아래 Label 추가

2. Containers 설정

3. viewController@IBOutlet 연결

4. 타이머 구현부에 self.progressTime.text로 원하는 텍스트 추가 ( text는 String이지만 secondsPassed는 Int 타입이므로 타입 변환을 해줘야 한다.)

5. 타이머가 끝나면 표시될 text 추가

class ViewController: UIViewController {
      ...
    
    @IBOutlet weak var progressTime: UILabel!
         ...
    @IBAction func pressedTimer(_ sender: UIButton) {
        
                ...
        
        // 타이머 구현부 ( tiemr = )
        timer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { (Timer) in

            if self.secondsPassed < self.totalTime {
                
                // 지속시간 텍스트 표시
                self.progressTime.text = "진행 시간 : \(Int(self.secondsPassed))초"
                
                ...   
                
            } else {
                  ...
                self.progressTime.text = "Time over!"
            }
        }
    }
}

Play Sound

  1. import AVFoundation
  2. player: AVAudioPlayer! 변수 생성
  3. playSound 함수 구현
  4. 타이머 구현부 else문에 playSound 함수 호출
import UIKit

import AVFoundation

class ViewController: UIViewController {
    
    ...
    
    var player: AVAudioPlayer!
    
    ...
    
    @IBAction func pressedTimer(_ sender: UIButton) {
        
        ...
        
        // 타이머 구현부 ( tiemr = )
        timer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { (Timer) in
        
            if self.secondsPassed < self.totalTime {
                ...
            } else {
            
                   ...
                   
                // 타이머 종료시 sound play
                self.playSound(title: "alarm_sound")
            }
        }
    }
    
    func playSound(title: String?) {
        let url = Bundle.main.url(forResource: title, withExtension: "mp3")
        player = try!AVAudioPlayer(contentsOf: url!)
        player.play()
    }
}

전체 코드

import UIKit
import AVFoundation

class ViewController: UIViewController {
    
    let eggTimes = ["Soft": 5, "Medium": 7, "HardTime": 12]
    var secondsPassed = 0
    var totalTime = 0
    var timer = Timer()
    var player: AVAudioPlayer!
    
    @IBOutlet weak var progressTime: UILabel!
    @IBOutlet weak var progressBar: UIProgressView!
    @IBOutlet weak var timerLabel: UILabel!
    
    @IBAction func pressedTimer(_ sender: UIButton) {
        
        // 버튼을 클릭했을때 취소시킴
        timer.invalidate()
        
        let hardness = sender.currentTitle!
        totalTime = eggTimes[hardness]!
        
        // 재 실행시 초기화 설정
        progressBar.progress = 0.0
        secondsPassed = 0
        timerLabel.text = hardness
   
        
        // 타이머 구현부 ( tiemr = )
        timer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { (Timer) in
            // secondsPassed = 0 이고 totalTime이 hardness(3) 이므로
            if self.secondsPassed < self.totalTime {
                
                // secondsPassed + 1 하고 이것이 hardness까지 온다면
                self.secondsPassed += 1
                
                // 지속시간 텍스트 표시
                self.progressTime.text = "진행 시간 : \(Int(self.secondsPassed))초"
            
                // secondsPassed / totalTime
                self.progressBar.progress = Float(self.secondsPassed)/Float(self.totalTime)
                
            } else {
                Timer.invalidate()
                self.timerLabel.text = "계란이 다 삶아졌습니다!"
                self.progressTime.text = "Time over!"
                
                // 타이머 종료시 sound play
                self.playSound(title: "alarm_sound")
            }
        }
    }
    
    func playSound(title: String?) {
        let url = Bundle.main.url(forResource: title, withExtension: "mp3")
        player = try!AVAudioPlayer(contentsOf: url!)
        player.play()
    }
}

느낀점

🦊  timer를 사용하는법과 백분률을 사용하는법을 조금 더 이해할 필요가 있겠다..

🦊  progressView 사용법은 생각 보다 쉽진 않네..

🦊  타이머를 정지하고 취소할때는 invalidate() 사용!

 

 


잘못된 내용이 있으면 언제든 피드백 부탁드립니다.


서근


위처럼 이미지 와 함께 댓글을 작성할 수 있습니다.