Swiftで遊ぼう! on Hatena

あしたさぬきblogでやってた初心者オヤジのiOSプログラミング奮闘記がHatenaに来ました

Swiftで遊ぼう! - 423 - Swiftでデザインパターン

2016年12月2日:Swift 3向けに改訂(加筆)*1

ベクトル計算や三角関数の復習をしてて、ふと気がつきました。私が作りたいアプリはアニメーション関連?

いえいえ、ゲームを作るためにiOSアプリの勉強を始めた訳ではありません。

医療アプリを作ろうと思っているわけで、テーブルビューを中心とした動きの少ないアプリケーションになる予定です。

アップルのチュートリアルFoodTracker
はいい勉強になりました。

Beginning iPhone Development with Swift 3: Exploring the iOS SDK

Beginning iPhone Development with Swift 3: Exploring the iOS SDK

  • 作者: Molly K. Maskrey,Kim Topley,David Mark,Fredrik Olsson,JEFF LAMARCHE
  • 出版社/メーカー: Apress
  • 発売日: 2016/11/18
  • メディア: ペーパーバック
  • この商品を含むブログを見る
上記のチュートリアルも中断しています(^^;)

yataiblue.hatenablog.com
スタンフォード大学ポール先生の講座も頓挫中...*2

しかし、これらに戻らず、以前から気になっていたチュートリアルに取り組むことにします。

デザインパターン

昨年の9月にオブジェクト指向でなぜつくるのか 第2版*3を読んでデザインパターンの重要性を知りました。しかし、この頃はSwift言語を使ったデザインパターンの説明は非常に少なかったんです。

久しぶりに調べて見ると、2014年9月に2つしかなかった説明も11に増えていました。

dev.classmethod.jp

実は「RAYWENDERLICHのチュートリアスサイト」にもデザインパターンの解説があったんです!

ということで、Swift 3を使ってデザインパターンの勉強をします。

www.raywenderlich.com

カスタムTableViewCellを使ったTableViewアプリです。

  • Creational: Singleton
  • Structural: MVC, Decorator, Adapter, Facade
  • Behavioral: Observer, and, Memento

このチュートリアルにも書かれていますが、iOSフレームワークを利用するということで、いくつかのデザインパターンは自然に習得できます。MVCモデル、デリゲーション、そしてプロトコールもすべてデザインパターンです。

これからデザインパターンを意識してプロジェクトに取りかかります。

まずプロジェクトを作成します。

ん...

実は、オリジナルサイトにはテンプレートが用意されていました。。面倒くさい人は、Starter Projectをダウンロードして、Xcodeで開くと、Swift 3向けに変更して使うことができます。スターター・プロジェクトは以下のようになっています。

f:id:yataiblue:20161128232803j:plain

私は、復習になるので最初っから自分で作ってみようと思います。

Xcode7.1で新規プロジェクトを作ります。「Single View Application」を選んで、プロジェクト名を「BlueLibrary」にします。チュートリアルでダウンロードして使える「BlueLibrary.Swift」と比較しながら進めます。

まず直ぐ気づくのがNavigation Controllerが無いことです。以前にも説明しましたが、既存のView ControllerにあるScene Dockを選んだ状態で、メニューから「Editor」->「Embed In」->「Navigation Controller」を選びます。

f:id:yataiblue:20161128233854j:plain

これで自動的にView Controllerの上部にNavigation Barが設置されます。タイトルが空白なのでダブルクリックして「Pop Music」と入力します。

f:id:yataiblue:20161129171850j:plain

View Controllerにオブジェクトを設置

どういうオブジェクトを設置すべきか、スターター・プロジェクトXcodeで立ち上げて、ドキュメント・アウトライン*4を参考にして並べていきます。

スーパービュー(デフォルト)のView上に、Toolbar、もう一つ別のView、そしてData Tableが並んでいます。

f:id:yataiblue:20161129173453j:plain

同じようにオブジェクトを並べるため、次のようにストーリーボード上に3つのオブジェクトを設置します。

f:id:yataiblue:20161129195026j:plain

  • オブジェクト・ライブラリからViewをドラッグ&ドロップします。Navigation Barの直下に高さ「120」で固定して、左右と上部のコンストレイントを設定します。

f:id:yataiblue:20161130095911j:plain

  • オブジェクト・ライブラリからTable Viewをドラッグ&ドロップします。Viewの下部に画面一杯広げてコンストレイントを設定します*5

f:id:yataiblue:20161130095927p:plain

次は、Main.storyboardに設置をした2つのオブジェクト、「Tool Bar」と「Table View」をViewControllerに「Ctr + ドラッグ」して@IBOutletインスタンスを作ります。それぞれ名前を「toolbar」と「dataTable」にします。

f:id:yataiblue:20151203211703j:plain

次にTable ViewにCellを設置していきますが、勘違いしていたことがありました。オブジェクト・ライブラリからTable View Cellをドラッグ&ドロップして使用する方法でプログラミングを進めようとしたのですが、チュートリアルではデフォルトで用意されているCellを使用していました*6

Data Table*7を選択してアトリビュート・インスペクタを選択して、Prototyle Cellsを「1」にして、Styledを「Grouped」にします。

f:id:yataiblue:20161130104620j:plain

Cellの設置で重要なのは、Identifierを決定することです。これを使ってCellをインスタンス化するため、ストーリーボード上のTable View Cellを選択して、アトリビュート・インスペクタを開きます。「Identifier」を「Cell」にして、忘れずに「Style」を「Right Detail」に変更します。

するとTable View Cellに「Title」と「Detail」が表示されます。

f:id:yataiblue:20161202194350j:plain

Albumクラスの準備

次に音楽データを扱うためのクラスを用意していきます。

Xcodeのメニューから「File > New > File...」を選び、「iOS > Source > Cocoa Touch Class」を選択してから「Next」ボタンを押します。

ファイル名を「Album」にして、subclassは「NSObject」を選択してから「Create」を押します。

できあがったファイルにプロパティを持たせるのですが、まず「// MARK: Properties」を入れてから次のコードを加えます。

import UIKit

class Album: NSObject {
    
    // MARK: Properties
    var title: String!
    var artist: String!
    var genre: String!
    var coverUrl: String!
    var year: String!

}

そしてプロパティ宣言をしたら、必ずイニシャライザ(初期化)ステップが必要になります*8。イニシャライザをコーディングします。

    // MARK: Initializers
    init(title: String, artist: String,
         genre: String, coverUrl: String, year: String) {
        super.init()
        self.title = title
        self.artist = artist
        self.genre = genre
        self.coverUrl = coverUrl
        self.year = year
    }

これは指定イニシャライザで、最低限のクラス実装ができました。次はNSObjectクラスで用意されているメソッドを拡張(override)します。メソッドと言っても計算型プロパティ(Computed Property)です。

    // MARK: Override Methods
    override var description: String {
        return "title: \(title)" + "artist: \(artist)" + 
               "genre: \(genre)" + "coverUrl: \(coverUrl)" + 
               "year: \(year)"
    }

これでアルバムデータを保持するAlbumクラスができました。

AlbumViewクラスの準備

次はアルバムのデザインワークを保持するクラスを用意する必要があります。

Xcodeのメニューから「File -> New -> File...」を選び、「iOS -> Source -> Cocoa Touch Class」を選択してから「Next」ボタンを選びます。

ファイル名を「AlbumView」にして、subclassは「UIView」を選択してから「Create」を押してファイルを作ります。

保持すべきプロペティを書きます。

import UIKit

class AlbumView: UIView {

    // MARK: Properties
    private var coverImage: UIImageView!
    private var indicator: UIActivityIndicatorView!

}

「private」が付いていることが重要です。このプロパティはこのクラスの外からアクセスできないようにして内部での保守性を保っています*9。他の人のためにライブラリやフレームワークを用意する時に重要な作法です。できるだけ「private」にして保守性を上げておく必要があります。

次はイニシャライザですが、必ずイニシャライザの復習をする必要があります。UIViewのイニシャライザに関する説明 -> Swiftで遊ぼう! - 248 - UIViewの初期化ステップ - Swiftで遊ぼう! on Hatena

このUIViewクラスは、NSCoding(プロトコール)に準拠させていく予定なので、必ず書かなければいけないメソッド(イニシャライザ)があります<*10

「required」が付くのはプロトコール準拠をさせるからです。オリジナルの記事と記述の異なる点があります。実は「convenience」を付ける必要があります。なぜならこのイニシャライザ単独で初期化ができないからです。単独で初期化できる指定イニシャライザ(designated initializer)を呼ぶ必要があるので、「self.init()」を指定します。

    required convenience init(coder aDecoder: NSCoder){
        self.init(coder: aDecoder)
        // 次のメソッドは後で記述するためこの段階ではエラー
        commonInit()
    }

commonInit()」はヘルパーメソッドで、初期化のための共通ステップを後で記述します。まず指定イニシャライザを用意する必要があるので次のコードを加えます。

    init(frame: CGRect, albumCover: String) {
        super.init(frame: frame)
        commonInit()
    }

指定イニシャライザは必ず継承しているスーパークラスの指定イニシャライザを呼ばないといけないので、「super.init()」を呼びます。そして初期化ステップ共通のcommonInit()に続きます。

    func commonInit() {
        backgroundColor = UIColor.black
        coverImage = UIImageView(frame: 
                                CGRect(x: 5, 
                                       y: 5, 
                                   width: frame.size.width - 10, 
                                  height: frame.size.height - 10))
        addSubview(coverImage)
        indicator = UIActivityIndicatorView()
        indicator.center = center
        indicator.activityIndicatorViewStyle = .whiteLarge
        indicator.startAnimating()
        addSubview(indicator)
    }

このステップで、このクラスが独自に持っているプロパティすべての初期化がされます。

アルバムのバックグラウンドカラーをハイライトで白くなり、外れると黒くするためにメソッドを用意します。

    func highlightAlbum(didHighlightView: Bool) {
        if didHighlightView == true {
            backgroundColor = UIColor.white
        } else {
            backgroundColor = UIColor.black
        }
    }

これでAlbumViewというクラスの初期化がされます。ちゃんとできているかどうか確認するのはラン(Cmd + R)... いえ、まだプロジェクトして繋がっていないのでこれで確かめることができません。単独でこのクラスだけビルドできるかどうか確認します。ビルド(Cmd + B)をします。

これでプロトタイプの設計が 終了です。

yataiblue.hatenablog.com
yataiblue.hatenablog.com
yataiblue.hatenablog.com
yataiblue.hatenablog.com

*1:2016年12月1日:Swift 3向けに改訂, 2016年11月28年:Swift 3対応に向けて書き直し中、2015年12月3日改訂:中途半端に終了しているチュートリアルをSwift2に準じて完成させます。

*2:元々はiOS 8向けの講座でした->Swiftで遊ぼう! - 211 - Developing iOS 8 Apps with Swift - Logistics, iOS 8 Overview - Swiftで遊ぼう! on Hatena

*3:プログラミング初心者にはマストリードの非常に重要な本です。最近プログラミングを始めた人は必ず読んでおきましょう!

*4:Xcodeのショートカットに説明があります。

*5:チュートリアルのスターター・プロジェクトで名前が「Data Table」になっていますが、後に「Ctrl + ドラッグ」してViewControllerに繋げて@IBoutletインスタンス生成時に名前を「dataTable」にすると自動的に変わります。

*6:iOSにはXcodeを使ったビジュアルな実装法とコードを使った実装法が混在しているので違いを理解する必要があります。

*7:@IBOutletを繋ぐ時に名前を「dataTable」に設定した時に自動的にドキュメント・アウトラインでData Tableに変化しています。

*8:イニシャライザを理解しないとクラスは書けません。ちょっと理解に苦しむかもしれませんが、きちんと理解しないとプログラミングはできません。フレームワークを利用するためにイニシャライザの使用を避けるメソッドも用意されているから、初心者は混乱するんだと思います。次のページでイニシャライザの説明をしているので確認して下さい -> Swiftで遊ぼう! - 407 - Initializer イニシャライザ - Swiftで遊ぼう! on Hatena

*9:Swift 3からアクセスコントロールも拡張されています。まだこのブログで取りあげていないので、勉強したら説明を加えます。

*10:まあ私も完全に理解しているとは言いがたいのですが、こういうもんだとというノリで覚えるしかありません。次のリンクを読んで下さい:Swiftで遊ぼう! - 409 - Persist Data 4 - Swiftで遊ぼう! on Hatena