구현 코드 작성하기
지금까지 배웠던 개념들을 바탕으로 음원 재생기 애플리케이션 구현 코드를 작성해보겠습니다.
강의를 보면서 빠진 코드가 없도록 잘 작성해보도록 합시다.
그리고 프로젝트에 사용된 새로운 개념인 AVFoundation과 Timer 클래스에 대해서도 공부해봅시다.
학습 목표
1. 음원 재생기 애플리케이션 코드를 작성하고 예상한대로 동작하는지 확인합니다.
2. 주석과 mark 키워드를 이해하고 프로젝트에 적용해봅니다.
3. AVFoundation과 Timer 클래스를 이해합니다.
내용 보완
영상 전반 코드
영상 전반에 걸쳐 보이는 코드 중 아래 두 코드를 대체하여 사용해야 합니다.
* UIAlertControllerStyle -> UIAlertController.Style
* UIAlertActionStyle -> UIAlertAction.Style
예) UIAlertControllerStyleAlert는 UIAlertController.Style.alert로 대체하여 사용합니다.
학습하기
AVFoundation
AVFoundation은 다양한 Apple 플랫폼에서 사운드 및 영상 미디어의 처리, 제어, 가져오기 및 내보내기 등 광범위한 기능을 제공하는 프레임워크입니다..
주요 기능
- 미디어 재생 및 편집(QuickTime 동영상 및 MPEG-4 파일 재생/생성/편집, HLS 스트림 재생: 재생가능 파일 목록 링크)
- 디바이스 카메라와 마이크를 이용한 영상 녹화 및 사운드 녹음
- 시스템 사운드 제어
- 문자의 음성화
AVAudioPlayer Class
이번 프로젝트에서 사용한 AVAudioPlayer 클래스에 대해 조금 더 알아봅시다.
AVAudioPlayer 클래스는 파일 또는 메모리에 있는 사운드 데이터를 재생하는 기능을 제공합니다.
AVAudioPlayer 주요기능
- 파일 또는 메모리에 있는 사운드 재생(네트워크에 있는 사운드 파일은 재생 불가)
- 파일 재생 시간 길이의 제한없이 사운드 재생
- 여러 개 사운드 파일 동시 재생
- 사운드의 재생 속도 제어 및 스테레오 포지셔닝
- 앞으로 감기와 뒤로 감기 등의 기능을 지원해 사운드 파일의 특정 지점 찾기
- 현재 재생 정보 데이터 얻기
- 사운드 반복재생 기능
AVAudioPlayer 주요 프로퍼티
var isPlaying: Bool
: 사운드가 현재 재생되고 있는지 아닌지 여부var volume: Float
: 사운드의 볼륨값, 최소 0.0 ~ 최대 1.0var rate: Float
: 사운드의 재생 속도var numberOfLoops: Int
: 사운드 재생 반복 횟수- 기본값은 0으로 사운드 1회 재생 후 자동 종료
- 양수값으로 설정시 설정값+1회 재생(ex. 1로 설정시 2회 재생 후 종료)
- 음수값으로 설정시
stop
메서드가 호출 될때까지 무한 재생
var dutation: TimeInterval
: 사운드의 총 재생 시간(초 단위)var currentTime: TimeInterval
: 사운드의 현재 재생 시각(초 단위)protocol AVAudioPlayerDelegate
: 사운드 재생 완료, 재생 중단 및 디코딩 오류에 응답할 수 있는 프로토콜
AVAudioPlayer 주요 메서드
// 특정 위치에 있는 사운드 파일로 초기화
func init(contentOf: URL)
// 메모리에 올라와있는 데이터를 이용해 초기화
func init(data: Data)
// 사운드 재생
func play()
// 특정 시점에서 사운드 재생
func play(atTime: TimeInterval)
// 사운드 일시 정지
func pause()
// 사운드 재생 정지
func stop()
Timer
Timer 클래스는 일정한 시간 간격이 지나면 지정된 메시지를 특정 객체로 전달하는 기능 제공합니다.
Timer 특징
- 타이머는 런 루프(run loops)에서 작동합니다.
- 타이머를 생성할 때 반복 여부를 지정합니다.
- 비 반복 타이머: 한 번 실행된 다음 자동으로 무효화 됩니다.
- 반복 타이머: 동일한 런 루프에서 특정 TimeInterval 간격으로 실행됩니다. 반복되는 타이머 기능을 정지하려면
invalidate()
메서드를 호출해 무효화합니다.
Timer 주요 프로퍼티
var isValid: Bool
: 타이머가 현재 유효한지 아닌지 여부var fireDate: Date
: 다음에 타이머가 실행될 시각var timeInterval: TimeInterval
: 타이머의 실행 시간 간격(초 단위)
Timer 생성 메서드
- 타이머 생성과 동시에 런 루프에
default mode
로 등록하는 클래스 메서드
class func scheduledTimer(withTimeInterval: TimeInterval, repeats: Bool, block: (Timer) -> Void)
class func scheduledTimer(timeInterval: TimeInterval, target: Any, selector: Selector, userInfo: Any?, repeats: Bool)
class func scheduledTimer(timeInterval: TimeInterval, invocation: NSInvocation, repeats: Bool)
- 타이머 생성 후 수동으로 타이머 객체를
add(_:forMode:)
메서드를 이용해 런 루프에 추가해줘야 하는 메서드
func init(timeInterval: TimeInterval, invocation: NSInvocation, repeats: Bool)
func init(timeInterval: TimeInterval, target: Any, selector: Selector, userInfo: Any?, repeats: Bool)
func init(fireAt: Date, interval: TimeInterval, target: Any, selector: Selector, userInfo: Any?, repeats: Bool)
comment
빌드도 되고 에러나 경고메세지 하나 없는데 돌아가지 않는 이유를 모르겠네요... 다운받은 프로젝트는 되니 버전이나 제 컴퓨터(애플 실리콘) 문제일까요?
빌드는 되는데 플레이 버튼 클릭시 unrecognized selector sent to instance 에러가 나시는 분들은 플레이 버튼의 IBAction과 코드를 다시 연결 해주시면 됩니다.
이전 강의에서 작성한 touchUpPlayPauseButton 메소드의 파라미터가 _Sender -> _ sender 로 변경되어서 인터페이스 객체 빌더랑 코드 연결이 안되어 있었네요.
예제를 따라서 작성하고 이후 프로젝트 진행시 너무 어려움을 겪고 있는데 어떠한 방법으로 학습을 해야 할까요?
IBAction func touchUpPlayPauseButton() 함수에 if, else 두번 쓰신 이유라도 있을까요?
audioPlayerDidFinishPlaying 함수는 자동적으로 player의 실행이 완료되었을때 수행되는 이벤트 메소드인가요?
예제 프로젝트 파일이 아닌,
지금 현재 강좌에서 강사님 언급하신대로 따라서 작성한 코드로는
음원 재생이 안되는게 정상인가요?
강의에서 언급하신 코드들만 작성해서 빌드하여 재생 버튼을 클릭하면
음원 재생이 안되고 일시정지 버튼도 나타나지 않습니다.
self를 반드시 써줘야 하나요? self를 쓰는 이유는 무엇인가요?
프로젝트 다운로드가 안됩니다.
addPlayPauseButton 메소드에서 button.translatesAutoresizingMaskIntoConstraints = false 를 주석처리해도 정상적으로 작동하는데 굳이 써야하는 이유가 있을까요?
첨부파일에 있는 코드로 하고 있는데 오류가 나타나서 질문드립니다.
커스텀 메소드 중에 addPlayPauseButton() 중에 UIButtonType.custom, UIControlState, UIControlEvents, NSLayoutAttribute, 마찬가지로 addTimeLabel(), addProgressSlider() 에서도 같은 부분에서 에러가 납니다.
게시글중에서 UIAlertActionStyle 부분처럼 .의 유무에 따라 달라지는 swift 버전차이같아 보여서 제 임의대로 . 을 찍어봤지만 해결되지 않아서 댓글 남깁니다. 근데 또 신기한건 이 메소드 부분을 주석처리해도 잘 실행이 된다는게... 조금 당황스럽긴하지만
혹시 해결방법 아시면 알려주세요!
주석 꿀팁 너무 감사합니다.
코드에서 self.timer.fire() 메소드 필요없지 않나요? 없어도 블록이 잘 동작하던데.. 정확한 역할이 궁금합니다.
문서에는 Causes the timer's message to be sent to its target. 라고 나와있네요. 실제로 타겟 클래스를 만들어서 타겟 설정해주고 연습해봤지만 fire()를 안해줘도 잘 동작하더라구요.
해당 내용을 직접 타이핑해 실습 할 경우
UIAlertControllerStyle, UIAlertActionStyle에서 넘어가지 않을 수 있습니다.
UIAlertController.Style 로 사용하시거나 프로젝트 Build Settings에서 혹은 Swift 3 또는 4로 변경해 주시면 정상적으로 작동됩니다. (Swift 4.2에서 강제)
사운드의 재생 속도 제어 및 스테레오 위지 지정 -> 위치 오타인거 같습니다ㅎㅎ
AVAudioPlayer를 보고 있는데 설명에서
이렇게 되어있는데
두 경우에 따라 init메소드의 파라미터도 달라지잖아요
그런데 파일/메모리에 있는 사운드가 정확히 이해가 가지않습니다.
파일은 현재 앱에 올라간 파일 경로 자체를 찾아서 사운드 재생을하고
메모리에 있는 사운드라면 예제 코드처럼 사운드 에셋을 이용해서 사운드 재생을 한다 라고 이해하면 될까요?
Function makeAndFireTimer() 에서
self.timer = Timer.scheduledTimer(withTimeInterval: 0.01, repeats: true, block: { [unowned self] (timer: Timer) in
…
})
처럼 강한 순환 참조를 해결하기 위해서 캡쳐 리스트를 사용하는 것으로 생각되는데요
self.timer = Timer.scheduledTimer(withTimeInterval: 0.01, repeats: true, block: { (timer: Timer) in
…
})
이렇게 작성을 해서 만약 강한 순환 참조가 발생하게 된다면 어떤 객체들 간의 어떤 형태로 참조가 되는지 정확히 알고싶습니다
Delegate는 리스너 같은 개념으로 이해하면 될까요?
오타 발견했습니다.
touchUpPlayPauseButton(_:) 에서 두개의 if문 모두 'sender.isSelected' 라는 같은 조건을 가지고 있는데 1개의 if문을 사용하지 않고 2개의 if문을 사용한 이유가 있나요?