본문 바로가기

iOS/Toy project

[iOS : Toy Project] Apple Music App (6) : 뷰사이 데이터 이동

이전 포스팅

- AppleMusicApp(1) : 뷰 구성

- AppleMusicApp(2) : Track 모델 (데이터 구조)

- AppleMusicApp(3) : UiCollectionViewCell 업데이트

- AppleMusicApp(4) : HeaderView (CollectionReusableView)

- AppleMusicApp(5) : Player 화면 + 싱글톤

 

자세한 코드는 여기로!

https://github.com/yexjin/iOS_Study/tree/main/AppleMusicApp

 

GitHub - yexjin/iOS_Study: iOS 토이프로젝트 모음집📱

iOS 토이프로젝트 모음집📱. Contribute to yexjin/iOS_Study development by creating an account on GitHub.

github.com

 

 

 

 


9/15

- HomeView 에서 PlayerView 이동 구현

 

HomeView에서 PlayerView 이동 구현 포스팅

 

[iOS] UICollectionViewDelegate

🌱 UICollectionViewDelegate - 사용자와 CollectionView의 아이템 사이의 상호작용을 관리해주기 위한 객체들에 의해 채택된 방식 - 이 프로토콜의 메소드들은 모두 Optional! 이 프로토콜을 통해 CollectionView.

yexjinitlog.tistory.com

↑ 근데 여기서는 그냥 단순 View이동이지 어떠한 데이터도 넘겨지지 않았다.

 

데이터를 어떻게 넘길까?

 

일단 PlayerViewController에서 전 포스팅에서 만들었던 Player 싱글톤 객체를 만들어 프로퍼티로 추가한다.

// PlayerViewController.swift

let simplePlayer = SimplePlayer.shared

 

다시 HomeViewController로 돌아와서,

playerVC 안에 존재하는 simplePlayer(AVplayer 싱글톤 객체)에 현재 아이템을 교체하여 PlayerViewController 띄우자

// HomeViewController.swift
extension HomeViewController: UICollectionViewDelegate {

    func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
        // TODO: 곡 클릭시, 플레이어 뷰 띄우기
        let playerStoryboard = UIStoryboard.init(name: "Player", bundle: nil)
        
        guard let playerVC = playerStoryboard.instantiateViewController(withIdentifier: "PlayerViewController") as? PlayerViewController else { return }
        
        let item = trackManager.tracks[indexPath.item]
        playerVC.simplePlayer.replaceCurrentItem(with: item)

        present(playerVC, animated: true, completion: nil)

    }
    
}

↑ 이렇게 하면 Cell을 클릭했을 때 데이터가 PlayerViewController로 넘어가게된다.

 

Cell 뿐만 아니라,

요 헤더도 View가 이동되어 곡이 플레이 되도록 데이터를 넘겨줘야하는데 

요 tapHandler 부분을 수정해보자

 

header.tapHandler = {item -> Void in
    // Player 뷰 띄우기
    let playerStoryboard = UIStoryboard.init(name: "Player", bundle: nil)

    guard let playerVC = playerStoryboard.instantiateViewController(withIdentifier: "PlayerViewController") as? PlayerViewController else { return }

    playerVC.simplePlayer.replaceCurrentItem(with: item)

    self.present(playerVC, animated: true, completion: nil)

}

↑ CollectionViewCell에서 구현한 것과 다른 점은 현재 재생되는 곡을 "item" 인자로 받아온다는 것

 

 

 

데이터 넘기기까지 완료

 

 

 

 

 


 

 

9/16 ~ 21

이제 받아온 데이터를 띄워보자

이 부분은 PlayerViewController.swift 에서 진행

//  PlayerViewController.swift

import UIKit
import CoreMedia

class PlayerViewController:
    UIViewController {
    
    @IBOutlet weak var thumbnailImageView: UIImageView!
    @IBOutlet weak var titleLabel: UILabel!
    @IBOutlet weak var totalDurationLabel: UILabel!
    @IBOutlet weak var currentTimeLabel: UILabel!
    @IBOutlet weak var artistLabel: UILabel!
    @IBOutlet weak var playControlButton: UIButton!
    @IBOutlet weak var timeSlider: UISlider!
    
    //TODO: SimplePlayer 만들고 프로퍼티 추가
    let simplePlayer = SimplePlayer.shared
    
    // 로드 시 호출
    override func viewDidLoad() {
        super.viewDidLoad()
        updatePlayButton()
        updateTime(time: CMTime.zero)
        
        // TODO: Time Observer 구현
    }
    
    // 보이기 전에 호출
    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        updateTintColor()
        updateTrackInfo()
    }
    
    // 사라지기 전에 호출
    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
    }

}

extension PlayerViewController {
    
    // 재생 멈춤 버튼 업데이트
    func updatePlayButton() {
        
    }
    
    // currentTimeLabel과 durationTimeLabel의 text값 초기화
    func updateTime(time: CMTime) {
        
    }
    
    // 다크모드 시, Tint 컬러 지정
    func updateTintColor() {
        
    }
    
    // 받아온 item을 가지고 업데이트 = simplePlayer 객체 업데이트
    func updateTrackInfo() {
        
    }
    
    // 시간 정보(Double형)를 00:00(String형)으로 보여줌
    func secondToString(sec: Double) -> String {
        guard sec.isNaN == false else { return "00:00" }
        let totalSeconds = Int(sec)
        let min = totalSeconds / 60
        let seconds = totalSeconds % 60
        return String(format: "%02d:%02d", min, seconds)
    }
}

전체적인 틀은 위와 같다.

 

 

secondToString 함수에서 %02d를 살펴보면

% : 명령어

0 : 채워질 숫자

2 : 몇자리수로 나타낼까?

d : 10진수(정수)

 

 

PlayViewController.swift

beginDrag, endDrag 의 기본값 isSeeking을 Bool type으로 구분

togglePlayButton : 재생버튼과 일시정지 버튼을 교차해서 보여줌

updatePlayButton() : 현재 재생중인지 멈춤인지 판단하여 알맞는 재생 / 일시정지 아이콘을 업데이트

 

 

 

 

이제, 큰 틀은 확인했으니 각 Method를 구현해보자

1️⃣ updateTrackInfo()

- 받아온 item을 가지고 업데이트 = simplePlayer 객체 업데이트

func updateTrackInfo() {
    guard let track = simplePlayer.currentItem?.convertToTrack() else { return }
    thumbnailImageView.image = track.artwork
    titleLabel.text = track.title
    artistLabel.text = track.artist
}

 

convertToTrack하는 이유 : Cell을 클릭했을 때 PlayerViewController에서 현재 아이템이 AVPlayer타입이니 때문에!

 

2️⃣ togglePlayButton()의 updatePlayButton()

죠기 updatePlayButton() 구현해보기
이 함수들고 연결될 Button!
이버튼은 system image를 사용하였고, configuration을 다음과 같이 설정해주었다.

이 버튼을 코드에 적용시켜주기 위해서는 configuration 객체를 만들어주어야한다.

// 재생 멈춤 버튼 업데이트
func updatePlayButton() {
    if simplePlayer.isPlaying{
        let configuration = UIImage.SymbolConfiguration(pointSize: 40)
        let image = UIImage(systemName: "pause.fill", withConfiguration: configuration)
        playControlButton.setImage(image, for: .normal)
    } else {
        let configuration = UIImage.SymbolConfiguration(pointSize: 40)
        let image = UIImage(systemName: "play.fill", withConfiguration: configuration)
        playControlButton.setImage(image, for: .normal)
    }
}

setImage([바꿀이미지], for: [UIControl.State])

 

 

 

🚀 데이터 이동된 Player 화면

 

 

 

이제 남은건 Slider 기능!