読者です 読者をやめる 読者になる 読者になる

Swiftで遊ぼう! on Hatena

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

Swiftで遊ぼう! - 684 - Interface Builder, FaceView controller, Gestures, Multiple MVCs 4

また講義5でもたつき始めました。さっさとクリアしないと本ちゃんの講義についていけなくなりますね。

Developing iOS 9 Apps with Swift - Free Course by Stanford on iTunes U

ModelがStruct型で構築されたことで少し戸惑っているのですが、先に進みます。

この抽象的なModelと具体的な値しか扱わないViewを取り持つFaceViewControllerのコーディングに入ります。

このステップは非常に理解しやすいんです。

まずFaceialExpressionのインスタンスをプロパティとして持ちます。

var expression = 
    FacialExpression(eyes: .Open, eyeBrows: .Normal, mouth: .Smile) {
    didSet {
        upDateUI()
    }
}

まずStruct型のイニシャライザーは簡単です。それぞれのプロパティに値を渡せばいいわけです。そして値が変化した時にモデルを変更するためにプロパティ・オブザーバーを使います。「upDate()」メソッドはカスタムメソッドなので後でコーディングします。

次にViewをプロパティとして持たせます。これは簡単です。「Ctrl + ドラッグ」してお馴染みの@IBOutletを作ります。名前は「faceView」にします。

@IBOutlet weak var faceView: FaceView! {
    didSet {
        upDateUI()
    }
}

当然faceViewもアップデイトされる必要があるので、upDateUIメソッドをプロパティ・オブザーバーで呼びます。

さて、upDate()メソッドを考えますが、FacialExpressionで用意されているプロパティは3つだけでeyes(目)、eyeBrows(眉)とmouth(口)の状態をレベル別で保存します。

eyesの状態はexpressionで3段階「Open」「Closed」「Squinting(目を細める)」に区別されていますが、faceViewではBool型です。今のところSquintingの対応はできないので、Closedと同じにします。

ということは、upDateUI()メソッド内で「switch-case」で制御すればいいわけです。

private func upDateUI() {
    switch expression.eyes {
    case .Open: faceView.eyesOpen = true
    case .Closed: faceView.eyesOpen = false
    case .Squinting: faceView.eyesOpen = false // 取りあえず
     }
....

次は眉毛の傾きです。これをどのように扱うか? ViewではDouble型なんで無段階調整できます。しかし、FacialExpressionでは3段階、「Relaxed」「Normal」「Furrowed」しかないので、傾きをある程度限定します。この条件をDictionary型で提供するんです。

private var eyeBrowTilts = 
    [FacialExpression.EyeBrows.Furrowed: -0.5, 
                               .Relaxed: 0.5, 
                                .Normal: 0.0]

すると、upDateUI()メソッドが簡単になります。

private func upDateUI() {
    switch expression.eyes {
    case .Open: faceView.eyesOpen = true
    case .Closed: faceView.eyesOpen = false
    case .Squinting: faceView.eyesOpen = false // 取りあえず
     }
    faceView.eyeBrowTilt = 
        eyeBrowTilts[expression.eyeBrows] ?? 0.0
....

Dictionary型のアイテムはオプショナル扱いなのでアンラップが必要です。「??」を使うとデフォルト値が設定できるので便利です。

口のカーブ具合も同様にできます。

private var mouthCurvatures = 
    [FacialExpression.Mouth.Frown: -1.0, 
                            .Grin: 0.5, 
                           .Smile: 1.0, 
                           .Smirk: -0.5, 
                          .Neutral: 0.0]

そして完全なupDateUI()メソッドは次のようになります。

private func upDateUI() {
    switch expression.eyes {
    case .Open: faceView.eyesOpen = true
    case .Closed: faceView.eyesOpen = false
    case .Squinting: faceView.eyesOpen = false // 取りあえず
     }
    faceView.eyeBrowTilt = 
        eyeBrowTilts[expression.eyeBrows] ?? 0.0
    faceView.mouthCurvature = 
        mouthCurvatures[expression.mouth] ?? 0.0

たぶんこれ以上説明はいらないと思います。MVCの実装も上手にできています。私の疑問点はあくまでもFacialExpressionがStructure型で宣言されているところです。どうしてだろう?

今日はここまで。