GithubHelp home page GithubHelp logo

jacobkosmart / timer-ios-practice Goto Github PK

View Code? Open in Web Editor NEW
0.0 1.0 0.0 125 KB

To practice dispatchSourceTimer, UIViewAnimation

Home Page: https://jacobko.info/uikit/ios-07/

Swift 100.00%
uikit dispatchsourcetimer uiviewanimation

timer-ios-practice's Introduction

πŸ•° TimerApp-iOS-practice

스크란샷

πŸ“Œ κΈ°λŠ₯ 상세

  • DatePicker λ₯Ό 톡해 타이머 μ‹œκ°„μ„ μ„€μ •ν•  수 μžˆμŠ΅λ‹ˆλ‹€

  • μ‹œμž‘ λ²„νŠΌμ„ λˆ„λ₯΄λ©΄ 타이머가 μ‹œμž‘λ˜κ³  μΌμ‹œ 정지λ₯Ό λˆ„λ₯΄λ©΄ 타이머가 μΌμ‹œμ •μ§€ λ©λ‹ˆλ‹€

  • μ·¨μ†Œ λ²„νŠΌμ„ λˆ„λ₯΄λ©΄ 타이머가 μ’…λ£Œλ©λ‹ˆλ‹€

  • 카운트 λ‹€μš΄μ΄ μ™„λ£Œλ˜λ©΄ μ•ŒλžŒμ΄ μšΈλ¦½λ‹ˆλ‹€

πŸ”‘ Check Point !

스크란샷

πŸ”· DispatchSourceTimer

// in ViewController.swift

// timer λ₯Ό μ„€μ •ν•˜κ³ , timer κ°€ μ‹œμž‘λ˜λŠ” method
func startTimer() {
	// timer κ°€ nil μΌλ•Œ makeTimerSource 생성 : flags 에 λΉˆλ°°μ—΄ λ„˜κ²¨μ£Όκ³ , queue parameter μ—λŠ” μ–΄λ–€ thread queue μ—μ„œ λ°˜λ³΅λ™μž‘ν• κ±΄μ§€ μ„€μ •ν•΄μ£ΌλŠ” 것(timer κ°€ λŒλ•Œλ§ˆλ‹€, UI μž‘μ—…μ„ ν•΄μ€˜μ•Ό 함. 예λ₯Ό λ“€μ–΄ 남은 μ‹œκ°„μ„ ν‘œμ‹œν•  수 있게 update ν•΄μ€˜μ•Ό ν•˜λ©°, pregressView 도 update ν•΄μ€˜μ•Ό 함 => .main thread μ—μ„œ 반볡 λ™μž‘ ν•˜κ²Œλ” 섀정함
	// GCD μ—μ„œ main thread λŠ” app μ „μ²΄μ—μ„œ 였직 ν•œκ°œλ§Œ μ‘΄μž¬ν•¨, κ·Έλž˜μ„œ 일반적인 code λŠ” main thread μ—μ„œ μ‹€ν–‰λ˜λŠ”λ°, μž‘μ„±λœ code λŠ” cocoaμ—μ„œ μ‹€ν–‰λ˜λŠ”λ°, cocoa λŠ” 맀번 main thread λ₯Ό ν˜ΈμΆœν•΄μ„œ μž‘μ—…μ„ μ²˜λ¦¬ν•˜λŠ” logic 이 있음
	// main thread μ—μ„œ μ€‘μš”ν•œ 점은, intefaced thread 라고 λΆˆλ¦¬λŠ”λ°, user κ°€ interface 에 μ ‘κ·Όν•˜λ©΄, event λ₯Ό main thread 에 전달 되며, μž‘μ„±ν•œ code κ°€ 거기에 λ°˜μ‘ν•˜κ²Œ 됨. 즉, interface 와 κ΄€λ ¨λœ code λŠ” λ°˜λ“œμ‹œ, main thread μ—μ„œ μž‘μ„±λ˜μ–΄μ•Ό 함을 의미 ν•©λ‹ˆλ‹€(UI와 κ΄€λ ¨λœ μž‘μ—…μ€ main thread μ—μ„œ 이뀄져야함)
	if self.timer == nil {
		self.timer = DispatchSource.makeTimerSource(flags: [], queue: .main)
		// schedule: μ–΄λ– ν•œ 주기둜 timer κ°€ μ‹€ν–‰λ˜λŠ”μ§€ μ„€μ •ν•΄μ€˜μ•Ό 함 deadline: .now λŠ” timer κ°€ μ‹œμž‘λ˜λ©΄ μ¦‰μ‹œ μ‹€ν–‰λ˜κ²Œ 함 (예둜 3μ΄ˆλ’€μ— μ‹€ν–‰λ˜μ•Ό ν•˜λ©΄ .now() + 3 ν•΄μ£Όλ©΄ 됨). repating 은 λͺ‡μ΄ˆλ§ˆλ‹€ λ°˜λ³΅ν• κ±΄μ§€ μ„€μ •ν•˜λŠ”κ²ƒ 1μ΄ˆλ§ˆλ‹€ 반볡 μ„€μ •
		self.timer?.schedule(deadline: .now(), repeating: 1)
		// setEventHandler: timer 와 ν•¨κ»˜ μ—°λ™λœ eventHandler ν• λ‹Ή => 클둜져 ν•¨μˆ˜λ‘œ timer κ°€ λ™μž‘ν• λ•Œ λ§ˆλ‹€, handler closure ν•¨μˆ˜κ°€ 호좜이 됨( μ—¬κΈ°μ„  1μ΄ˆμ— ν•œλ²ˆμ”© handler 에 κ΅¬ν˜„λ˜λŠ” code κ°€ μ‹€ν–‰λ˜κ²Œ 됨)
		self.timer?.setEventHandler(handler: { [weak self] in
			// μΌμ‹œμ μœΌλ‘œ self κ°€ strong reference κ°€ 되게 함
			guard let self = self else { return }
			self.currentSeconds -= 1 // μ΄ˆλ‹Ή 1μ”© κ°μ†Œν•˜κ²Œ 함
			// 초λ₯Ό μ‹œ,λΆ„,초둜 λ³€ν™˜ν•˜κΈ°
			let hour = self.currentSeconds / 3600 // 초λ₯Ό 3600 으둜 λ‚˜λˆ„λ©΄ μ‹œκ°„
			let minutes = (self.currentSeconds % 3600) / 60 // 뢄은 3600으둜 λ‚˜λˆˆ λ‚˜λ¨Έμ§€μ—μ„œ 60 λ‚˜λˆ„κΈ°
			let seconds = (self.currentSeconds % 3600) % 60 // μ΄ˆλŠ” 3600으둜 λ‚˜λˆˆ λ‚˜λ¨Έμ§€μ— 60의 λ‚˜λ¨Έμ§€ κ°’
			// String 포멧 ν˜•μ‹μœΌλ‘œ 2자리 μˆ«μžμ— : 으둜 κ΅¬λΆ„λœ ν˜•μ‹μœΌλ‘œ ν•˜κ³ , arguments μ—λŠ” hour, minutes, seconds ν• λ‹Ή
			self.timerLabel.text = String(format: "%02d:%02d:%02d", hour, minutes, seconds)
			// debugPrint(self.currentSeconds)

			// μ‹œκ°„μ— λ§žμΆ°μ„œ progressBar 도 μ€„μ–΄λ“œλŠ” logic: countDown 되고 μžˆλŠ” μ‹œκ°„μ„ datePicker μ—μ„œ μ„€μ •ν•œ 총 μ‹œκ°„μ„ λ‚˜λˆ  μ£Όλ©΄ countDown 될 λ•Œλ§ˆλ‹€ progress κ²Œμ΄μ§€κ°€ 쀄어 λ“€κ²Œ 됨(progress λŠ” Float type 으둜 λ³€ν™˜ μ‹œμΌœμ•Ό 함)
			self.pregressView.progress = Float(self.currentSeconds) / Float(self.duration)
			debugPrint(self.pregressView.progress)

			// currentSeconds κ°€ 0보닀 μž‘κ±°λ‚˜, κ°™λ‹€λ©΄ countDown 이 λλ‚œκ²ƒμ΄κΈ° λ•Œλ¬Έμ—
			if self.currentSeconds <= 0 {
				// timer μ’…λ£Œ code
				self.stopTimer() // timer μ’…λ£Œ
			}
		})
		// hander closure κ°€ μ™„λ£Œ 되면 timer κ°€ μ‹œμž‘λ˜κ²Œ 함
		self.timer?.resume()
	}
}

// timer κ°€ 0보닀 κ°™κ±°λ‚˜ μž‘μ„λ•Œ 와 cancelBtn 을 λˆ„λ₯΄λ©΄ timer κ°€ μ’…λ£Œλ˜λŠ” method
func stopTimer() {
	// .pause μƒνƒœ μΌκ²½μš°μ— resume method μƒμ„±λ˜κ²Œ μž‘μ„±
	if self.timerStatus == .pause {
		self.timer?.resume()
	}
	self.timerStatus = .end
	self.cancelBtn.isEnabled = false
	self.setTimerInfoViewVisable(isHidden: true)
	self.datePicker.isHidden = false
	self.toggleBtn.isSelected = false
	self.timer?.cancel() // timer μ’…λ£Œ
	self.timer = nil // timer λ©”λͺ¨λ¦¬μ—μ„œ ν•΄μž¬ μ‹œν‚΄: ν•΄μž¬ μ•ˆμ‹œν‚€λ©΄ 화면을 λ²—μ–΄λ‚˜λ„, timer κ°€ 계속 λ™μž‘ ν•  수 있음
}

πŸ”· Aram μ†Œλ¦¬ λ‚˜κ²Œ ν•˜κΈ°(timer κ°€ μ’…λ£Œμ‹œ)

iPhone AudioServices - https://iphonedev.wiki/index.php/AudioServices

image

import AudioToolbox

if self.currentSeconds <= 0 {
			// timer μ’…λ£Œ code
			self.stopTimer() // timer μ’…λ£Œ
			// 0이 되면 μ•Œλ¦Ό 울리게 함: 아이폰 κΈ°λ³Έ alert μ‚¬μš΄λ“œ μž¬μƒ μ‹œν‚΄
			AudioServicesPlaySystemSound(1005)
		}
	})
	// hander closure κ°€ μ™„λ£Œ 되면 timer κ°€ μ‹œμž‘λ˜κ²Œ 함
	self.timer?.resume()
}

πŸ”· UIView Animation

ν™”λ©΄μ˜ transition 효과 μ μš©ν•˜κΈ° (alpha κ°’ 쑰절)

  • View의 μ—¬λΆ€λ₯Ό isHidden 값이 μ•„λ‹Œ, alpha κ°’μœΌλ‘œ μ„€μ •ν•΄μ„œ View κ°€ 사라지고 ν‘œμ‹œλ˜κ²Œ λ§Œλ“­λ‹ˆλ‹€. alpha 값은 opacity 값을 μ‘°μ ˆν•˜λŠ” 인자 μž…λ‹ˆλ‹€. μ΅œμ†Œ 0 ~ 1κΉŒμ§€ μ„€μ •ν•˜λŠ”λ° 0 에 κ°€κΉŒμšΈμˆ˜λ‘ view κ°€ 투λͺ…ν•΄ μ§‘λ‹ˆλ‹€.
func stopTimer() {
	// .pause μƒνƒœ μΌκ²½μš°μ— resume method μƒμ„±λ˜κ²Œ μž‘μ„±
	if self.timerStatus == .pause {
		self.timer?.resume()
	}
	self.timerStatus = .end
	self.cancelBtn.isEnabled = false
	// alpha κ°’ μ‘°μ ˆμ„ ν†΅ν•œ animation
	UIView.animate(withDuration: 0.5, animations: {
		self.timerLabel.alpha = 0
		self.pregressView.alpha = 0
		self.datePicker.alpha = 1
	})

@IBAction func tabCancelBtn(_ sender: UIButton) {
	switch self.timerStatus {
		// start, paue μƒνƒœμ—μ„œ cancelBtn λˆ„λ₯΄λ©΄ .end μƒνƒœλ‘œ 놓고, cancelBtn 을 λΉ„ν™œμ„±ν™”λ‘œ ν•˜κ³ , timerLabelκ³Ό pregressView κ°€ ν‘œμ‹œλ˜μ§€ μ•Šκ²Œν•¨, datePicker κ°€ λ‹€μ‹œ ν‘œμ‹œλ˜κ²Œ 함, toggleBtn이 strart 되게 함
	case .start, .pause:
		switch self.timerStatus {
			// end 일경우 아직 μ‹œμž‘ μ•ˆν•œμƒνƒœ timer label κ³Ό progressView κ°€ ν‘œμ‹œλ˜κ²Œ ν•˜κ³ , datePicker κ°€ hidden 되게 함
	case .end:
		self.currentSeconds = self.duration // ν˜„μž¬ μ‹œκ°„μ„ duration 에 λŒ€μž… μ‹œν‚΄
		self.timerStatus = .start
		// alpha κ°’ μ‘°μ ˆμ„ ν†΅ν•œ animation
		UIView.animate(withDuration: 0.5, animations: {
			self.timerLabel.alpha = 1
			self.pregressView.alpha = 1
			self.datePicker.alpha = 0
		})

Kapture 2021-12-17 at 14 01 52

κ³ μ •λ˜μ–΄ μžˆλŠ” image 의 νšŒμ „ 효과 animation

	// image rotation animation
	UIView.animate(withDuration: 0.5, delay: 0, animations: {
		// CGAffineTransform 은 ꡬ쑰체 인데, view 의 frame 을 κ³„μ‚°ν•˜μ§€ μ•Šκ³  2D κ·Έλž˜ν”½μ„ 그릴 수 μžˆμŠ΅λ‹ˆλ‹€. (예λ₯Ό λ“€μ–΄ Viewλ₯Ό 이동 μ‹œν‚€κ±°λ‚˜, νšŒμ „μ‹œν‚€λŠ” 효과λ₯Ό μ€„μˆ˜ μžˆμŠ΅λ‹ˆλ‹€. rotationAngle: .pi 은 180도 νšŒμ „μ„ μ˜λ―Έν•©λ‹ˆλ‹€.
		self.imageView.transform = CGAffineTransform(rotationAngle: .pi)
	})
	// λ‹€μ‹œ 360도 νšŒμ „ μ‹œν‚΄ : μœ„μ— 180 도 νšŒμ „ animation 이 λλ‚˜λ©΄ λ™μž‘ ν•  수 있게 delay 0.5 μ„€μ •
	UIView.animate(withDuration: 0.5, delay: 0.5, animations: {
		self.imageView.transform = CGAffineTransform(rotationAngle: .pi * 2)
	})

	self.imageView.transform = .identity // cancel 되면 imageView κ°€ μ›μƒνƒœλ‘œ 되게 함

Kapture 2021-12-17 at 14 03 38

Describing check point in details in Jacob's DevLog - https://jacobko.info/ios/ios-06/

❌ Error Check Point

πŸ”Ά Pause μƒνƒœμ—μ„œ CancelBtn λˆ„λ₯΄λ©΄ Error λ°œμƒ

image

  • Timer λ₯Ό suspend λ₯Ό μ‚¬μš©ν•΄μ„œ μΌμ‹œμ •μ§€ ν•˜κ²Œ 되면, 아직 μˆ˜ν–‰ν•΄μ•Ό 될 μž‘μ—…μ΄ μžˆμ„μ„ μ˜λ―Έν•˜κΈ° λ•Œλ¬Έμ—, suspend 된 timer 에 nil 을 λŒ€μž…ν•˜κ²Œ 되면 runTime error λ°œμƒ 됨

  • μΌμ‹œμ •μ§€ 된 μƒνƒœμ—μ„œ timer λ₯Ό μ€‘μ§€ν•˜κ³ , nil 을 λŒ€μž…ν•˜λ €λ©΄ 그전에 resume method λ₯Ό μ‹€ν–‰ μ‹œμΌœμ•Ό 함

Solving Problem

// in ViewController.swift

	// timer κ°€ 0보닀 κ°™κ±°λ‚˜ μž‘μ„λ•Œ 와 cancelBtn 을 λˆ„λ₯΄λ©΄ timer κ°€ μ’…λ£Œλ˜λŠ” method
func stopTimer() {
	// .pause μƒνƒœ μΌκ²½μš°μ— resume method μƒμ„±λ˜κ²Œ μž‘μ„±
	if self.timerStatus == .pause {
		self.timer?.resume()
	}
	self.timerStatus = .end
	self.cancelBtn.isEnabled = false
	self.setTimerInfoViewVisable(isHidden: true)
	self.datePicker.isHidden = false
	self.toggleBtn.isSelected = false
	self.timer?.cancel() // timer μ’…λ£Œ
	self.timer = nil // timer λ©”λͺ¨λ¦¬μ—μ„œ ν•΄μž¬ μ‹œν‚΄: ν•΄μž¬ μ•ˆμ‹œν‚€λ©΄ 화면을 λ²—μ–΄λ‚˜λ„, timer κ°€ 계속 λ™μž‘ ν•  수 있음
}

πŸ”Ά πŸ”· πŸ“Œ πŸ”‘ πŸ‘‰

πŸ—ƒ Reference

Jacob's DevLog - https://jacobko.info/ios/ios-07/

재:νŽΈμ§‘ κ°œλ°œλΈ”λ‘œκ·Έ - https://dev-dream-world.tistory.com/133

fastcampus - https://fastcampus.co.kr/dev_online_iosappfinal

timer-ios-practice's People

Contributors

jacobkosmart avatar

Watchers

 avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    πŸ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. πŸ“ŠπŸ“ˆπŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❀️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.