SwiftUIでいこう! - Search App! - 2
引き続き参考サイトを見ながらコードを書いていきます。
新しいファイルSonglListViewModel.swiftを作ってリストが表示できるViewを作っていきます。リストを作るためのモデルを作ります。
import SwiftUI
import Combine
を宣言します。Combineを使います。
まず、リストを作るためのModelを定義します。
class SongViewModel:Identifiable,ObservableObject{
let id:Int
let trackName:String
let artistName:String
@Published var artwork:Image?
init(song:Song) {
self.id = song.id
self.trackName = song.trackName
self.artistName = song.artistName
}
}
を作ります。DataModelのデータを使えるように代入します。
class SongListViewModel{}を作っていきます。まず変数宣言。
class SongListViewModel: ObservableObject {
@Published var searchTerm:String = ""
@Published public private(set) var songs:[SongViewModel] = []
private let dataModel:DataModel = DataModel()
private var disposables = Set<AnyCancellable>()
そして
private func loadSongs(seachTerm:String){
songs.removeAll()
dataModel.loadSongs(searchTerm: seachTerm){songs in
songs.forEach{self.appendSong(song: $0)}
}
}
private func appendSong(song:Song){
let songViewModel = SongViewModel(song:song)
DispatchQueue.main.sync {
self.songs.append(songViewModel)
}
}
Combineフレームワークを使ってデータの受け渡しをします。
init() {
$searchTerm
.sink(receiveValue:loadSongs(seachTerm:))
.store(in:&disposables)
}
@Publishedで送られたデータを.sink()で受けます。Combineについては以下参考になります。
ContentView()に戻ってリスト表示とサーチバーで検索できるように編集します。
まずアートワークを表示する構造体を作ります。
struct ArtworkView:View{
let image:Image?
var body:some View{
ZStack{
if image != nil{
image
}else{
Color(.systemIndigo)
Image(systemName: "music.note")
.font(.largeTitle)
.foregroundColor(.white)
}
}
.frame(width: 50, height: 50)
.shadow(radius: 5)
.padding(.trailing)
}
}
リストの表示。HStackでアートワークを左に、右側にはVStackでトラック名とアーチスト名を縦に並べます。
struct SongView:View{
@ObservedObject var song:SongViewModel
var body: some View{
HStack {
ArtworkView(image: song.artwork)
.padding(.trailing)
VStack(alignment: .leading){
Text(song.trackName)
Text(song.artistName)
.font(.footnote)
.foregroundColor(.gray)
}
}
.padding()
}
}
このSongViewをListで表示させます。
List(viewModel.songs){song in
SongView(song:song)
}
.listStyle(PlainListStyle())
NavigationView全体は
NavigationView{
VStack{
SearchBar(searchTerm: $viewModel.searchTerm)
if viewModel.songs.isEmpty{
EmptyStateView()
}else{
List(viewModel.songs){song in
SongView(song:song)
}
.listStyle(PlainListStyle())
}
}
.navigationBarTitle("Music Search")
}
となります。
あとは SearchBarを編集していきます。
@Binding var searchTerm:String
編集部分です。
func makeCoordinator() -> SearchBarCordinator {
SearchBarCordinator(searchTerm: $searchTerm)
}
class SearchBarCordinator:NSObject,UISearchBarDelegate{
@Binding var searchTerm:String
init(searchTerm:Binding<String>) {
self._searchTerm = searchTerm
}
func searchBarSearchButtonClicked(_ searchBar:UISearchBar){
searchTerm = searchBar.text ?? ""
UIApplication.shared.windows.first{$0.isKeyWindow}?.endEditing(true)
}
}
編集が終わればシュミレーターで試してみます。
シュミレーターを起動する前に以下を編集(太字部分)します。
struct SampleAppApp: App {
var body: some Scene {
WindowGroup {
ContentView(viewModel: SongListViewModel())
}
}
}
これをしないとエラーが出ます。
この記事が気に入ったらサポートをしてみませんか?