뽀모도로 앱 만들기!! -> 이제 조금은 적응이 되서 스토리보드에서 ViewController연결까지 하는것과 스토리보드에서 버튼이나 라벨 추가는 수월하게 하게 됐다 ㅋㅋㅋ
항상 하던것처럼 전체적인 틀을 말하고 코드를 구현해보겠다
-> 확실히 다른 웹사이트 만드는 것보다 훨씬 재밋네 ㅎㅎ
뽀모도로라고 하길래 뭔가 했는데 그냥 타이머 였다.....(허무하네 ㅋㅋ)
토마토가 타이머가 다 될때 까지 회전하는 어플이다ㅎㅎ
다른 화면이 필요하지 않아서 navigation은 사용하지 않았다
추가로 토마토가 필수여서 토마토 사진을 이미지에 픽셀별로 넣었다(지금꺼는 조금 깨지네)
DatePicker => (날짜와 시간을 선택할 수 있는 객체!!)가 핵심으로 사용되었다.
데이터 피커의 경우 스타일을 설정할 수 있다 인스펙터 영영에서 Preferred Style 항목을 통해서 지정하면 된다
기본은 Automatic,
우리가 알고있는 Wheels 방식,
그리고 날짜와 시간을 세상 심플하게 보여주는 Compact가 있습니다
inline스타일도 있는데 이거는 달력이 전체적으로 펼쳐지면서 아래 날짜와 시간 표시되는 녀석
추가로 인스펙터 영역에서 Mode를 들어가면 시간만 보여주는 Time,
날짜와 시간보여주는 Date and Time ,
타이머를 설정하는 Count Down Timer가 있다.
DatePicker의 경우 인스펙터의 Locale에서 언어 또한 설정 할 수 있다
progress View (진행 상황을 나타낼 수 있는 바)
-> 인스펙터에서 Progress를 1로 설정하면 바가 파랑으로 꽉 찬다 ㅎ
코드를 전체를 구현할 건데 변수끼리 함수는 함수끼리 작성해서 이해해보려고 한다
import UIKit
import AudioToolbox
enum TimerStatus {
case start
case pause
case end
}
@IBOutlet weak var imageView: UIImageView!
@IBOutlet weak var timerLabel: UILabel!
@IBOutlet weak var progreeView: UIProgressView!
@IBOutlet weak var datePicker: UIDatePicker!
@IBOutlet weak var cancelButton: UIButton!
@IBOutlet weak var toggleButton: UIButton!
var duration = 60
var timerStatus: TimerStatus = .end
var timer: DispatchSourceTimer?
var currentSecond = 0
일단 변수와 열거형을 적었다
TimerStatus라는 열거형은 말 그대로 지금 타이머의 상태값을 편하게 정하려고 적었다.
(이 코드에서 많이 쓰일 예정)
@IBOutlet붙은 애들은 스토리보드랑 각각 연동한거니까 설명은 생략하겠다
timer는 DispatchSourceTimer를 사용함으로써 타이머에 관련된 이벤트를 처리할 수 있다
var duration은 우리가 타이머에서 정한 시간을 초 단위로 tapToggleButton에서 받아놓을 예정이다.
self.duration = Int(self.datePicker.coutDownDuration) 이거 함수부분에서 설명
override func viewDidLoad(){
super.viewDidLoad()
self.configureToggleButton()
}
func setTimerInfoViewVisible(isHidden: Bool){
self.timerLabel.isHidden = isHidden
self.progreeView.isHiddn = isHidden
}
func configureToggleButton(){
self.toggleButton.setTitle("시작", for: .normal)
self.toggleButton.setTitle("일시정지", for: .selected)
}
func startTimer(){
if self.timer == nil {
self.timer = DispatchSource.makeTimerSource(flags: [], queue: .main)
self.timer?.schedule(deadline: .now(), repeating: 1)
self.timer?.setEventHandler(handler: { [weak self] in
guard let self = self else {return} // self가 strong 되게 만든거
self.currentSeconds -= 1
//debugPrint(self.currentSeconds)
let hour = self.currentSeconds / 3600
let minutes = (self.currentSeconds % 3600) / 60
let secondes = (self.currentSeconds % 3600) % 60
self.timerLabel.text = String(format: "%02d:%02d:%02d", hour,minutes,secondes)
self.progreeView.progress = Float(self.currentSeconds) / Float(self.duration)
UIView.animate(withDuration: 0.5,delay: 0, animations: {
self.imageView.transform = CGAffineTransform(rotationAngle: .pi) // 180도
})
UIView.animate(withDuration: 0.5,delay: 0.5, animations: {
self.imageView.transform = CGAffineTransform(rotationAngle: .pi * 2)
})
if self.currentSeconds <= 0 {
self.stoptimer()
AudioServicesPlaySystemSound(1005)
}
})
self.timer?.resume()
}
}
func stoptimer(){
if self.timerStatus == .pause {
self.timer?.resume()
}
self.timerStatus = .end
self.cancleButton.isEnabled = false
UIView.animate(withDuration: 0.5, animations: {
self.timeLabel.alpha = 0
self.progreeView.alpha = 0
self.datePicker.alpha = 1
self.imageView.transform = .identity
})
self.toggleButton.isSelected = false
self.timer?.cancel()
self.timer = nil
}
@IBAction func tapCancelButton(_ sender: UIButton){
switch self.timerStatus {
case .start, .pause:
self.stoptimer()
default:
break;
}
}
@IBAction func tapToggleButton(_ sender: UIButton){
self.duration = Int(self.datePicker.countDownDuration)
switch self.timerStatus {
case .end:
self.currentSeconds = self.duration
self.timerStatus = .start
UIView.animate(withDuration: 0.5, animations: {
self.timerLabel.alpha = 1
self.progreeView.alpha = 1
self.datePicker.alpha = 0
})
self.toggleButton.isSelected = true
self.cancelButton.isEnabled = true
self.startTimer()
case .start:
self.timerStatus = .pause
self.toggleButton.isSelected = false
self.timer?.suspend()
case .pause:
self.timerStatus = .start
self.toggleButton.isSelected = true
self.timer?.resume()
}
}
함수를 길게 적어봤는데 하나하나 해석해보면서 어떤 영향을 끼치고 왜 작성했는지 알아보자
(추가로 몰랐던 내용들에 대한 공부는 다음 블로그에 적거나 이 아래 정리해서 적어야겠다)
configureToggleButton()
이거는 토클 버튼을 '시작'으로 할건지 '일시정지'로 할건지 미리 설정해놓는 함수다
setTitle에서 인자 for: 가 .normal이면 평소
setTitle에서 인자 for: 가 .selected면 isSelected가 true일때만 적용된다
setTimerInfoViewVisible
타이머와 진행 바가 어떤 상황에 따라 보여주고 안보여줄지를 결정하는 함수이다.
isHidden 을 사용함으로써 제어를 했다
startTimer()
if self.timer == nil 즉 타이머가 원래 존재자체를 안했을 때
self.timer = DispatchSource.makeTimerSource(flags: [], queue: .main)
타이머 이벤트를 모니터링 하기 위한 객체를 만드는 과정이다.
인자인 flags는 타이머 동작을 나타내는 추가 플러그다 (타이머 디스패치 소스를 구성할때 쓰일수있는 애들만 가능???)
인자인 queue는 설치된 핸들러를 실행할 큐를 찾는데 우리는 메인에서 돌릴거다 -> 이유는 나중에
self.timer?.schedule(deadline: .now(). repeating: 1)
스케줄 함수는 예약된 시간동안 몇번 반복될지 설정하는거다 ->
즉 바로 실행하고 타이머는 1초마다 반복된다
그리고 1초마다 무슨 이벤트를 할 지 알려주는 핸들러 함수를 아래 작성했다
.pause일때 self.timer?.resume()이라는 애가 추가로 설정되어있다.(이거는 나중에 설명)
그리고 아래는 timerStatus를 .end로 만들고
화면을 datePicker가 보이게 전환한다.
animate에서 self.imageView.transform을 .identity로 해서 다시 토마토를 원상태로 만든다.
self.timer = nil 이거 핵심!!! 왜냐하면 nil이 없으면 화면을 벗어나서 계속 실행되고 있을수도 있다
타이머 앱을 만들면서 새로 알게된 내용이나 좀 더 정리가 필요한 내용은 다음 블로그에 정리해보겠다.
점점 익숙해지면서 어플을 기획할때 어떻게 해야하는지 좀더 알아보고 싶어졌다
이번 강의가 끝나고 블로그를 작성해서 받은 패키지를 추가로 공부한 뒤
유튜브에 있는 넷플릭스 등을 클론코딩해보고 코드 파헤치기를 하면서 자신만의 어플을 1년안에 4개 이상을 내보고 1년 6개월 안에 ios개발자로서 취업에 도전해보겠다