0. 시작
한참동안 또 안건드리다가 다시 잡게되었다.
이전에 잠금화면에서 재생화면으로 뜨는거와 mvvm으로 나눠둔것까지 했었다. 기능적으론 딱히 더 필요한게 재생바에서 재생시간옴기는거려나 그럼 이거 추가하는김에 SwiftUI를 공부도 하려니 uikit부분을 swiftui로 변경할거다.
SwiftUI 뷰만 만들어봐야 별로 다를것없어보이니 기존과 꽤나 다른점인 @Published 이게 눈에 보여서 @EnviromentObject이걸 뷰모델에 넣어서 뷰와 연결해볼거다
@EnviromentObject는 컨테이너시스템에서 시스템 시작할때 환경변수파일 넣어주듯이 넣는 개념이라 이해하고있다
이 객체로 선언된건 바로 뽑아와서 쓰고 Published 태그가 붙은 변수들은 변경될때마다 이벤트를 발생시켜 구독중인 애들이 새로 그려진다는듯하다
이것만들으면 그냥 notification을 변수의 set 항목에 지정해서 쓴거아닌가 싶은데 뭐 아무튼 설명이 그러니 그냥 쓰려고한다
1. 기존 프로젝트에서 어떻게 바꿀까?
기존 파일 구성이
ControllViewController.swift
MusicInfo.swift
MusicPlayer.swift
NewMainVC.swift
PlayList.swift
PlayListCell.swift
였는데
NewMainVC가 겉으로
여기에 테이블을 넣고 테이블셀은 PlayListCell로 지정해서 모양을 잡아줬고
테이블 아래로는 ControllViewController를 집어넣어서 위에는 파일리스트 아래는 재생버튼으로 뷰를 마무리 하고
실 플레이 기능은 MusicPlayer와 음악데이터값은 PlayList에 나눠서 구성했었다.
기능별 분리를 해놔서 swiftUI로 변경은 어렵지 않을거라 생각했건만
실 기능과 데이터가 있는 MusicPlayer와 PlayList 클래스를 ObservableObject를 달아줘서 밖으로 쓸 변수를 @Pbulished를 해줬다
그리고 이건 망했다 왜 망했는지는 뷰를 그리고 나서 이야기하자
뷰는 일단 따로 ai로 그려진걸 그려볼까했는데 막상 하려니 내가 원하지 않는 기능들이 덕지덕지 붙은 그림을 줘서 그냥 포기.
기존과 비슷하게 그러나 swiftUI기본 디자인이 워낙 괜찮아서 같은 위치에 같은 기능이나 기존보다 살짝 더 디자인적으로 나아졌다
우선 가장 겉은 부분은
MainScreen 이라는 이름으로 뷰를 그려줬다 기존 방식그대로 위에는 리스트, 아래는 컨트롤바
VStack(spacing: 0) {
PlayListView()
ControllBarView()
}
기본 스페이싱을 없애기위애 따로 spacing 0을 넣어줬다.
PlayListView는 뭐 별다를거 없는데
var body: some View {
List(player.musicData) { item in
Text(item.title)
}
.listStyle(.automatic)
.onAppear(perform: {
player.loadList()
})
}
셀지정이나 그런것도 없이 바로 Text를 이용해 뿌려주면 끝! 너무 간단해서 이걸 굳이 나눴어야 했나 싶을 정도로 끝이 났고
생각해보니 원본도 테이블뷰를 메인에 넣었던거같아서 실수했다.
아래는 컨트롤바로 기존과 다른점이라곤 버튼에 배경을 넣어줬다
원래는 자리차지가 어느정도 되나 집어넣은 테스트 색상이였는데 보다보니 괜찮아보여 그냥 냅뒀다
var body: some View {
VStack(
alignment: .leading,
spacing: 4,
content: {
Button(action: {
player.loadList()
}, label: {
Text("내부파일 재로드")
})
.padding(.horizontal)
.foregroundColor(.white)
.background(Color.blue)
.cornerRadius(10)
.font(Font.title3)
HStack(content: {
Text("\(minuteString(to: player.currentTime))")
Slider(value: $player.currentTime, in: 0...player.duration) { isEditing in
if !isEditing {
player.seek(to: player.currentTime)
}
}
Text("\(minuteString(to: player.duration))")
})
.padding(.horizontal, 8)
HStack(content: {
Spacer()
ControllButton(text: "❮") {
_ = player.previousMusic()
}
ControllButton(text: player.isPlaying ? "❚❚" : "▶︎") {
_ = player.play()
}
// .disabled(true)
ControllButton(text: "❯") {
_ = player.nextMusic()
}
Spacer()
})// HStack
Color.clear
.frame(height: 8)
})
}
struct ControllButton: View {
let text: String
let action: () -> Void
var body: some View {
Button(action: action) {
Text(text)
.font(.title)
.frame(width: 50)
.foregroundColor(.white)
.background(Color.blue)
.cornerRadius(10)
}
}
}
뷰 파일을 이렇게 만들려고했으면 기존엔 앵커로 엄청 줄줄이 써댔거나 snapkit으로 그나마 짧게 잡아줬을텐데
이런거 보면 정말 간단해졌다.
2. 뷰를 만들어주고 연결해주려니 에러가 났다.
1의 뷰랑 연결시켜야하는 기존의 MusicPlayer와 PlayList 클래스에대한 문제는...
MusicPlayer에서 PlayList를 참조한다는점에서 문제가 발생했다
--
ControllBarView.swift:27:36: error: cannot find type 'PlayList' in scope
@EnvironmentObject var playList: PlayList
--
둘다 ObservableObject로 만들어주고 처음화면의 뷰에다가
.environmentObject(musicPlayer)
.environmentObject(playList)
이렇게 두개를 주입해줬는데 musicPlayer에서 playlist를 찾지못한다는 에러가 발생 했다 처음하는 swiftUI라 뭔가 잘못했구나 싶어 gpt에게 열심히 물어봤지만, 무료분량이 끝날때까지 이놈은 이상한 소리만하고 결국 고쳐지지 못했었다
그리고 누군가 올린 블로그글에서 문제를 찾았는데....
뷰모델에선 뷰모델을 부르지 못한다고한다
단순히 notifiation이라고 생각하고 있던 내가 아 잘못이해했구나 한걸 깨달게되는 사건이였다
싱글톤에서 벗어나 바인드도 하고하는 좀 rx스타일같은걸 배우고싶었는데 이제와서 다시 싱글톤으로 하기엔 미묘하다
결국 그렇게 난 이 상태를 포기하고 MusicPlayer와 PlayList 클래스를 합쳤다
주입해주던 클래스도 하나로 변경되서
.environmentObject(player)
로 깔끔해졌다.
뭐 결국 그냥저냥.... 그렇게 되었다
아 재생바는 slider로 연결해서 값 변경이 완료되면
func seek(to time: TimeInterval) {
avPlayer.currentTime = time
currentTime = time
}
이 함수로 변경해주도록 해서 마무리.
뭐 결국 그렇게 테스트해보고 잘됐다.
역시 기본설정들이 다크모드용으로 따로 스타일 지정안해줘도 보기좋게 나온다
리스트도 딱히 만져준게 없는데도 나름 예쁘게 잘나왔고 슬라이더도 잘동작하니 만족
여기서 더 만질일이 있으련지는 모르겠다
나중에 뭔가 추가한다면 리스트에서 선택해서 재생하도록하는거?
그런데 나는 그냥 긴 파일을 다운받아서 재생해놓기에 쓸일이 있으려나
아니면 웹주소로 재생되게하는거?
이건 초기 컨셉이였던거같은데 생각나면 해야지
변경된 전체 코드는 역시 내 깃에 ~
https://github.com/wiwi-git/proj_ypl/
GitHub - wiwi-git/proj_ypl: ios에서 간단하게 쓸 백그라운드 플레이어가 필요하다
ios에서 간단하게 쓸 백그라운드 플레이어가 필요하다. Contribute to wiwi-git/proj_ypl development by creating an account on GitHub.
github.com
3. ps
다 쓰고 나니 생각났는데
기존 uikit으로 만든 프로젝트이기에 이 프로젝트에는 Appdelegate와 SceneDelegate파일이 있는 프로젝트다
거기다 추가로 스토리보드 기반이 아니기에 SceneDelegate에서 루트 vc를 설정해준 프로젝트다
이걸 시작지점부분을 swfitUI파일과 연결해줘야하는데
swiftUI는 struct이고 view이기에 viewcontroller가 없다 그래서 추가로 따로 만들어줘야한다 이렇게
let hostingController = UIHostingController(rootView: MainScreen())
window?.rootViewController = hostingController
그렇게 만들어주고 기존에 하던데로 루트를 만들어준거로 넣어주면 끝!
참고로 mainScreen에는 App 프로토콜뷰가 없다 App은 SwiftUI기반 프로젝트를 생성하면 나오는애
'iOS > swift' 카테고리의 다른 글
background audio play - 4 [mvvm 패턴] (1) | 2024.11.27 |
---|---|
background audio play - 3 [제어센터에서의 컨트롤] (5) | 2024.11.10 |
background audio play - 2 [AVAudioSession] (1) | 2024.09.15 |
background audio play - 1 [AVAudioPlayer] (2) | 2024.09.12 |
앱이 열리면 뱃지 카운트 0으로 설정하기 (0) | 2024.04.28 |