はじめに
本日は何気に今まで使ってこなかったAlamofireImageについてメモ書きです。
キャッシュコントロールやら同期/非同期での画像取得など考えなくても良いというのはパワー的にかなり楽になりますね。
ということで早速見ていきましょう。
API経由で取得した画像URLを使ってUIImageViewに画像を表示する
テストとして利用するAPI
今回はテスト用APIとしてホットペッパーのグルメサーチAPIを利用しました。
ホットペッパーAPIを利用するには新規登録してAPI Key
をゲットする必要があります。
また、前提として取得したAPI Key
はkey.plist
に書いているとします。
上記準備をした上で下記クラスを作成しました。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
| // HotpepperAPI.swift
import Foundation
import CoreLocation
import Alamofire
import SwiftyJSON
/**
ホットペッパーAPI
*/
class HotpepperAPI {
/// API Key
private var apiKey: String = String()
/// ホットペッパーAPIのベースURL
private let baseURL: String = "https://webservice.recruit.co.jp/hotpepper/gourmet/v1/"
/// 初期化処理
init() {
if let path = Bundle.main.path(forResource: "key", ofType: "plist") {
if let dic = NSDictionary(contentsOfFile: path) as? [String: Any] {
if let apiKey = dic["hotpepperApiKey"] as? String {
self.apiKey = apiKey
}
}
}
}
/**
ホットペッパーグルメサーチAPI
- parameter coordinate: 位置
- parameter completion: レストラン情報を返却するcallback
*/
func searchRestaurant(coordinate: CLLocationCoordinate2D, completion: @escaping ((JSON) -> Void)) {
let parameters = ["key": self.apiKey, "format": "json", "lat": coordinate.latitude, "lng": coordinate.longitude, "range": 2] as [String : Any]
Alamofire.request(baseURL, method: .get, parameters: parameters, encoding: URLEncoding.default, headers: nil).responseJSON { response in
let json = JSON(response.result.value as Any)
let result = json["results"]["shop"]
completion(result)
}
}
}
|
テストとして用意するUIImageView
今回はテストとして マップにプロットしたマーカをタップしたときに表示するInfoWindow内にUIImageViewを用意する ようにしました。
【準備事項】
– Google Maps SDK for iOSをマップとして利用
– マーカタップ時に表示されるInfoWindowをカスタム化
表示するViewとしては下図のようになります。
AlamofireImageの用意
では肝心なAlamofireImage
の導入を見てみましょう。
CocoaPods
で簡単に導入が可能です。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| # Podfile
use_frameworks!
platform :ios, '10.0'
target "SampleApp" do
# Normal libraries
...
pod 'AlamofireImage', '~> 3.1'
end
post_install do |installer|
installer.pods_project.targets.each do |target|
target.build_configurations.each do |config|
config.build_settings['SWIFT_VERSION'] = '3.0'
end
end
end
|
マーカタップ時に表示するInfoWindowに画像を表示する
まずはInfoWindow
をカスタム化したクラスであるMarkerInfoContentsView.swift
のソースコードを書きます。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
| // MarkerInfoContentsView.swift
import Foundation
import UIKit
import AlamofireImage
class MarkerInfoContentsView: UIView {
@IBOutlet weak var shopName: UILabel!
@IBOutlet weak var categoryName: UILabel!
@IBOutlet weak var shopImage: UIImageView!
override init(frame: CGRect) {
super.init(frame: frame)
self.xibViewSet()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)!
self.xibViewSet()
}
internal func xibViewSet() {
if let view = Bundle.main.loadNibNamed("MarkerInfoContentsView", owner: self, options: nil)?.first as? UIView {
view.frame = self.bounds
self.addSubview(view)
}
}
/**
データの設定処理
- parameter shopName: 店舗名
- parameter categoryName: カテゴリ名
- parameter shopImageURLString: 画像URL
*/
func setData(shopName: String?, categoryName: String?, shopImageURLString: String?) {
// 店舗名の設定
if let shopNameTextCount = shopName?.characters.count, shopNameTextCount > 0 {
self.shopName.text = shopName
} else {
self.shopName.text = "店舗名不明"
self.shopName.textColor = UIColor.gray
}
// 詳細説明の設定
if let categoryNameTextCount = categoryName?.characters.count, categoryNameTextCount > 0 {
self.categoryName.text = categoryName
} else {
self.categoryName.text = "カテゴリ不明"
self.categoryName.textColor = UIColor.gray
}
// 画像の設定
if let shopImageURLStringTextCount = shopImageURLString?.characters.count, shopImageURLStringTextCount > 0 {
if let shopImageURL = URL(string: shopImageURLString!) {
self.shopImage.af_setImage(withURL: shopImageURL, placeholderImage: UIImage(named: "NoImageIcon"))
} else {
self.shopImage.image = UIImage(named: "NoImageIcon")
}
} else {
self.shopImage.image = UIImage(named: "NoImageIcon")
}
}
}
|
実際にAlamofireImage
を利用して画像URLから取得した画像データを格納している箇所は、
1
| self.shopImage.af_setImage(withURL: shopImageURL, placeholderImage: UIImage(named: "NoImageIcon"))
|
になります。
続いて、ViewController.swift
でのGMSMapViewDelegate
部分の処理を抜粋して書きます。
1
2
3
4
5
6
7
8
9
10
11
12
| // ViewController.swift
extension ViewController: GMSMapViewDelegate {
func mapView(_ mapView: GMSMapView, markerInfoWindow marker: GMSMarker) -> UIView? {
guard let cMarker = marker as? CustomGMSMarker else {
return nil
}
cMarker.tracksInfoWindowChanges = true
let view = MarkerInfoContentsView(frame: CGRect(x: 0, y: 0, width: 250, height: 265))
view.setData(shopName: cMarker.shopName, categoryName: cMarker.categoryName, shopImageURLString: cMarker.imageURL)
return view
}
}
|
重要なのは、 cMarker.tracksInfoWindowChanges = true
です。
これを書かないと 画像URLから画像データを取得したタイミングでInfoWindow
の画像を更新するということができなくなります。
(placeholderImage
として用意した画像がずっと表示されてしまいます。)
筆者はここでドハマリして試行錯誤してしまいました。
非同期で画像データを取得しに行っているので、データ取得前にInfoWindow
の描画処理に進んでしまうということはわかるのですが、どうすれば想定した挙動が実現できるのか悩みました。
ですが、蓋を開けてみれば何ということもなかったんですよね。
まとめ
さて、如何でしたでしょうか?
画像取得/キャッシュ関連のOSSライブラリは多種多様なものが出ており、好き嫌いもあるかもしれませんが、筆者は通信ライブラリにAlamofire
を使うことが多いため、AlamofireImage
も嫌いではないんですよね。
と言ったところで本日はここまで。