美文网首页
SearchViewController 源码学习

SearchViewController 源码学习

作者: _浅墨_ | 来源:发表于2021-07-15 19:07 被阅读0次

    SearchViewController.swift 源码:

    class SearchViewController: UIViewController {
      @IBOutlet var searchBar: UISearchBar!
      @IBOutlet var tableView: UITableView!
    
      var searchResults = [SearchResult]()
      var hasSearched = false
    
      // 可借鉴 1: struct 声明常量
      struct TableView {
        struct CellIdentifiers {
          static let searchResultCell = "SearchResultCell"
          static let nothingFoundCell = "NothingFoundCell"
        }
      }
    
      override func viewDidLoad() {
        super.viewDidLoad()
        tableView.contentInset = UIEdgeInsets(top: 50, left: 0, bottom: 0, right: 0)
        var cellNib = UINib(nibName: TableView.CellIdentifiers.searchResultCell, bundle: nil)
        tableView.register(cellNib, forCellReuseIdentifier: TableView.CellIdentifiers.searchResultCell)
        cellNib = UINib(nibName: TableView.CellIdentifiers.nothingFoundCell, bundle: nil)
        tableView.register(cellNib, forCellReuseIdentifier: TableView.CellIdentifiers.nothingFoundCell)
        searchBar.becomeFirstResponder()
      }
    
      // MARK: - Helper Methods
      func iTunesURL(searchText: String) -> URL {
        
        // 可借鉴 2:url 字符串 Encoding 处理,避免特殊字符出现问题
        let encodedText = searchText.addingPercentEncoding(withAllowedCharacters: CharacterSet.urlQueryAllowed)!
        let urlString = String(format: "https://itunes.apple.com/search?term=%@", encodedText)
        let url = URL(string: urlString)
        return url!
      }
    
      func performStoreRequest(with url: URL) -> Data? {
        do {
          return try Data(contentsOf: url)
        } catch {
          print("Download Error: \(error.localizedDescription)")
          showNetworkError()
          return nil
        }
      }
    
      func parse(data: Data) -> [SearchResult] {
        do {
          let decoder = JSONDecoder()
          let result = try decoder.decode(ResultArray.self, from: data)
          return result.results
        } catch {
          print("JSON Error: \(error)")
          return []
        }
      }
    
      func showNetworkError() {
        let alert = UIAlertController(title: "Whoops...", message: "There was an error accessing the iTunes Store. Please try again.", preferredStyle: .alert)
    
        let action = UIAlertAction(title: "OK", style: .default, handler: nil)
        alert.addAction(action)
        present(alert, animated: true, completion: nil)
      }
    }
    
    // MARK: - Search Bar Delegate
    extension SearchViewController: UISearchBarDelegate {
      func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
        if !searchBar.text!.isEmpty {
          searchBar.resignFirstResponder()
    
          hasSearched = true
          searchResults = []
    
          let url = iTunesURL(searchText: searchBar.text!)
          print("URL: '\(url)'")
    
          // 可借鉴 3:数组排序
          if let data = performStoreRequest(with: url) {
            searchResults = parse(data: data)
            searchResults.sort { $0 < $1 }
          }
    
          tableView.reloadData()
        }
      }
    
      func position(for bar: UIBarPositioning) -> UIBarPosition {
        .topAttached
      }
    }
    
    // MARK: - Table View Delegate
    extension SearchViewController: UITableViewDelegate, UITableViewDataSource {
      func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        if !hasSearched {
          return 0
        } else if searchResults.count == 0 {
          return 1
        } else {
          return searchResults.count
        }
      }
    
      func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        if searchResults.count == 0 {
          return tableView.dequeueReusableCell(withIdentifier: TableView.CellIdentifiers.nothingFoundCell, for: indexPath)
        } else {
          let cell = tableView.dequeueReusableCell(withIdentifier: TableView.CellIdentifiers.searchResultCell, for: indexPath) as! SearchResultCell
          let searchResult = searchResults[indexPath.row]
          cell.nameLabel.text = searchResult.name
          if searchResult.artist.isEmpty {
            cell.artistNameLabel.text = "Unknown"
          } else {
            cell.artistNameLabel.text = String(format: "%@ (%@)", searchResult.artist, searchResult.type)
          }
          return cell
        }
      }
    
      func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        tableView.deselectRow(at: indexPath, animated: true)
      }
    
      func tableView(_ tableView: UITableView, willSelectRowAt indexPath: IndexPath) -> IndexPath? {
        if searchResults.count == 0 {
          return nil
        } else {
          return indexPath
        }
      }
    }
    

    SearchResult 参数类:

    class ResultArray: Codable {
      var resultCount = 0
      var results = [SearchResult]()
    }
    
    class SearchResult: Codable, CustomStringConvertible {
      var artistName: String? = ""
      var trackName: String? = ""
      var kind: String? = ""
      var trackPrice: Double? = 0.0
      var currency = ""
      var imageSmall = ""
      var imageLarge = ""
      var trackViewUrl: String?
      var collectionName: String?
      var collectionViewUrl: String?
      var collectionPrice: Double?
      var itemPrice: Double?
      var itemGenre: String?
      var bookGenre: [String]?
    
      enum CodingKeys: String, CodingKey {
        case imageSmall = "artworkUrl60"
        case imageLarge = "artworkUrl100"
        case itemGenre = "primaryGenreName"
        case bookGenre = "genres"
        case itemPrice = "price"
        case kind, artistName, currency
        case trackName, trackPrice, trackViewUrl
        case collectionName, collectionViewUrl, collectionPrice
      }
    
      var name: String {
        return trackName ?? collectionName ?? ""
      }
    
      var storeURL: String {
        return trackViewUrl ?? collectionViewUrl ?? ""
      }
    
      var price: Double {
        return trackPrice ?? collectionPrice ?? itemPrice ?? 0.0
      }
    
      var genre: String {
        if let genre = itemGenre {
          return genre
        } else if let genres = bookGenre {
          return genres.joined(separator: ", ")
        }
        return ""
      }
    
      var type: String {
        let kind = self.kind ?? "audiobook"
        switch kind {
        case "album": return "Album"
        case "audiobook": return "Audio Book"
        case "book": return "Book"
        case "ebook": return "E-Book"
        case "feature-movie": return "Movie"
        case "music-video": return "Music Video"
        case "podcast": return "Podcast"
        case "software": return "App"
        case "song": return "Song"
        case "tv-episode": return "TV Episode"
        default: break
        }
        return "Unknown"
      }
    
      var artist: String {
        return artistName ?? ""
      }
    
      var description: String {
        return "\nResult - Kind: \(kind ?? "None"), Name: \(name), Artist Name: \(artistName ?? "None")"
      }
    }
    
    func < (lhs: SearchResult, rhs: SearchResult) -> Bool {
      return lhs.name.localizedStandardCompare(rhs.name) == .orderedAscending
    }
    

    以后写代码,可借鉴的地方,见注释。

    相关文章

      网友评论

          本文标题:SearchViewController 源码学习

          本文链接:https://www.haomeiwen.com/subject/wbsmpltx.html