連載
» 2015年06月09日 05時00分 公開

iPhone 6/6 Plusアプリ開発入門(終):iOS 8アプリ開発のこれまで&Swift 1.2への対応、プロパティオブザーバーとwillSetの基本的な使い方、セグエを使った画面遷移 (2/6)

[平屋真吾,クラスメソッド株式会社]

Swift 1.2への対応

 Xcode 6.0の時点ではバージョン1.0だったSwiftですが、Xcode 6.3のリリースと共にバージョン 1.2になりました。Swift 1.2の改善点や修正点は以下のSwiftの公式ブログで説明されています。

 「PhotoMap」アプリの実装に関係するSwift 1.2の変更は以下の2点です。この変更に関わる部分を修正しましょう。

  • ダウンキャストの仕様変更
  • let定数の仕様変更

ダウンキャストの仕様変更

 プロジェクトファイル(PhotoMap.xcodeprojファイル)を開きビルド([command]+[B]キー)を行ってください。図2のように、PhotoAnnotationViewクラスのコードが原因でビルドが失敗すると思います。

図2 「ダウンキャスト」部分修正前のビルド結果

 エラーメッセージは以下の通りです。

'AnyObject' is not convertible to 'UIView'; did you mean to use 'as!' to force downcast?

 「AnyObjectはUIViewに変換できない」という内容のエラーメッセージです。

override init(annotation: MKAnnotation!, reuseIdentifier: String!) {
    super.init(annotation: annotation, reuseIdentifier: reuseIdentifier)
    
    self.frame = CGRect(origin: self.frame.origin, size: PhotoAnnotationView.size)
    
    self.canShowCallout = true
    self.rightCalloutAccessoryView = UIButton.buttonWithType(.DetailDisclosure) as UIView 	// エラー箇所
    
    self.thumbnailImageView = UIImageView(frame: CGRect(origin: CGPointZero, size: PhotoAnnotationView.size))
    self.thumbnailImageView.contentMode = .ScaleAspectFill
    self.thumbnailImageView.clipsToBounds = true
    self.addSubview(self.thumbnailImageView)
}
修正前のPhotoAnnotationViewクラスのinitメソッド(引数:annotation、reuseIdentifier)

 PhotoAnnotationViewクラスのinitメソッド(引数:annotation、reuseIdentifier)で使用している「UIButton」の「buttonWithType」メソッド(7行目)の返却値は「AnyObject」型です。

 「AnyObject」型は任意のクラスのインスタンスを表す型であり、Objective-Cのクラスのメソッドの返却値の型などで使われています。「UIButton」といった具体的な型を与えて変換を行うことにより、変換後の型のオブジェクトとして動作させることができます。

 これまでは「as」演算子を使用してダウンキャスト(抽象度の高い型から具体的な型への型変換)を行っていましたが、Swift 1.2以降では「as!」演算子を使用してダウンキャストを行う必要があります。以下のように「as!」演算子を使用した記述(7行目)に変更しましょう。

override init(annotation: MKAnnotation!, reuseIdentifier: String!) {
    super.init(annotation: annotation, reuseIdentifier: reuseIdentifier)
    
    self.frame = CGRect(origin: self.frame.origin, size: PhotoAnnotationView.size)
    
    self.canShowCallout = true
    self.rightCalloutAccessoryView = UIButton.buttonWithType(.DetailDisclosure) as! UIView 	// 修正箇所
    
    self.thumbnailImageView = UIImageView(frame: CGRect(origin: CGPointZero, size: PhotoAnnotationView.size))
    self.thumbnailImageView.contentMode = .ScaleAspectFill
    self.thumbnailImageView.clipsToBounds = true
    self.addSubview(self.thumbnailImageView)
}
修正後のPhotoAnnotationViewクラスのinitメソッド(引数:annotation、reuseIdentifier)

 修正後、再びビルドを行うとダウンキャストを行っている行のエラーは消えると思います。別のエラーが表示されますが、これは次のセクションで修正します。

let定数の仕様変更

 「ダウンキャスト」部分を変更しても、ビルドは失敗してしまい、エラーの指摘がさらに増えるかと思います(図3)。

図3 「ダウンキャスト」部分修正後のビルド結果

 指摘部分を修正してしまいましょう。表示されているエラーメッセージは以下の2種類です。

Immutable value 'self.thumbnailImageView' may only be initialized once
Property 'self.thumbnailImageView' not initialized at super.init call

 「thumbnailImageViewは複数回初期化できない」「thumbnailImageViewはスーパークラスのイニシャライザーを呼ぶ前に初期化されていない」といった内容の指摘です。

private let thumbnailImageView: UIImageView!	// オプショナル型なので初期値nilがセットされる
 
override init(annotation: MKAnnotation!, reuseIdentifier: String!) {
    super.init(annotation: annotation, reuseIdentifier: reuseIdentifier)
    
    self.frame = CGRect(origin: self.frame.origin, size: PhotoAnnotationView.size)
    
    self.canShowCallout = true
    self.rightCalloutAccessoryView = UIButton.buttonWithType(.DetailDisclosure) as! UIView
    
    self.thumbnailImageView = UIImageView(frame: CGRect(origin: CGPointZero, size: PhotoAnnotationView.size)) // 定数への代入
    self.thumbnailImageView.contentMode = .ScaleAspectFill
    self.thumbnailImageView.clipsToBounds = true
    self.addSubview(self.thumbnailImageView)
}
thumbnailImageViewの初期化処理を修正する前のinitメソッドとthumbnailImageViewプロパティの定義

 PhotoAnnotationViewクラスのinitメソッドの例では、以下のようにthumbnailImageView定数に2回値を設定していました。

  • 定数の宣言時の初期値設定(オプショナル型なので初期値nilがセットされる)(1行目)
  • スーパークラスのイニシャライザー呼び出し後の値の代入(11行目)

 このように、定数へ値を与えるタイミングは2回存在していましたが、Swift 1.2からは1回に制限されます。

 thumbnailImageViewプロパティはUIImageView!型のままにしておきたいので、定数定義時に初期値を設定するようにして、イニシャライザー内での値の代入は削除します。

 修正後のコードは以下の通りです。

private let thumbnailImageView: UIImageView! = UIImageView(frame: CGRect(origin: CGPointZero, size: PhotoAnnotationView.size))
    
override init(annotation: MKAnnotation!, reuseIdentifier: String!) {
    super.init(annotation: annotation, reuseIdentifier: reuseIdentifier)
    
    self.frame = CGRect(origin: self.frame.origin, size: PhotoAnnotationView.size)
    
    self.canShowCallout = true
    self.rightCalloutAccessoryView = UIButton.buttonWithType(.DetailDisclosure) as! UIView
    
    self.thumbnailImageView.contentMode = .ScaleAspectFill
    self.thumbnailImageView.clipsToBounds = true
    self.addSubview(self.thumbnailImageView)
}
thumbnailImageViewの初期化処理を修正した後のinitメソッドとthumbnailImageViewプロパティの定義

 ここまでの修正を終えると、エラー表示は消えると思います。次ページからは、「詳細画面」を実装していきましょう。

Copyright © ITmedia, Inc. All Rights Reserved.

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。