티스토리 뷰
오늘은 유데미를 통해 배운 에그 타이머 예제를 통해 타이머를 알아보고자 합니다!!
이 에그 타이머가 꽤 마음에 들어서 제 버전으로 만들어 보았어요.
(달걀이 너무 귀여워,,,🥚)
1. 에그 타이머 UI 잡기
언제나 제일 기본적인 부분이져. UI잡기부터 해볼게요.
코드는 접어둘게요!
import UIKit
class ViewController: UIViewController {
let mainLbl = UILabel()
let background = UIImageView()
let softBtn = UIButton()
let softLbl = UILabel()
let mediumBtn = UIButton()
let mediumLbl = UILabel()
let hardBtn = UIButton()
let hardLbl = UILabel()
let progressBar = UIProgressView()
let pointColor = UIColor(red: 81.0 / 255.0, green: 215.0 / 255.0, blue: 202.0 / 255.0, alpha: 1.0)
let pointColor2 = UIColor(red: 95.0 / 255.0, green: 87.0 / 255.0, blue: 215.0 / 255.0, alpha: 1.0)
override func viewDidLoad() {
super.viewDidLoad()
configureUI()
}
}
//MARK: -UI
extension ViewController {
final private func configureUI() {
setAttributes()
addTarget()
setConstraints()
}
final private func setAttributes() {
mainLbl.text = "달걀🥚삶기"
mainLbl.textAlignment = .center
mainLbl.font = UIFont.boldSystemFont(ofSize: 40)
mainLbl.textColor = pointColor2
background.image = UIImage(named: "cool-background")
softBtn.setImage(UIImage(named: "soft_egg"), for: .normal)
softLbl.text = "반숙"
mediumBtn.setImage(UIImage(named: "medium_egg"), for: .normal)
mediumLbl.text = "반+완숙"
hardBtn.setImage(UIImage(named: "hard_egg"), for: .normal)
hardLbl.text = "완숙"
[softBtn, mediumBtn, hardBtn].forEach {
$0.contentMode = .scaleAspectFit
}
[softLbl, mediumLbl, hardLbl].forEach {
$0.textAlignment = .center
$0.textColor = pointColor
$0.font = UIFont.boldSystemFont(ofSize: 25)
}
progressBar.backgroundColor = .white
progressBar.progress = 0.5
progressBar.tintColor = pointColor2
}
final private func addTarget() {
softBtn.addTarget(self, action: #selector(BtnTapped(_:)), for: .touchUpInside)
mediumBtn.addTarget(self, action: #selector(BtnTapped(_:)), for: .touchUpInside)
hardBtn.addTarget(self, action: #selector(BtnTapped(_:)), for: .touchUpInside)
}
final private func setConstraints() {
let softStack = UIStackView(arrangedSubviews: [softLbl,softBtn])
softStack.axis = .vertical
softStack.spacing = 10
let mediumStack = UIStackView(arrangedSubviews: [mediumLbl,mediumBtn])
mediumStack.axis = .vertical
mediumStack.spacing = 10
let hardStack = UIStackView(arrangedSubviews: [hardLbl,hardBtn])
hardStack.axis = .vertical
hardStack.spacing = 10
let stackView = UIStackView(arrangedSubviews: [softStack, mediumStack, hardStack])
stackView.axis = .horizontal
stackView.distribution = .fillEqually
stackView.spacing = 20
[background, mainLbl, stackView, progressBar].forEach {
view.addSubview($0)
$0.translatesAutoresizingMaskIntoConstraints = false
}
NSLayoutConstraint.activate([
progressBar.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -200),
progressBar.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: 20),
progressBar.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: -20),
//progressBar.centerXAnchor.constraint(equalTo: view.safeAreaLayoutGuide.centerXAnchor),
background.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor),
background.topAnchor.constraint(equalTo: view.topAnchor),
background.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor),
background.bottomAnchor.constraint(equalTo: view.bottomAnchor),
mediumLbl.heightAnchor.constraint(equalTo: softLbl.heightAnchor),
hardLbl.heightAnchor.constraint(equalTo: softLbl.heightAnchor),
stackView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: 10),
stackView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 240),
stackView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: -10),
stackView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -300),
mainLbl.centerXAnchor.constraint(equalTo: view.safeAreaLayoutGuide.centerXAnchor),
mainLbl.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 120),
])
}
}
각 달걀 상태별로 레이블과 달걀버튼을 스택으로 묶은 후, 그 3개(반숙 + 반완숙 + 완숙)를 다시 스택뷰로 묶었어요!
아! 그리고 저는 색깔을 넣고 싶어서 포인트 컬러 2개도 설정했어요!
타이머 만들기
우선 타이머와 여러가지 기본 설정이 필요해요.
viewDidLoad 위에 아래 5가지 변수 또는 상수를 만들어줍니다요
var timer = Timer() // 타이머
var totalTime = 0 // 달걀 삶는 전체 시간
var timePassed = 0 // 지나간 시간
let timeToBoil = ["반숙":5, "반+완숙":8, "완숙":10] // 달걀별 삶는 시간
원래 반숙은 7분인가 8분인가 해야하는데, 그걸 기다릴 순 없으니, 간단하게 5초, 8초, 10초로 설정할게요!
버튼(달걀그림) 터치 시 이벤트를 extension을 통해 따로 빼줘볼게요!
어차피 달걀마다 발생할 이벤트가 동일하기 때문에 이벤트 메서드는 하나만 만들어주고, 버튼에 addTarget할 때 모두 이 메서드를 액션으로 주려구요.
//MARK: -Event
extension ViewController {
@objc func BtnTapped(_ sender: UIButton) {
// 달걀그림(버튼) 터치 시 발생할 이벤트
progressBar.progress = 0.0
}
1. progressBar
progressBar는 UI를 설정할 때 thumb가 중간에 오도록 설정했었는데, 버튼을 누를 때는 맨 처음으로 오는 게 맞으니까 0.0으로 다시 설정해주었어요.
2. 버튼 이벤트 설정
버튼의 sender값은 총 세개 softBtn(반숙), mediumBtn(반+완숙), hardBtn(완숙)여서 switch 문을 활용했어요.
각 버튼의 값을 위에서 선언한 timeToBoil 딕셔너리에서 찾아서 그 value값을 hardness에 넣어준 후, 다시 그 값을 totalTime에 넣어줄게요!
예를 들어,
softBtn(반숙) 버튼을 누르면 딕셔너리에서 "반숙"의 value값인 5를 찾아 hardness에 넣겠져?
그리고 그 값을 다시 totalTime에 할당해주는 걸로!
//MARK: -Event
extension ViewController {
@objc func BtnTapped(_ sender: UIButton) {
switch sender {
case softBtn:
guard let hardness = timeToBoil["반숙"] else { return }
totalTime = hardness
case mediumBtn:
guard let hardness = timeToBoil["반+완숙"] else { return }
totalTime = hardness
case hardBtn:
guard let hardness = timeToBoil["완숙"] else { return }
totalTime = hardness
default:
break
}
}
3. Timer
그리고 아까 찍어낸 timer 객체에 Timer 설정을 해줄게요!
저는 아래 타입메서드를 사용할건데요. 타입메서드니까, 앞에 타입(Timer)까지 명시해주는 거 잊지말기
이 타입 메서드는 5개의 파라미터를 가지는 놈인데,
-ti : 타이머의 간격
-target : 타이머가 실행될 때 aSelector에서 지정한 메시지를 보낼 대상이래요. 바로 자기 자신(self)이져
-aSelector : 타이머가 작동하면 타겟에서 보낼 메세지래요. 이건 밑에서 메서드를 만들거에요.
-userinfo : 타이머의 사용자 정보라는데, nil일거래요. (뭔 말?)
-repeats : 타이머의 반복 여부. true면 타이머가 무효화(invalidate)될 때까지 타이머가 반복돼요.
파리미터를 설정해주면? 아래와 같습니다.
//MARK: -Event
extension ViewController {
@objc func BtnTapped(_ sender: UIButton) {
switch sender {
case softBtn:
guard let hardness = timeToBoil["반숙"] else { return }
totalTime = hardness
case mediumBtn:
guard let hardness = timeToBoil["반+완숙"] else { return }
totalTime = hardness
case hardBtn:
guard let hardness = timeToBoil["완숙"] else { return }
totalTime = hardness
default:
break
}
timer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(updateTime), userInfo: nil, repeats: true)
}
}
이제 타이머의 파라미터 중 하나였던 selector 파라미터에 보낼 메시지를 만들어볼게요.
버튼 이벤트 밑에 만들어주고, selctor로 파라미터로 줄게요!
//MARK: -Event
extension ViewController {
@objc func BtnTapped(_ sender: UIButton) {
//생략
}
@objc func updateTime() {
//타이머 작동 시 발생될 일
if timePassed < totalTime {
timePassed += 1
let percentage = Float(timePassed) / Float(totalTime)
progressBar.setProgress(percentage, animated: true)
print(timePassed)
} else {
timePassed = 0
mainLbl.text = "다 됐다!"
}
}
}
softBtn(반숙)을 터치했다고 상상해볼게요.
BtnTapped(_:)의 switch문을 통해 totalTime에 5라는 값이 들어왔져?
totalTime = 5 / timePassed = 0
이게 이제 if문으로 들어갑니다
if 0(timePased) < 5(totalTime)
-> 참
-> timePassed + 1 과 progressview에 (지나간시간/전체시간)을 %를 부여해줄게요.
-> 거짓 (timePassed가 5보다 작지 않아지는 순간)
-> 다시 timePassed = 0 설정
-> 달걀🥚삶기 텍스트를 "다됐다!"로 변경
;;또 왜저래;;ㅎ;; 제법 엉망이네요;;ㅎㅎ
바로 타이머를 안 멈춰줘서 그런건데요. (안 멈추면 저렇게 빨라지더라구여,,,무서워,,)
두 시점에서 멈춰줘야 해요.
1) 달걀 버튼을 눌렀을 때
2) 정해진 시간이 다 지났을 때 (if문의 else문에서)
+ 다른 달걀을 눌렀을 때, 메인레이블이 계속 "다됐다!" 상태니까 매우 이상해요. 버튼 이벤트에 다시 메인레이블을 "달걀🥚삶기"로 설정해볼게요.
(바뀐 부분은 ⭐️표시)
extension ViewController {
@objc func BtnTapped(_ sender: UIButton) {
timer.invalidate() //⭐️ 타이머야 멈춰줘
progressBar.progress = 0.0
mainLbl.text = "달걀🥚삶기" //⭐️ 메인레이블 재설정
switch sender {
case softBtn:
guard let hardness = timeToBoil["반숙"] else { return }
totalTime = hardness
case mediumBtn:
guard let hardness = timeToBoil["반+완숙"] else { return }
totalTime = hardness
case hardBtn:
guard let hardness = timeToBoil["완숙"] else { return }
totalTime = hardness
default:
break
}
timer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(updateTime), userInfo: nil, repeats: true)
}
@objc func updateTime() {
if timePassed < totalTime {
timePassed += 1
let percentage = Float(timePassed) / Float(totalTime)
progressBar.setProgress(percentage, animated: true)
print(timePassed)
} else {
timer.invalidate() //⭐️ 타이머야 멈춰줘
timePassed = 0
mainLbl.text = "다 됐다!"
}
}
}
timePassed를 프린트해서 앱실행과 같이 볼게요!
(시간관계상 반숙과 반+완숙만!)
이렇게 에그 타이머는 완료!!!되었습니다!!
다음 번에는 시간이 다되면, 음악이 울리도록 해보께요!!
퐈이링!!
틀린 부분은 언제나 말씀주세여 슨배님덜!!
감사합니다!!!
'iOS 개발자 되기' 카테고리의 다른 글
[swift] 데이터를 저장하는 방법 - UserDefaults (0) | 2022.03.13 |
---|---|
[swift] 감동란 만들기로 알아보는 노래 재생 (0) | 2022.02.16 |
[swift] Alamofire 걸음마 3 (Alamofire) (0) | 2022.01.30 |
[swift] Alamofire 걸음마 2 (기본 설정, SnapKit) (0) | 2022.01.28 |
[swift] 간단한 Typing animation 만들기 (0) | 2022.01.23 |
- Total
- Today
- Yesterday