#40 串接 TIH API - present, shouldPerformSegue, AlertController & prepare

reviews, website & map

本篇為實作 present 呈現網頁,加入 shouldPerformSegue 決定跳出 AlertController 或執行 prepare 顯示 reviews 及 map

繼前兩篇介紹了 TIH 及其 API 及部分APP應用的實作

接下來將介紹其餘在APP上的實作部分:

顯示 reviews

利用 shouldPerformSegue 分辨不同 segue

present 跳出 AlertController

顯示自訂 Date 格式

顯示飯店 map

prepare 傳遞資料

present 呈現 website

顯示 reviews、利用 shouldPerformSegue 分辨不同 segue & present 跳出 AlertController

看到 JSON 裡有旅客對飯店的 reviews (評論),這裡也是用 function prepare 去傳遞資料,但若是有 function shouldPerformSegue 則在執行 prepare 前會先執行 shouldPerformSegue。

仔細觀察,發現有時 reviews 也會是空的!!

Ex: Array of reviews is empty

若是在同一頁面會傳出資料到不同頁面,注意 shouldPerformSegue 的參數 identifier,就得設定 segue 的 identifier,才可分辨是專指哪一條 segue

setting identifier of segue

若 reviews 的 array 是空的則會跳出 AlertController 提醒 user;

    //決定segue要不要執行
override func shouldPerformSegue(withIdentifier identifier: String, sender: Any?) -> Bool {

if identifier == "reviewSegue", hotel?.reviews.isEmpty == true {
let alert = UIAlertController(title: "NO REVIEW YET !", message: "To return, click on Close ", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "Close", style: .default))
present(alert, animated: true)
return false
}
return true
}

反之,則顯示 reviews 在 ReviewTableViewController

    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
return reviews.count
}

func configuration(_ cell: ReviewTableViewCell) {
cell.authorNameLabel.frame = CGRect(x: 10, y: 30, width: 150, height: 25)
cell.authorNameLabel.adjustsFontSizeToFitWidth = true

cell.ratingLabel.font = .systemFont(ofSize: 12, weight: .semibold)

cell.textTextView.backgroundColor = .systemGray6
cell.textTextView.textColor = .black
cell.textTextView.layer.cornerRadius = 10

cell.timeLabel.frame = CGRect(x: 30, y: 120, width: 130, height: 25)
cell.timeLabel.font = .systemFont(ofSize: 13, weight: .regular)
cell.timeLabel.textAlignment = .right
}

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "\(ReviewTableViewCell.self)", for: indexPath) as! ReviewTableViewCell

configuration(cell)

let review = reviews[indexPath.row]

cell.authorNameLabel.text = review.authorName
cell.ratingLabel.text = review.rating.description
cell.textTextView.text = review.text

//顯示時間
let newDateFormatter = DateFormatter()
newDateFormatter.dateFormat = "MMMM d, yyyy"
cell.timeLabel.text = newDateFormatter.string(from: review.time)

for star in cell.starImageViews {
star.image = nil
star.image = UIImage(systemName: "star")
star.tintColor = .systemGray5
}

let rating = review.rating
let starRagne = Int(rating)-1

if rating != 0 {
for star in 0...starRagne {
cell.starImageViews[star].tintColor = .systemYellow
cell.starImageViews[star].image = UIImage(systemName: "star.fill")

if rating - Double(Int(rating)) > 0 {
cell.starImageViews[Int(rating)].tintColor = .systemYellow
cell.starImageViews[Int(rating)].image = UIImage(systemName: "star.leadinghalf.filled")
}
}
}

return cell
}
demonstration of reviews
Left: Array of reviews is empty, Right: show reviews

AlertController 可參考

顯示自訂 Date 格式

在上一篇解釋如何讓程式看懂 JSON 的 Date 格式,現在要呈現出我們自己想要的時間格式。

同樣的,需要 DateFormatter() 幫忙

//顯示時間
let newFormatter = DateFormatter()
newFormatter.dateFormat = "MMMM d, yyyy"
cell.timeLabel.text = newFormatter.string(from: review.time)

一樣是用 DateFormatter() 的參數 dateFormat 去設定想要的 Date 格式。

特別注意:要顯示時間的來源,則是用 DateFormatter() 的參數 string(from: ) 去設定其來源。

結果可參考上圖右。

顯示飯店 map、prepare 傳遞資料

除了顯示 reviews 用到 function prepare ,顯示飯店 map 同樣也用到 function prepare !

所以它們的 source (起始頁面) 一樣,destination (終點頁面) 不一樣,
用 segue 的 destination 型別分辨要傳什麼資料

    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {

if let destoination = segue.destination as? ReviewTableViewController {

destoination.reviews = hotel!.reviews

} else if let destination = segue.destination as? MapViewController {

destination.hotel = hotel
}
}

在 destination 頁面自訂型別並遵從 UIViewController,新增 Map Kit View 及拉好 IBOutlet,import MapKit 及宣告好要承接資料的屬性 var hotel: Hotel?

JSON 裡也有給座標,同樣解析後就可以使用了

要顯示地圖需要用到:
CLLocationCoordinate2D(latitude:, longitude:) 設定座標,
在把座標丟進 MKCoordinateRegion 設為 center 及設好範圍,
之後用 Map Kit View 的 Method setRegion() 把設好的 center 及範圍丟進去

最後,設定地圖上的註解,利用 MKPointAnnotation()

        hotelMapView.frame = CGRect(x: 0, y: 0, width: 393, height: 700)

if let latitude = hotel?.location.latitude,
let longitude = hotel?.location.longitude {
//defining the coordinate via latitude and longitude
let location = CLLocationCoordinate2D(latitude: latitude, longitude: longitude)
//defining the center and range(meter) of the map
let mapRange = MKCoordinateRegion(center: location, latitudinalMeters: 1000, longitudinalMeters:520)
//show the map
hotelMapView.setRegion(mapRange, animated: true)
let annotation = MKPointAnnotation()
//defining the coordinate of annotation
annotation.coordinate = location
annotation.title = hotel?.name
annotation.subtitle = "Hotel"
//show the annotation on the map
hotelMapView.addAnnotation(annotation)
hotelMapView.showsScale = true
}
demonstration of map
show map

present 呈現 website

發現 JSON 給的飯店網址有的不是很完整,少了 https:// ,以至於無法顯示,只能自己幫它補齊了

    @IBAction func pressWebsiteButton(_ sender: Any) {

if hotel?.officialWebsite.contains("https://") == false {
let urlString = "https://\(hotel!.officialWebsite)"
print(urlString)
if let url = URL(string: urlString) {
let browser = SFSafariViewController(url: url)
present(browser, animated: true)
}
} else {
if let url = URL(string: hotel!.officialWebsite) {
let browser = SFSafariViewController(url: url)
present(browser, animated: true)
}
}
}
demonstration of website
show website

--

--