- https://www.omdbapi.com/是一个获取电影数据的API网站
- 获取的Json如下:
- 根据Json设计数据结构如下:
- Swift 4引入了
Codable
协议,与NSCoding
协议不同的是:如果自定义的类中全都是基本数据类型、基本对象类型,无需再实现编解码,只需要在自定义的类声明它遵守Codable
协议即可 - 原Json中的
Type
与Swift的关键字冲突了,使用CodingKeys
替换变量名
struct MovieResult: Codable {
let Search: [Movie]
}
struct Movie: Codable {
let Title: String
let Year: String
let imdbID: String
let _Type: String
let Poster: String
enum CodingKeys: String, CodingKey {
case Title, Year, imdbID, _Type = "Type", Poster
}
}
- 搜索框Text Field
@IBOutlet var field: UITextField!
//按下return调用
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
serachMovies()
return true
}
//具体实现见Github
func serachMovies() {
//数据预处理
//网络请求
//更新Model与View
}
- 显示搜索结果的Table View
@IBOutlet var table: UITableView!
var movies = [Movie]()
// Table View 数据源和代理方法,具体实现见Github
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return movies.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
//创建自定义的nib cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
//跳转到详细页面
}
- 设置数据源和代理
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource,UITextFieldDelegate {
override func viewDidLoad() {
super.viewDidLoad()
table.dataSource = self
table.delegate = self
field.delegate = self
}
}
class MovieTableViewCell: UITableViewCell {
@IBOutlet var movieTitleLabel: UILabel!
@IBOutlet var movieYearLabel: UILabel!
@IBOutlet var moviePosterImageView: UIImageView!
// configure the nib cell
func config(with model: Movie){
self.movieTitleLabel.text = model.Title
self.movieYearLabel.text = model.Year
let url = model.Poster
if let imgData = try? Data(contentsOf: URL(string: url)!){
self.moviePosterImageView.image = UIImage(data: imgData)
}
}
}
- 数据源方法如下
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
//根据nib注册cell
let nibCell = UINib(nibName: "MovieTableViewCell", bundle: nil)
table.register(nibCell, forCellReuseIdentifier: "MovieTableViewCell")
//复用cell
let cell = tableView.dequeueReusableCell(withIdentifier: "MovieTableViewCell", for: indexPath) as! MovieTableViewCell
//设置cell数据
cell.config(with: movies[indexPath.row])
return cell
}
- 将搜索内容转为URL可以识别的字符串
guard let searchText = field.text, !searchText.isEmpty else {
return
}
let encodedText = searchText.addingPercentEncoding(withAllowedCharacters: CharacterSet.urlQueryAllowed)!
let url = "https://www.omdbapi.com/?apikey=3aea79ac&s=\(encodedText)&type=movie"
- 对于基本请求可以使用
URLSession.shared
单例,简单的数据任务使用dataTask
方法 dataTask(with: URL, completionHandler: (Data?, URLResponse?, Error?) -> Void)
方法
- 获取数据成功时,数据保存在
Data
,Error
为nil - 获取失败时,
Error
不为nil - 不论是否获取成功,
URLResponse
不为nil,存着HTTP响应报文中的数据
- 获取data后将其转为结构体
JSONDecoder().decode()
可能失败throw
异常,因此放在do{} catch{}
中,并try
- 若转换失败,
result
就成了nil
,因此声明为可选型
guard let data = data, error == nil else{
return
}
//JsonData转为结构体
var result: MovieResult?
do {
result = try JSONDecoder().decode(MovieResult.self, from: data)
}
catch {
print("Json转换结构体失败\(error)")
}
guard let finalResult = result else {
return
}
- 数据获取成功后更新movies数组
- UI更新只在主线程更新,用GCD将UI更新的代码异步派发到主队列
//更新 movies 数组
let newMovies = finalResult.Search
self.movies.removeAll()
self.movies.append(contentsOf: newMovies)
//更新Table View,UI更新到主线程
DispatchQueue.main.async {
self.table.reloadData()
}
https://www.imdb.com/title/"IMDb_ID"/
可以显示根据IMDb ID显示电影的详细内容,这个ID就在请求到的Json数据中
import SafariServices
//Table View代理方法
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
//跳转到详细页面
let url = "https://www.imdb.com/title/\(movies[indexPath.row].imdbID)/"
let vc = SFSafariViewController(url: URL(string:url)!)
present(vc,animated: true)
}