リブートキャンプ by Swift 目次

 

 例えば、前回のサンプルにしても、説明されてないプロパティや、クラスが出てくるわけですが…

 

   ↓こんな感じで

   message.backgroundColor = UIColor.orange

 

 そういうときにどうするかって話です。

 

 基本、単語をコピペしてググればいいです。

 まあ「backgroundColor」をそのまま検索よりはmessageはUILabelのオブジェクトだから「backgroundColor UILabel」というようにキーワードを増やして検索した方がいいでしょうね。UILabelでダメなら派生元のUIViewとかね。

 ここら辺は、みなさん心得てるとおりです。

 

 なので、ここではそれとは別の、Xcodeのクイックヘルプを使う方法も紹介しておきます。

 Xcodeにはクイックヘルプという、クラスやプロパティ、メソッドを調べる機能が用意されているんですよ。

 

 ↓それがこれだ!

 

 

 クイックヘルプに出てくる説明文は英語なんで、なかなか敷居が高いと思いますが、それでもリンクたどって関連するクラスやプロパティ、メソッドを見つけたり、それを元にググるってことはできます。

 そうすると、日本語で解説してるサイトにぶち当たる可能性があるわけです。

 あと、上のビデオで紹介したDocumentation and API Reference画面で検索すると、Apple提供のサンプルコードを見つけたりもできるのよ。

 ここら辺はプログラミングに慣れた者には結構おいしいかも。

 なんせSwiftやObjective-Cは世界共通言語ですから〜。

 プログラミング初心者は、まあ、最初に言ったように入門サイト探してSwift Playgroundsなんかで学習しましょう。

 このリブートキャンプ by Swiftでも、極力説明していくつもりっす。

 

 

 

 英語読めるならガイドなんかもオススメ。

 対象のクラスやメソッドの使い方ガイドです。

 

 

 

 ガイドはありがたいけど、さすがに英語は〜という人は、ググるしかないんですが、そういう時にも検索に使えそうなキーワードが見つかったりするので、まあ諦めずに頑張れ。

 サンプル読んでて、知らない単語に突き当たった時の対処法はそんなとこです。

 

 んじゃ、そもそも、そのサンプルにたどり着くために、iPhoneにどんな機能があるかを調べるにはどーするのというと、このサイトです。

 

 ↓アップル・デベロッパー・サイト

https://developer.apple.com/jp/

 


 

 当然ですな。

 総本山。このサイト以上の情報源はあり得ません。ただし英語です。

 最初の日本語ページに騙されちゃダメ。ちょっと読み進めるとすぐ英語ページ飛ばされるから。

 ちなみに。一番上の「開発」タグをクリックすると、開発に関する情報ページが表示されます。

 

 

 最初に漁るのはWWDCのビデオがいいかな。

 

 

 2013〜2016年のもあるけど、基本最新の年からみるといいです。

 

WWDC 2017 Keynote

 
 で、新機能の大まかな説明、あとは興味のある機能別に観るって感じなりよ。

 こいつも英語なんで、わからない人にはさっぱり(しかもプレゼンしてるの、喋りのプロじゃないので、興が乗ってくるとマシンガントークになってくるし…)だけど、キーワードは得ることができるわけで、それ使ってググりましょう。

 

 何よりキーワードを見つけることが重要。

 

 キーワードをアップル・デベロッパー・サイト内で検索することも可能っす。

 

 

 例えばUIViewで検索してVideoでフィルタリングするとこんな感じ。

 

 

 キーワードを見つけてググって、日本語の説明サイト見つけて、サンプル見つけて、知らない単語出てきたらまた調べて、そんな繰り返しっす。

 ラノベ本のあとがきにも書いたけど

 

  心配ばっかして時間を無駄にせず、とりあえず突っ走ってみてください。

  意外となんとかなります。飛んでから着地点考えましょう。

 

 で、ちょっとだけ朗報。

 アップル・デベロッパー・サイトのガイドを訳した

 

 日本語ドキュメントのページ

 

があります。

 非常にありがたいし、アップル・デベロッパー・サイトのページからもリンクが貼られているんで公式だと思うんですが、たまにリンクが消えます。今も消えてる。

 とりあえず、上に貼ったURLは健在みたいなんで、そこでFAQでも紹介してる以下のガイドを読みましょう。

 

iOSアプリケーション プログラミングガイド

iOS View プログラミングガイド

iOS View Controller プログラミングガイド

 

 Apple純正のガイドを読みたいなら、ここら辺から読むのが順当だと思われ。

 もっとも、ここら辺は「iPhone アプリ 開発」で見つけたサイト読みながら勉強でもいいと思います。

 あとSwift言語に関しては究極の説明書が、iBooksでタダで読めます。

 

 

 まあ、英語なんですがね。

 あらゆる日本語のSwift解説本はこれを原本にしてます。してなきゃ嘘。

 ここら辺も「Swift入門」あたりで見つけたサイト読むのでいいんじゃないかとは思うが…

 興味がある人はiBooks開いてStoreを

 

 Swift Programming

 

で検索だ。

 

 長々話したけど、次回からは通常運転で行きます。

 サンプル用意して、上で書いた

 

   message.backgroundColor = UIColor.orange

 

の説明や、画面タップへの反応なんかを説明入れつつやって行きます。

 

 ↓こんな感じのアプリはどー作るのか?

 

 

サンプル:

http://tetera.jp/xcc/book-sample/RPG.zip

 

 ぶっちゃけ、こういうのはUnityとかUnrealEngine、Cocosなんかを使えばいいんですけどね。直接作ったほうがいいのはTwitterとか小遣い帳、ノートとか言ったユーティリティ系ですな。

 ただ、そういうのは地味なので、とっかかりとしてはこっちの方がいいんじゃないかと思われ。

 

 ちょっと長くなってるけど、やってることは画面の貼り付けがほとんどなんで、腕試しに、ヘルプやグーグル駆使して調べてみてください。

 答え合わせは次回。

 ちなみにViewControllerクラス定義中に出てくるプロパティ

    let script = [
        "雨の日はだるい…\n今日は学校休んじゃおっかな〜",
        "誰だ"
    ]

 

は、ノーヒントだと厳しいので配列という情報だけ教えときます。「swift Array」なんかでググってみてね。

 デワデワ。

 

 リブートキャンプ by Swift 目次

↑結構溜まったんでまとめました

 

 前回、画面は階層化できるって話をしましたが、そこらへんをもうちょっと具体例で示してみます。

 それと、任意の文字列を表示できるUILabelがテキストの基本なら、もう一つ、グラフィックの基本UIImageViewも知ってないとあかんでしょうということで、そっちも紹介しておく。

 

UIImageView

 

 こいつは、既存のPNGやJPEG画像を画面に表示するUIView派生クラス。

 基本的な扱いはUILabel(つまりUIView)と同じで、親画面上の矩形を指定して作り、addSubviewで子供にします。

 違うのは、表示する内容が画像なんでtextプロパティじゃなくimageプロパティってのを持っている点。

 なので、前回のUILabel版プロジェクト風にやるなら、次のようになります。

    override func viewDidLoad() {
        super.viewDidLoad()
        let imageView = UIImageView(frame:CGRect(x: 50, y: 100, width: 200, height: 200))
        imageView.image = UIImage(named: "girl")
        self.view.addSubview(imageView)
    }

 そして、imageプロパティには文字列ではなく画像を指定しなきゃならんわけですよ。

 で、画像はUIImageというオブジェクトで表現します。

 

UIImage

 

 こいつを作るときは、プロジェクトに登録した画像ファイルの名前を指定して作るのが基本。今回なら"girl"が画像ファイルの名前です。

 利用する画像ファイルは、プロジェクトにあらかじめ用意されているAssets(アセット:資産という意味じゃよ)フォルダにPNGやJPEG画像ファイルをドラッグ&ドロップして登録します。

 

 

 この時に、ファイル名がそのままAssets内での名前になり、これをUIImage作成時に指定することになるわけです。

 登録された項目を選ぶと、横の画面にサムネイルが出ます。

 2x、 3xとあるのは、iPhone 7、iPhone 7 Plusにそれぞれ最適化した画像をしてする場合用です。なければないで、1xが拡大されて使い回される。

 

 

 

 

 ちなみに、サンプルのgirl.pngは289 × 431ピクセルの大きさなので、iPhone 7用だとその2倍の578 × 862ピクセルの大きさの画像、iPhone 7 Plusだと3倍の867 x 1293ピクセルの大きさの画像を指定することになります。
 登録したらファイルのコピーがプロジェクトフォルダ内に作成されるので、元のファイルは捨てるなり保存するなり好きにしてください。

 

 ここまでできたらRun。

 

 ↓こんな感じの画面が開くことになります。

 

 

サンプル:

http://tetera.jp/xcc/book-sample/image.zip

 

 女の子が太ってるのは、元々の画像が289 × 431 ピクセルなのを、200 x 200 ポイントの矩形にスケーリングして表示してるから。解決法は色々あるけど、何も指定しないとこうなります。

 サンプルいじってimageViewの矩形を縦長にしたり色々大きさを変えて試してみるといいでしょう。

 

 上で出てきた「ポイント」は、フォントの大きさを指定する単位で、1ポイント=1/72インチという大きさです。iPhoneでは画面座標もこの単位で指定します。

 例えば、物理的な液晶の画素(ピクセル)は、初代iPhoneで横320ピクセル、iPhone 4、5では640ピクセルと、初代の2倍になってるんですが、ポイント座標での表現はどちらも320ポイントとなります。

 座標の横1ポイントの大きさに画素が1個なのが初代、2個なのがRetinaディスプレイ搭載と言われるiPhone 4以降の機種。

 より画面がきめ細やかになったわけですな。

 

 

 ちなみにiPhone 6 plusで導入されたRetina HDディスプレイだと1ポイントの画素は3個と、初代の3倍となります。

 

 ↓並べるとこんな感じ(S,SEは省略)

 

         液晶画素数  ポイント  倍率 

初代       320     320    x1

4、5        640     320    x2

6、7        750     375    x2

6 plus、7 plus  1080     360    x3

 

 液晶画素数だとかなりの開きがあるんですが、ポイントで座標を指定してる限り、画面上に表示される物理的な大きさはそこそこ統一されます。

 6以降は物理的な横幅が広くなってるのでポイントも広がってる。

 まあ、これがAssetsでのx1、x2、x3の意味でもあります。最近インフレ気味なんでPDFのベクター画像もサポートするようになりました。

 

UIView画面の階層化

 

 で、いよいよUIView画面の階層化の話です。

 今度は背景用の画面(UIImageView)の上に、前景用の画面(UIView)をかぶせてみます。で、その前景用の画面には少女の画面用(UIImageView)とセリフ画面用(UILabel)が子画面として登録されている。

 

 

 

 

 

    override func viewDidLoad() {
        super.viewDidLoad()

        //  背景
        let backgroundImageView = UIImageView(frame:CGRect(x: 0, y: 0, width: 300, height: 300))
        backgroundImageView.image = UIImage(named: "back")
        self.view.addSubview(backgroundImageView)

        //  セリフ付き前景
        let baseView = UIView(frame: CGRect(x: 20, y: 200, width: 260, height: 100))

        //  人物
        let imageView = UIImageView(frame:CGRect(x: 0, y: 0, width: 150, height: 220))
        imageView.image = UIImage(named: "girl")
        baseView.addSubview(imageView)

        //  セリフ
        let message = UILabel(frame: CGRect(x: 90, y: 130, width: 160, height: 40))
        message.backgroundColor = UIColor.orange
        message.text = "ここを通りたくば、私と"

        baseView.addSubview(message)
        self.view.addSubview(baseView)
    }

 どこにaddSubviewされてるかを注意しましょう。

 でRunすると次のようになります。

 

 

 で、これの最後に以下を付け加えてやると

    ・・・
        baseView.addSubview(message)
        self.view.addSubview(baseView)
        
        baseView.frame = CGRect(x: -50, y: 120, width: 260, height: 100)
    }

前景用の画面がまとめて移動することになるわけですな。

 

 

サンプル:

http://tetera.jp/xcc/book-sample/image-hierarchy.zip

 
 iPhoneのアプリの画面ってのは、こういうUIView、またはその派生クラスのオブジェクトの組み合わせで作り上げられとるわけですよ。
 以上、画面階層の話でした。
 ち・な・み・に、自分のアプリの画面の階層化され具合をみてみたい人は、Run中にDevag View hierarchyボタンをクリックだ!
 
 
 次回は、ここら辺の情報をどうやって見つけ出していくかについて…
 全員シャルジャー!
 

リブートキャンプ by Swift 目次

 

 前回説明したように、XcodeのSingle View Applicationテンプレートで用意されるアプリプロジェクトは、ViewController.swiftの加工を起点とするように設計されとります。オブジェクト指向言語であるSwiftのオーバーライド機能を利用して、元々の振る舞いを自分の処理に振り替えたりするんですわ。

 

 で、その実例が前回の、viewDidLoadメソッドの記述を数行の変更したプロジェクトというわけです。

 

 ↓viewDidLoadメソッドに追加記述したプロジェクトがこれ

http://tetera.jp/xcc/book-sample/test-hello.zip

 

 以下の3行の追加で元々の真っ白い画面に、文字列が表示されるようになってます。

 

 

 

 

 派生元であるUIViewControllerのviewDidLoadメソッドが、何をするものかというと、画面を表示する直前の最終画面調整っす。

 例の白い画面が用意されていて、表示される直前に呼び出されるメソッドなので、このタイミングで白い画面に、別の画面を貼り付けたりすることで、アプリが起動されて表示される画面のカスタマイズができるんですよ。

 

 プロジェクト作成直後のViewController.swiftでも、すでにviewDidLoadメソッドがオーバーライドされてるんですが内容は以下の通り。

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after 
    }

  ここで使われているsuperというキーワードは、メソッド内で自分自身にメッセージを送る時に使うキーワードの1つ。

 通常はselfというキーワードを使うか、送り先を省略することで自分自身を指定したことになるんだけど…

 

 

 そこにselfの代わりにsuperを使うと、送り先は自分自身に違いないが、呼び出されるメソッドは派生元であるUIViewController側のメソッドになるんですな。 

 

 

 結果、この1文を書くことで、派生元のUIViewControllerのviewDidLoadメソッドの処理を、そっくりそのまま真似ることができるわけだ。

 

 

 え、んじゃオーバーライドの意味ないじゃないすか、と思った人。

 大正解です。意味ないです。実質、派生元のUIViewController本来の作業をやってるだけなんで、用がなければ消しちゃってかまいません。

 このViewControllerクラスの定義でオーバーライドしてるviewDidLoadメソッドを、丸ごと消しても全然元気にアプリは真っ白い画面を表示します。

 

 じゃ、なんでこんな事してるかというと、さっき言ったように、たいていのアプリは、白い画面の上に自分たち独自の画面を表示するわけで、何もしないわけないんですよ。「オーバーライドすると思うんで、自分、準備しときました」という、Appleの親切心なわけですな。

 そういうわけで、viewDidLoadメソッドはプロジェクト作成直後からオーバーライドされてるし、中には、元の作業を再現するsuper.viewDidLoad()を書かれた上で「Do any additional setup…」つまり、なんか追加の設定作業ここに書けって感じのコメントがあるわけです。

 

 そして、実際にここに追加の画面を登録する処理を書き込んだのがこれ。

    override func viewDidLoad() {
        super.viewDidLoad()
        let label = UILabel(frame: CGRect(x: 50, y: 100, width: 200, height: 44))
        label.text = "こんにちは世界"
        view.addSubview(label)
    }

 これが、白い画面の上に文字表示用の画面を貼り付ける作業になります。

 

 

 具体的には、画面を管理しているオブジェクトに、あなたの担当画面上に、子画面を貼り付けさせてくださいとメッセージする作業になる。

 

 画面を管理しているオブジェクトは、UIViewControllerオブジェクト(つまり自分のことでもある)が、プロパティとして持っているUIViewクラスのオブジェクト。

 

 

 で、そいつに、どうやって子画面を貼り付けるかというと、子画面側を管理する別のUIViewオブジェクトを用意して、UIView同士で親子関係を結べばいいんですな。

 それがプログラム中にも出ている

 

   親側UIViewオブジェクト.addSubview(子側UIViewオブジェクト)

 

というメッセージの送信。

 

 このメッセージで渡されたUIViewは、メッセージを送った先のUIViewの子供になります。で、画面上にも子供のなったUIViewが管理する画面が子画面として表示されることになる。

 

 

 

 そのためにUIViewは、自分の親画面上での矩形情報をframeプロパティとして保持します。

 

 

 

 

 今回は、シンプルに文字表示用の画面を、元からある画面に貼り付けただけなんでピンとこないかもしれないけど、大抵の場合は、子画面の中にまた子画面があるって感じになって、親画面が移動すると、子画面も一緒に移動するので、addSubviewで、どの画面と親子関係を結ぶかは画面レイアウトで重要な意味を持つことになります。

 

 

 

 

 

 ま、そこらへんは自分で経験積んでください。

 

 それはともかく、今回、子供にする文字列表示用画面を管理するオブジェクトはUILabelと言います。で、当然こいつはUIViewの派生クラスなんで、さっきのaddSubviewに渡すことができます。
 なんで?と思った人は前回から読み直すように。

 

 作るときには、上で言ったframeプロパティを設定するために、貼り付けたい位置や大きさを、矩形を表現するCGRectという構造体を作成して引数として渡すのが慣例。

 

 

 慣例通りするのが嫌なら、そのまま
 
  let label = UILabel()
 
とやって、後からframeプロパティを設定してもかまいません。
 
  label.frame = CGRect(x: 50, y: 100, width: 200, height: 44)
 
 いずれにせよ、frameプロパティはCGRectという構造体で指定します。
 で、この今回初登場の構造体て何?なんですが…
 ぶっちゃけ、プログラム始めたばっかなら、オブジェクトの一種、以上!
 てのでもいいんですが、一応、知識として知っておいた方がいいだろうから、ちょっと語ります。

 

構造体

 

 構造体は、クラスから作るオブジェクトとよく似ていて、structというキーワードで新しい型を定義し、その定義から作成するようになってます。

 

 

 プロパティやメソッドを定義することもできて、かなりクラスとまぎらわしいけど、大雑把にいうと、次の2点が異なる。
 

  1、派生できない。したがってメソッドのオーバーライドは不可能

  2、変数同様、値全体がコピーされる

 

 2の方は、クラスから作られるオブジェクトを設定した変数や定数がどういったものなのかを知らなければ、意味わからないでしょうな。

 

 実は、オブジェクトの変数や定数には、オブジェクトそのものではなく、オブジェクトを特定するための識別子が設定されます。

 オブジェクトのメアドが設定されるとでも考えればいい。メアドがわかればメッセージを送ることができるし、プロパティの値を問い合わせることもできる。

 

オブジェクトなら

    

    

 

 これに対し、構造体は構造体そのものが変数や定数に設定されます。

 

構造体なら

 

 

 この違いは、変数や定数のコピー時に影響を及ぼすわけで、次のような記述は、オブジェクトか構造体かで、実際の動作が異なることになるわけですよ。

 

   let pt = Point(x: 1, y: 2)

   let pt2 = pt

 

オブジェクトなら

   

 

構造体なら

    

 

 

 そのほか、次のようなコピー先のプロパティの値変更にも影響します。

 

   let pt = Point(x: 1, y: 2)

   let pt2 = pt

   pt2.x = 3

 

 まず、オブジェクトだと、定数として用意したpt2のプロパティxを変更することができる。変更不可なのは識別子の値であって、識別子が特定するオブジェクトではないから。

 この場合、識別子が特定するオブジェクトはptが特定するオブジェクトでもあるので、結果、pt2.xの変更はpt.xの変更にもなる。

 

オブジェクトなら

 

 

 これが構造体の場合、定数として用意したpt2のプロパティxを変更することはできない。変更したければpt2を変数として用意する必要がある。

 

  let pt = Point(x: 1, y: 2)

  var pt2 = pt            ← まず、変更可能にするには変数とする

  pt2.x = 3

 

 また、こうした場合、pt2.xの変更はpt.xには影響しない。

 

構造体なら

 

 

 クラスや構造体を定義する者は、このような特性の違いを考慮して、どちらにするか決定します。

 ま〜、いずれは、この手の知識も重要になってくるわけですよ。

 今は、そんなもんか程度でいいです。

 

 座標を表現するCGPointは派生の必要性はなさそうだし、値そのものがコピーされる方が扱いやすい。そういった点で構造体として用意されているんでしょう。

 Swiftが作られる前からObjective-Cで用意されてたから、名残っちゃ名残だけどObjective-Cでも、クラスじゃなく構造体になってるのは上記の理由と思われ。

 

 

ラノベ本の原型から抜粋(前回の続き)


 

 あとは、作ったUILabelオブジェクトのtextプロパティに、表示したい文字列を設定して、こいつをViewControllerが持つUIViewに、addSubviewで小画面として登録すれば、アプリの画面として文字列が表示される

 ViewControllerが持つUIViewはプロパティとして用意されている。

 自分(または派生元)が持っているプロパティは、そのことを明示するためにselfを使う場合もあるが、省略されている場合も多い。

 ViewControllerのviewDidLoadメソッドは、UIViewControllerが画面表示用オブジェクトを用意した直後に呼び出され、UIViewControllerがやるべきことをやった後で、画面文字列表示用オブジェクトであるUILabelを作成して追加するという処理を行っていることになる。

 

 

 この理解で正しいなら、このviewDidLoadメソッド内の処理を、同じようにtestプロジェクトのViewControllerでやれば、同じように画面に文字列が表示されるはずだった。

 試してみましょうと安堂が、サンプル側のviewDidLoadメソッドの処理をtestプロジェクトのviewDidLoadメソッドにコピー&ペーストする。

 そしてRun。

「こんにちは世界」

 シミュレータが立ち上がり、表示されたtestアプリの真っ白い画面に、くっきりと文字列が表示された。

 


 

 ↓viewDidLoadメソッドに追加記述したプロジェクトがこれ

 

http://tetera.jp/xcc/book-sample/test-hello.zip

 

 文字列表示用のUILabel以外にも、画像表示用のUIImageViewとか、いろいろなUIView派生クラスがあります。

 そこらへんを先駆者がアップしてるサンプルプロジェクトを見たり、Appleのデベロッパページに行って調べたりして、iPhoneアプリを作っていくわけですな。

 

 ↓ここね

 

https://developer.apple.com/jp/

 

 こんにちはデベロッパの世界。

 でわでわ。