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
}
以后写代码,可借鉴的地方,见注释。
网友评论