見出し画像

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())
     }
   }
}

これをしないとエラーが出ます。

この記事が気に入ったらサポートをしてみませんか?