質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
86.12%
API

APIはApplication Programming Interfaceの略です。APIはプログラムにリクエストされるサービスがどのように動作するかを、デベロッパーが定めたものです。

解決済

Swift TableViewをSearchBarで検索したいです

gon_878
gon_878

総合スコア2

API

APIはApplication Programming Interfaceの略です。APIはプログラムにリクエストされるサービスがどのように動作するかを、デベロッパーが定めたものです。

1回答

0グッド

0クリップ

610閲覧

投稿2022/05/10 08:34

前提

「Alamofireを使用してAPIを叩き、その結果を反映したTableView」を「SearchBar」で検索する方法を教えてください

実現したいこと

画像は拾い画のサンプルですが、APIを叩いて反映させたTableViewで以下の画像と同じようなことを実現したいです。
イメージ説明

発生している問題・エラーメッセージ

ググりまくって何パターンが試したのですがsearchbarがうまく検索してくれないです。

該当のソースコード

swift

1//QiitaArticleViewController(ファイル名です) 2import UIKit 3 4 5struct QiitaArticle: Codable { 6 let title: String 7 let url: String 8 let user: QiitaUser 9} 10 struct QiitaUser: Codable { 11 let id: String 12 let imageUrl: String 13 14 enum CodingKeys: String, CodingKey { 15 case id 16 case imageUrl = "profile_image_url" 17 } 18 } 19 20class QiitaArticleViewController: UIViewController { 21 22 @IBOutlet weak var tableView: UITableView! 23 @IBOutlet weak var qiitaSearchBar: UISearchBar! 24 25 var articles: [QiitaArticle] = [] 26 27 override func viewDidLoad() { 28 super.viewDidLoad() 29 tableView.dataSource = self 30 tableView.delegate = self 31 32 qiitaSearchBar.delegate = self 33 34 35 36 let nib = UINib(nibName: "QiitaTableViewCell", bundle: nil) 37 tableView.register(nib, forCellReuseIdentifier: "QiitaTableViewCell") 38 tableView.rowHeight = 90 39 getQiitaArticles() 40 } 41} 42 43extension QiitaArticleViewController { 44 func getQiitaArticles() { 45 ApiClient().request { result in 46 switch result { 47 case.success(let articles): 48 self.articles = articles 49 DispatchQueue.main.async { 50 self.tableView.reloadData() 51 } 52 case.failure(let error): 53 self.showApiAlert(error: error) 54 } 55 } 56 } 57 58 func showApiAlert(error: APIError) { 59 let alert = UIAlertController(title: error.errorTitle().title, message: nil, preferredStyle: .alert) 60 alert.addAction(UIAlertAction(title: "閉じる", style: .cancel, handler: nil)) 61 self.present(alert, animated: true, completion: nil) 62 } 63} 64 65extension QiitaArticleViewController: UITableViewDataSource,UITableViewDelegate { 66 func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 67 return articles.count 68 } 69 70 func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 71 guard let cell = tableView.dequeueReusableCell(withIdentifier: "QiitaTableViewCell", for: indexPath) as? QiitaTableViewCell else { 72 return UITableViewCell() 73 } 74 let article = articles[indexPath.row] 75 cell.set(title: article.title, author: article.user.id, imageUrl: article.user.imageUrl) 76 return cell 77 } 78 79 func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { 80 let storyboard = UIStoryboard(name: "WebViewController", bundle: nil) 81 guard let webViewController = storyboard.instantiateViewController(withIdentifier: "WebViewController") as? WebViewController else { return } 82 let article = articles[indexPath.row] 83 webViewController.url = article.url 84 navigationController?.pushViewController(webViewController, animated: true) 85 } 86} 87 88extension QiitaArticleViewController: UISearchBarDelegate { 89 func searchBarSearchButtonClicked(_ searchBar: UISearchBar) { 90 91 92      //おそらくこの {} 内で何かするのではないかと思っています。 93 94}
//QiitaAPI(ファイル名です) import Foundation import Alamofire struct ErrorTitle { let title: String } enum APIError: Error { case invaildURL case invaildResponce case unknown(Error) func errorTitle() -> ErrorTitle { switch self { case .invaildURL: return ErrorTitle(title: "無効なURL") case .invaildResponce: return ErrorTitle(title: "無効なレスポンス") case .unknown(let error): return ErrorTitle(title: "予期せぬエラー\(error)") } } } typealias ResultHandler<T> = (Result<T, APIError>) -> Void struct ApiClient { func request(handler: @escaping ResultHandler<[QiitaArticle]>) { let urlString = "https://qiita.com/api/v2/tags/iOS/items" guard let url = URL(string: urlString) else { handler(.failure(.invaildURL)) return } AF.request(urlString) .response { response in guard let data = response.data else { handler(.failure(.invaildResponce)) return } do { let articles = try JSONDecoder().decode([QiitaArticle].self, from: data) handler(.success(articles)) } catch { handler(.failure(.unknown(error))) } } } }
//QiitaTableViewCell(ファイル名です) class QiitaTableViewCell: UITableViewCell { @IBOutlet weak var iconImageView: UIImageView! @IBOutlet weak var titleLabel: UILabel! @IBOutlet weak var authorLabel: UILabel! func set(title: String, author: String, imageUrl: String) { Nuke.loadImage(with: URL(string: imageUrl), into: iconImageView) titleLabel.text = title authorLabel.text = author } }
//WebViewController(ファイル名です) import UIKit import WebKit enum RectSize: CGFloat{ case x = 0 case y = 50 } class WebViewController: UIViewController { var webView = WKWebView() var url: String? override func viewDidLoad() { super.viewDidLoad() let screenWidth:CGFloat = view.frame.size.width let screenHeight:CGFloat = view.frame.size.height let rect = CGRect(x: RectSize.x.rawValue, y: RectSize.y.rawValue, //ここいじる ここに関係する width: screenWidth, height: screenHeight) let configuration = WKWebViewConfiguration() webView = WKWebView(frame: rect, configuration: configuration) view.addSubview(webView) guard let url = URL(string: self.url ?? "") else { return } let request = URLRequest(url: url) webView.load(request) } }

以下のような質問にはグッドを送りましょう

  • 質問内容が明確
  • 自分も答えを知りたい
  • 質問者以外のユーザにも役立つ

グッドが多くついた質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

気になる質問をクリップする

クリップした質問は、後からいつでもマイページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

下記のような質問は推奨されていません。

  • 間違っている
  • 質問になっていない投稿
  • スパムや攻撃的な表現を用いた投稿

適切な質問に修正を依頼しましょう。

回答1

1

ベストアンサー

まず、記事の配列が 2 つ必要です。ひとつは取得したすべての記事、もうひとつは検索結果で、表示するのは検索結果の配列にします。

swift

1class QiitaArticleViewController: UIViewController { 2 3 var allArticles: [QiitaArticle] = [] // 追加 4 var articles: [QiitaArticle] = []

API の結果を取得したら、両方の配列に値をセットします。また、この際に reloadData だけ main スレッドで呼んでますが、Alamofire のコールバックはデフォルトで main スレッドなので、そのまま呼べばいいはずです。(逆に、URLSession を使う場合など、結果がバックグラウンドスレッドで返る場合は、self.articles などに値をセットするのも main スレッドでやる必要が…。)

swift

1extension QiitaArticleViewController { 2 func getQiitaArticles() { 3 ApiClient().request { result in 4 switch result { 5 case.success(let articles): 6 self.allArticles = articles // 追加 7 self.articles = articles 8 self.tableView.reloadData() // DispatchQueue.main.async 不要

検索の処理は、allArticles の中で検索文字列を含むものを articles に格納して reloadData します。ちなみに、searchBarSearchButtonClicked よりも searchBar(:textDidChange:) でやった方がリアルタイムに検索できて良いのでは。

swift

1extension QiitaArticleViewController: UISearchBarDelegate { 2 // インクリメンタルサーチ 3 func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) { 4 searchArticles(searchText) 5 } 6 7 // textDidChange で検索する場合、これは不要 8 func searchBarSearchButtonClicked(_ searchBar: UISearchBar) { 9 searchArticles(searchBar.text ?? "") 10 } 11 12 func searchArticles(_ searchText: String) { 13 if searchText != "" { 14 articles = allArticles.filter { article in 15 // タイトルで検索 (大文字小文字の違いを無視) 16 return article.title.localizedCaseInsensitiveContains(searchText) 17 } 18 } else { 19 // 検索文字列が空なら、すべて表示 20 articles = allArticles 21 } 22 self.tableView.reloadData() 23 } 24}

投稿2022/05/11 00:35

編集2022/05/11 00:41
hoshi-takanori

総合スコア7740

gon_878👍を押しています

良いと思った回答にはグッドを送りましょう。
グッドが多くついた回答ほどページの上位に表示されるので、他の人が素晴らしい回答を見つけやすくなります。

下記のような回答は推奨されていません。

  • 間違っている回答
  • 質問の回答になっていない投稿
  • スパムや攻撃的な表現を用いた投稿

このような回答には修正を依頼しましょう。

回答へのコメント

gon_878

2022/05/11 02:09

非常に分かりやすいご回答をいただきありがとうございます! 無事実装できました! また、Alamofireのmainスレッドの件、インクリメンタルサーチ、大変勉強になりました! この度は本当にありがとうございました!

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
86.12%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問

同じタグがついた質問を見る

API

APIはApplication Programming Interfaceの略です。APIはプログラムにリクエストされるサービスがどのように動作するかを、デベロッパーが定めたものです。