- νλ‘κ·Έλλ¨Έμ€ μ°μ΅ κ³Όμ μ FLO μ± μꡬμ¬νμ λ°νμΌλ‘ κ°λ°ν μ±μ λλ€.
- FLO μ± κ°λ° μΌμ§ #1. MVVM ν¨ν΄κ³Ό Data Binding
[iOS] FLO μ± κ°λ° μΌμ§ #2. TableView λ‘ κ°μ¬ νλ©΄ κ°λ°νκΈ°
μ΄λ² κ°λ° μΌμ§λ μ 체 κ°μ¬ 보기 νλ©΄μ κ°λ°νλ©΄μ TableView λ₯Ό μ΄λ»κ² μ΄μ©νλμ§μ λν΄ μμ±ν΄λ³Όκ²μ! μλ μμλ‘ μμ±νμ΅λλ€
1. λ¬Έμμ΄μ κ°μ¬λ₯Ό λμ λλ¦¬λ‘ λ³ννκΈ°
2. TableView λ‘ μ 체 κ°μ¬ νλ©΄ UI κ°λ°νκΈ°
3. Music Player μ μκ°μ΄ μ§λ¨μ λ°λΌ κ°μ¬κ° λ³κ²½λλλ‘ νκΈ°
* Issue :: Observer λ₯Ό λ±λ‘νκ³ νλ©΄μ΄ μ’ λ£λ λ ν΄μ ν΄μ£Όμ§ μμ κ°μ¬ μ±ν¬κ° λ§μ§μλ μ΄μκ° λ°μ (ν΄κ²°)
4. TableView λ‘ νΉμ κ°μ¬ ν°μΉ μ ν΄λΉ ꡬκ°λΆν° μ¬μλλλ‘ κ°λ°νκΈ°
π 1. λ¬Έμμ΄μ κ°μ¬λ₯Ό λμ λλ¦¬λ‘ λ³ννκΈ°
JSON μΌλ‘ λ°μμ¨ κ°μ¬λ String νμ μΌλ‘, "[mm:ss:mss]κ°μ¬\n[mm:ss:mss]κ°μ¬\n" ννλ‘ λμ΄μμ΅λλ€. λ¨Όμ split ν¨μλ₯Ό μ΄μ©ν΄ '\n' μ κΈ°μ€μΌλ‘ νλμ κ°μ¬μ© λΆλ¦¬ν ν μ κ·ννμμ μ΄μ©ν΄ μκ° λΆλΆμ νμ±νκ³ , ']' μ΄νμ λΆλΆμ κ°μ¬λ‘ μ μ₯νμ΅λλ€.
"[00:16:200]we wish you a merry christmas\n[00:18:300]we wish you a merry christmas\n
[00:21:100]we wish you a merry christmas\n[00:23:600]and a happy new year\n...
[02:57:900]we wish you a merry christmas\n[03:00:500]and a happy new year"
JSON λ°μ΄ν°λ₯Ό λ°μμ€λ©΄μ [μκ°(Int): κ°μ¬(String)] νμ μ λμ λλ¦¬λ‘ κ°μ μ μ₯ν΄λμ΅λλ€. μ΄ λ, ms λ¨μλ μ μΈνκ³ , λΆκ³Ό μ΄ λ¨μλ§ κ³ λ €ν΄μ μ΄ λ¨μ μκ°μΌλ‘ Key λ₯Ό μ§μ νμ΅λλ€! κ·Έλ¦¬κ³ , μ²μ μμμΈ μκ° 0κ³Ό λλλ μκ°(duration) 198 μ Key λ‘ κ°λ κ°μ μΆκ°ν΄μ€¬μ΅λλ€.
{ 0: "κ°μ£Όμ€",
16 : "we wish you a merry christmas",
18 : "we wish you a merry christmas",
...
177 : "we wish you a merry christmas",
180 : "and a happy new year"
198 : "κ°μ£Όμ€" }
* μ€μ λμ λ리λ μ λ ¬λμ΄μμ§ μμ΅λλ€.
TableView μμ κ°μ¬λ₯Ό μκ°μμΌλ‘ μ λ ¬ν΄μ 보μ¬μ€μΌ νκΈ° λλ¬Έμ, μμ λμ λ리λ₯Ό κ°μ¬ λ°°μ΄λ‘ λ§λ€μ΄μ€λλ€! μ΄ λ, λμ λ리λ₯Ό key κΈ°μ€μΌλ‘ μ λ ¬ν ν value λ§ λ°λ‘ λΆλ¦¬νλ©΄ λ©λλ€. μ°Έκ³ λ‘, λμ λ리λ₯Ό μ λ ¬νμ λμλ μ€μ λμ λλ¦¬κ° μ λ ¬λλ κ²μ΄ μλ λμ λ리μ μ리먼νΈλ₯Ό μ λ ¬ν λ°°μ΄μ΄ λ°νλ©λλ€.
// sorted ν¨μμ λ°νκ° : [Dictionary<Int, String>.Element]
// lyricsArray μ νμ
: [String]
lyricsArray = lyricsDict.sorted { $0.key < $1.key }.map { $0.value }
βοΈ 2. TableView λ‘ μ 체 κ°μ¬ νλ©΄ UI κ°λ°νκΈ°
TableView μ Cell μ κ°μ¬λ₯Ό 보μ¬μ£Όλ lyricsLabel νλλ§μ κ°λ λ¨μν ꡬ쑰μ λλ€!
βοΈ lyricsArray λ°°μ΄μ μ΄μ©ν΄ Table View Data Source νλ‘ν μ½μ νμ λ©μλλ₯Ό ꡬνν΄μ€λλ€.
1. tableView(_:numberOfRowsInSection:)
Return the number of rows for the table. :: ν΄λΉ λ©μλλ₯Ό ꡬνν΄ ν μ΄λΈμ μ 체 ν κ°μλ₯Ό μλ €μ€μΌ νλ€.
2. tableView(_:cellForRowAt:)
Provide a cell object for each row. :: ν μ΄λΈμ κ° νλ§λ€ Cell μ μ 곡ν΄μΌνλ€.
extension LyricsViewController: UITableViewDelegate, UITableViewDataSource {
// MARK: - TableView method
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return viewModel.lyricsCount
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: "LyricsCell", for: indexPath) as? LyricsCell else {
return UITableViewCell()
}
cell.setLyrics(text: viewModel.lyricsArray[indexPath.row])
return cell
}
// other methods ...
}
βοΈ Music Player μ μκ°μ΄ μ§λ¨μ λ°λΌ κ°μ¬κ° λ³κ²½λλλ‘ νκΈ°
- μμ μ¬μ μκ°μ΄ λ³ν¨μ λ°λΌ, UI κ° λ³κ²½λμ΄μΌνλλ° μ΄ λ ν μ΄λΈλ·°κ° κ°λ¦¬ν€λ νλ λ³κ²½λμ΄μΌν©λλ€.
- AVPlayer μ Observer λ₯Ό λ±λ‘ν΄μ μκ°μ΄ λ³ν λλ§λ€ Label, Slider κ°, ν μ΄λΈλ·°κ° κ°λ¦¬ν€λ νμ λ³κ²½ν΄μ£Όλ©΄ λ©λλ€.
- TableView λ₯Ό μ€ν¬λ‘€ νλ λμμ κ°λ¦¬ν€λ νμ΄ λ³κ²½λλ©΄ λΆνΈν¨μ΄ μμ μ μμ΄, μ€ν¬λ‘€μ€μμ 체ν¬νμ¬ μ λ°μ΄νΈνμ§ μλλ‘ νμ΅λλ€.
- λ±λ‘ν Observer λ View κ° μ’ λ£λ λ, ν΄μ ν΄μ£Όμ΄μΌ ν©λλ€.
π€ Issue
κ°μ¬ μ 체 νλ©΄μ μ’ λ£νκ³ λ€μ μμνμ λ, κ°μ¬μ μ±ν¬κ° μ ννκ² λ§μ§ μλ μ΄μκ° μμμ΅λλ€.
Observer λ₯Ό λ±λ‘νκ³ νλ©΄μ΄ μ’ λ£λ λ ν΄μ ν΄μ£Όμ§ μμμ, λ°μν λ¬Έμ μμ΅λλ€.
View κ° λ‘λ©λ λ Observer λ₯Ό λ±λ‘νκ³ , View κ° μ’ λ£λ λ Observer λ₯Ό ν΄μ ν΄μ£Όμ΄ ν΄κ²°νμ΅λλ€.
1) LyricsViewController μμ Music Player μ Observer λ±λ‘ / ν΄μ νλ μ½λ
func addObserverToPlayer() {
timeObserver = player.addPeriodicTimeObserver(forInterval: CMTime(seconds: 1, preferredTimescale: 10), queue: DispatchQueue.main) { time in
guard self.isScrolling == false else { return }
self.updateTime(time)
}
}
func removePeriodicTimeObserver() {
if let token = timeObserver {
player.removeTimeObserver(token: token)
timeObserver = nil
}
}
2) Music Player μ μκ°μ΄ λ³κ²½λ λλ§λ€ νΈμΆλμ΄ UI λ₯Ό μ λ°μ΄νΈ νλ ν¨μ
- μμ Observer λ₯Ό λ±λ‘νλ ν¨μμ μν΄, μ€ν¬λ‘€μ΄ λμ§ μλ κ²½μ°μ updateTime λ©μλκ° νΈμΆλ©λλ€.
- updateTime λ©μλλ μ¬μμκ°μ λνλ΄λ Labelκ³Ό Slider μ κ°μ λ³κ²½νκ³ κ°μ¬μ index κ° λ³κ²½λλ κ²½μ°μλ§ TableView μ row λ₯Ό λ³κ²½ν΄μ€λλ€.
- κ°μ¬λ λ§€λ² λ³κ²½λλ κ²μ΄ μλλΌ, μ¬μ μκ°μ΄ νμ¬ κ°μ¬μ λ²μλ₯Ό λμ΄κ° λμλ§ μ λ°μ΄νΈλλλ‘ νκΈ° μν¨μ λλ€.
func updateTime(_ time: CMTime) {
currentTimeLabel.text = player.currentTimeText
progressSlider.value = Float(player.currentValue)
let index = viewModel.getCurrentLyricsIndex()
guard viewModel.prevIndex != index else { return }
updateLyricsTableViewCell(prev: viewModel.prevIndex, index: index)
viewModel.prevIndex = index
}
3) TableView μμ νμ¬ μ¬μλλ κ°μ¬λ‘ λ³κ²½νλ νμ΄λΌμ΄ν νλ ν¨μ
- updateLyricsTableViewCell(prev: index:) λ©μλμμ prev λ μ΄μ μ μ¬μλ κ°μ¬μ μΈλ±μ€, index λ νμ¬ μ¬μμ€μΈ κ°μ¬ μΈλ±μ€μ λλ€.
- νμ¬ μ¬μνλ κ°μ¬μ row λ‘ ν μ΄λΈμ μ€ν¬λ‘€μν€κ³ , Cell μ κΈμ μμ λ°°κ²½λ±μ λ°κΎΈλ ν¨μμ λλ€.
- UITableView.scrollToRow(at:at:animated:) λ©μλλ₯Ό ν΅ν΄ νμ¬ μ¬μλλ κ°μ¬κ° μλ νμΌλ‘ μ΄λλλλ‘ ν©λλ€.
- index κ°μ ν΅ν΄ νμ¬ μ¬μμ€μΈ κ°μ¬μ Cell μ λν΄ .setNowLyrics() λ©μλλ₯Ό νΈμΆν΄ μμμ κ°μ‘°μμΌμ€λλ€.
- prev κ°μ ν΅ν΄ μ΄μ μ¬μμ€μΈ κ°μ¬μ Cell μ λν΄ .desetPrevLyrics() λ©μλλ₯Ό νΈμΆν΄ κΈ°λ³Έ μμμΌλ‘ λ³κ²½ν΄μ€λλ€.
func scrollToRow(at indexPath: IndexPath, at scrollPosition: UITableView.ScrollPosition, animated: Bool)
* 첫λ²μ§Έ νλΌλ―Έν° indexPathλ IndexPath νμ μΌλ‘ μ΄λν row λ₯Ό μ§μ ν©λλ€.
* λλ²μ§Έ νλΌλ―Έν° scrollPositionλ ν΄λΉ νμ΄ ν μ΄λΈλ·°μ μλ¨, μ€κ°, νλ¨ μ€ μ΄λμ μμΉν μ§ μ ν μ μμ΅λλ€.
* μΈλ²μ§Έ νλΌλ―Έν° animated λ μ€ν¬λ‘€ μ λλ©μ΄μ μ μ¬λΆμ λλ€.
func updateLyricsTableViewCell(prev: Int, index: Int) {
lyricsTableView.scrollToRow(at: IndexPath(row: index, section: 0), at: .middle, animated: true)
let indexPath = IndexPath(row: index, section: 0)
if let cell = lyricsTableView.cellForRow(at: indexPath) as? LyricsCell {
cell.setNowLyrics()
}
guard prev >= 0 else { return }
let prevIndexPath = IndexPath(row: prev, section: 0)
if let prevCell = lyricsTableView.cellForRow(at: prevIndexPath) as? LyricsCell {
prevCell.desetPrevLyrics()
}
}
βοΈ TableView λ‘ νΉμ κ°μ¬ ν°μΉ μ ν΄λΉ ꡬκ°λΆν° μ¬μλλλ‘ κ°λ°νκΈ°
- νΉμ κ°μ¬λ₯Ό ν°μΉνλ€λ κ²μ, TableView μ Cell μ ν΄λ¦ν κ²κ³Ό κ°λ€κ³ μκ°νμ΅λλ€.
- UITableViewDelegate νλ‘ν μ½μ λ©μλ μ€, μ μ ν΄λ¦νμ λ νΈμΆλλ ν¨μλ₯Ό μ΄μ©νμ΅λλ€.
- μꡬμ¬νμ μλ, toggleButton μ΄ μ νλ¨μ λ°λΌ μ¬μꡬκ°μ λ³κ²½νκ±°λ, View λ₯Ό μ’ λ£νλλ‘ νμ΅λλ€.
π‘ tableView(_:didSelectRowAt:)
Tells the delegate a row is selected. :: νΉμ νμ΄ μ νλμ λ, Delegate μκ² μλ €μ€λ€.
βοΈ μ¬μꡬκ°μ λ³κ²½νκΈ° μν λ°©λ²
- μ¬μꡬκ°μ λ³κ²½νκΈ° μν΄μ, ν΄λ¦λ κ°μ¬μ indexλ₯Ό ν΅ν΄ λμ λ리μ key λ₯Ό κ°μ Έμμ΅λλ€.
- λμ λ리μ key μΈ μκ°κ°μ ν΅ν΄ AVPlayer μ seek ν¨μλ₯Ό μ΄μ©ν΄ μ¬μμκ°μ λ³κ²½νκ³ UIλ₯Ό μ λ°μ΄νΈνμ΅λλ€.
extension LyricsViewController: UITableViewDelegate, UITableViewDataSource {
// other methods ...
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if toggleButton.isSelected {
let seconds = viewModel.lyricsDict.sorted { $0.key < $1.key }[indexPath.row].key
let time = CMTime(seconds: Double(seconds), preferredTimescale: 100)
player.seek(time)
updateTime(time)
} else {
dismiss(animated: true, completion: nil)
}
}
}
π Github
'π iOS > UIKit' μΉ΄ν κ³ λ¦¬μ λ€λ₯Έ κΈ
[iOS/Swift] TextView - URL (νμ΄νΌλ§ν¬)ν΄λ¦ν΄ μΉλ·° λμμ£ΌκΈ° (0) | 2022.01.25 |
---|---|
[iOS] λ€μ΄λ²μ§λ μ± μ°λ - Place ID λ‘ μ± μ΄κΈ° (2) | 2021.07.16 |
[iOS] FLO μ± κ°λ° μΌμ§ #1. MVVM ν¨ν΄κ³Ό Data Binding (0) | 2021.06.29 |
[iOS] MVC ν¨ν΄ (Model - View - Controller) (0) | 2021.06.18 |
[iOS] λμμΈ ν¨ν΄ & Singleton ν¨ν΄ μ μ©λ μ¬λ‘ (0) | 2021.06.16 |