Swiftで遊ぼう! on Hatena

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

Swiftで遊ぼう! - 297 - Split View Controller

Swiftで遊ぼう!の古い記事-> Life-LOG OtherSide
質問 : Swiftで遊ぼう! - 252 - Happiness - カスタムViewチュートリアル - Swiftで遊ぼう! on Hatena

これでアプリを立ち上げるとスタンフォード大学の風景が表示されるようになりました。

これではつまらないですよね。プロジェクトのタイトルは「Cassini」です。土星探査衛星から名前をとっているので、イメージ画面の切りかえをしてCassiniが表示されるようにします。

Multiple MVCsの講義で使ったSplit View Controllerを使います。

オブジェクト・ライブラリから「Split View Controller」をドラッグしてMain.storyboardに引っ張ってくると、色々なテンプレートViewがくっついてきます。前の講義では、Split View Controller以外のViewを選択して消去、自分で作ったカスタムViewControllerを繋ぎました。メニューから「Editor -> Embed in -> Navigation Controller」を選んだのを覚えていますか? この作業を省くために、テンプレートViewのNavigation Controllerだけ残します。

SplitViewControllerから「Ctrl + ドラッグ」してImageViewControllerに繋いで「detail view controller」を選びます。

次にNavigation Controllerから「Ctrl + ドラッグ」してメインのViewControllerに繋いで「root view controller」を選びます。

f:id:yataiblue:20150501190140j:plain

次にMain.storyboardにボタンを配置します。

これも慣れたものです。オブジェクト・ライブラリからボタンをドラッグして3つ並べます。

ボタンをそれぞれ「Earth」「Cassini」「Saturn」と変更してフォントのサイズを適当に大きくsasコンストレイントの設定をします。

それぞれのボタンからSegue(セグエ)を設定するために「Ctrl + ドラッグ」してImageViewControllerに繋ぎます。

ポップアップメニューから「show detail」を選びます。Segueの設定でポイントはアトリビュート・インスペクタにあるIdentifierの設定です。

ボタンの名前に合わせてIdentifierを「Earth」「Cassini」「Saturn」にします。

これで下準備ができました。

f:id:yataiblue:20150503081303j:plain

次はコーディングですがまた明日。

Swiftで遊ぼう!の古い記事-> Life-LOG OtherSide
質問 : Swiftで遊ぼう! - 252 - Happiness - カスタムViewチュートリアル - Swiftで遊ぼう! on Hatena

今日はSegueのコーディングをします。

Segueのコーディングもある意味、定型化されていて覚えておくべきでしょう。ボタンを配置したメインのViewControllerにコーディングをしていきます。

class ViewController: UIViewController
{

}

このクラスの中にprepareForSegueとタイプしていくとコーディングのための補完機能が働きます。
f:id:yataiblue:20150503084109j:plain
これを選べば自動的にoverrideのキーワードが付いてコードを入力する準備状態になります。
f:id:yataiblue:20150503084210j:plain

そして「定型文」を入力します。

override func prepareForSegue(segue: UIStoryboardSegue, 
                              sender: AnyObject?) {
    if let ivc = 
    segue.destinationViewController as? 
            ImageViewController {
        if let identifier = segue.identifier {
            switch identifier {
                case "Earth":
                    ivc.imageURL = DemoURL.NASA.Earth
                    ivc.title = "Earth"
                case "Cassini":
                    ivc.imageURL = DemoURL.NASA.Cassini
                    ivc.title = "Cassini"
                case "Saturn":
                    ivc.imageURL = DemoURL.NASA.Saturn
                    ivc.title = "Satrun"
            default: break
            }
        }
    }
}

これで準備はできたのですが、このままラン(Cmd + R)してもスタンフォード大学の風景が出たままになっています。

というのも作成されるImageViewControllerのviewDidLoad()内にある次のコードを消す必要があります。

if image == nil {
    imageURL = DemoURL.Stanford
}

これでSegueが動きます。

Earthボタンを押すと

f:id:yataiblue:20150503085443j:plain

バックしてCassiniボタンを押すと

f:id:yataiblue:20150503085544j:plain

巨大な画像の一部しか表示されていないのです。

ここでスクロールビューが必要になるということです。

今日はここまで。

Swiftで遊ぼう!の古い記事-> Life-LOG OtherSide
質問 : Swiftで遊ぼう! - 252 - Happiness - カスタムViewチュートリアル - Swiftで遊ぼう! on Hatena

ScrollViewを組み込む最も簡単な方法は当然Xcodeのオブジェクト・ライブラリからUIScrollViewをドラッグするやり方です。これでrootビューの上にジェネリックなスクロールビューが設置されたので、「Ctrl + ドラッグ」してImageViewControllerに引っぱって名前を「scrollView」にしてoutletを作ります。

@IBOutlet weak var scrollView: UIScrollView! {
    didSet {
        scrollView.contentSize = imageView.frame.size
    }
}

こういう場合のプロパティ・オブザーバーは常套手段のようです。scrollViewが呼ばれる毎にコンテンツサイズを変化させるようにします。

このoutlet作成で、自動的にrootビュー上にscrollViewがインスタンス化されました。

次はViewControllerが呼ばれた時、viewDidLoad()内でイメージをscrollViewに加える必要があります。

override func viewDidLoad() {
    super.viewDidLoad()
    scrollView.addSubview(imageView)
}

もう一つ重要なステップがあります。imageViewのサイズでscrollView.contentSizeは変化しますが、imageViewはimageのサイズで変化します。

private var image: UIImage? {
    get {
        return imageView.image
    }
    set {
        imageView.image = newValue
        imageView.sizeToFit()
        scrollView?.contentSize = imageView.frame.size
    }
}

imageに新しい画像が書き換えられた時にcontentSizeが変化するようにします。この時のポイントは、imageの書き換えは画面の描画と関係なく内部的に発生するかもしれません。そういう場合はscrollViewが存在しないかもしれないので、オプショナル(?)で回避すると言うことです。

たったこれだけ変更を加えただけで、ラン(Cmd + R)してCassiniボタンを押してしばらく待つと(次のマルチスレッドで対処します)、大きな画像をスクロールできるようになりました。
f:id:yataiblue:20150504102452j:plain

感動です。次はズーミングですね。

2016年6月6日:加筆していません。2年後のiOS9バージョンの講義と全く同じでした(^_^;) 

実はScroll Viewの学習で最も重要なトピックは、iOS API利用におけるデリゲート・メソッドの取り扱いです。

yataiblue.hatenablog.com

基本的なデリゲーションの説明は上記でしていますが、iOS API利用にこの考え方は欠かせません。ここで6つのステップを説明していますが、API利用のデリゲートはステップ4、5そして6を実装するだけです。

プロトコール利用の場合、必ずデリゲートされているメソッドを全て実装しないとランタイムエラーに陥りますが、API利用の場合、すべてオプショナル扱いになるので使いたいメソッドだけ実装したらいいようです。

Scroll Viewには全部で12個のデリゲーションメソッドがありますが、ZoomingのメソッドのviewForZoomingInScrollView()を実装してみます*1

先ず、Scrol Viewでデリゲートさせたいメソッドを含んだプロトコールをImageViewController宣言をさせます。これはステップ4になります。

class ImageViewController: UIViewController, UIScrollViewDelegate {
...

次はステップ5で、scrollViewのdelegateプロパティにImageViewController自身を持たせます。ImageViewController上のscrollView宣言部分でdelegate先を指定します。たぶん初心者には分かりにくい説明だと思いますが、これは慣れるしかないでしょう。

@IBOutlet weak var scrollView: UIScrollView! {
    didSet {
        scrollView.contentSize = imageView.frame.size
        scrollView.delegate = self
        scrollView.minimumZoomScale = 0.03
        scrollView.maximumZoomScale = 1.0
    }
}

そして最後のステップ6はメソッドの実装です。

func viewForZoomingInScrollView(scrollView: UIScrollView) -> UIView? {
    return imageView
}

たったこれだけで画像のズーミングができるようになりました。

f:id:yataiblue:20150504113359j:plain

今日はデリゲーションの復習でした。

*1:このメソッドは、minimumZoomScaleとmaximumZoomScaleを設定しないと動かないので同時に設定します。