- 投稿日:2020-05-25T23:23:25+09:00
何となく理解するSwiftのアンダースコア
はじめに
Swiftをやっていて「何やねんこれは」となり、最初は放置していましたが、
最近何となしにわかり始めたので書きました。値の破棄
Swiftでは変数名にアンスコを使用することで代入する値を破棄することができます。
例えば、nilチェックするけど別にそれ自体を何か他で使用することがないときとか。
for-inで回すけど特にループ変数は使うことがないとか、そんなときに使います。
以下、SceneDelegate.swiftに初期で記されてあるものです。guard let _ = (scene as? UIWindowScene) else { return }ダウンキャストのチェックはするけど以降で別に使用しないので、アンスコで破棄しています。
とりあえず、変数名にアンスコを見かけたら「他のところで使わんのやね」でおkと思います。引数名の省略
Swiftでは関数を呼ぶ際には通常ラベル名もしくは引数名を記す必要がありますが、
アンスコをラベルに使用することで値のみの記載で呼び出せるようになります。// 引数名のみ func sumNum(x: Int, y: Int) -> Int { return x + y } sumNum(x: 1, y: 2) // 3// ラベル func sumNum(num1 x: Int, num2 y: Int) -> Int { return x + y } sumNum(num1: 1, num2: 2) // 3// アンスコ func sumNum(_ x: Int, _ y: Int) -> Int { return x + y } sumNum(1, 2) // 3おわりに
まだまだ理解不足なところだらけなので、コメント欄で補記していただいたり、
追加のリクエストくださると嬉しいです。
- 投稿日:2020-05-25T22:46:34+09:00
【Swift】TableViewで必要なメソッド
セルの数を決めるメソッド
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return セルの数(chatArray.count) }セルに値を設定するメソッド(以下はチャットアプリ用)
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { //セルを取得する let cell = tableView.dequeueReusableCell(withIdentifier: “ID", for: indexPath) as! CustomCell //セルに表示する値を設定する cell.messageLabel.text = chatArray[indexPath.row].messsage cell.userNameLabel.text = chatArray[indexPath.row].sender cell.iconImageView.image = UIImage(named: "") return cell }セルのセクション数を決めるメソッド
func numberOfSections(in tableView: UITableView) -> Int { return セクションの数 }セルの高さを決めるメソッド
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { return view.frame.size.height/6 }セルがタップされて時に呼ばれるメソッド
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) }
- 投稿日:2020-05-25T22:36:55+09:00
swift 日記 20200525
こんばんは。
今日は、じゃんけんアプリの作成のstepの1つを実施しました。
やったこと
1, Labelの配置(前回のおさらい)
2, Buttonの配置
3, Imageviewの配置
4, Buttonの文字サイズ変更、背景変更
5, 画面全体の背景変更
- 投稿日:2020-05-25T22:21:38+09:00
【Swift5】Googleフォームで作成したお問い合わせフォームにあらかじめ自動入力する
はじめに
アプリの不具合の報告などで、OS情報やアプリのバージョンなどの情報があるととても助かりますよね。
そこで今回は、それらの項目を自動入力してくれるフォームの作り方を紹介します。方法
Googleフォームには、自動入力済みのURLを発行する機能があります。
このURLの一部をSwiftで書き換えればいいのです。1. フォームを作成
こちらからフォームを作成してください。
作成方法については説明を割愛させていただきます。2. URLを生成
フォーム作成画面の右上にある、・が縦に3つ並んだメニューボタンから、「事前入力したURLを取得」をクリックします。
するとフォーム入力画面に飛ぶので、事前入力しておきたい項目に分かりやすい文字列を入力しておきます。
「リンクを取得」をクリックするとURLが生成されます。
画面左下の「リンクをコピー」を押してコピーしておきましょう。
自分の場合はこのようなリンクになりました。
https://docs.google.com/forms/d/e/1FAIpQLScUGRP-Pbs0bpBw2MtwjN6OpxeoHgUjloGV-OMNVOBvEkLJ6A/viewform?usp=pp_url&entry.1009686721=osversion&entry.1835889012=appversion3. OS情報、アプリのバージョンを取得
OS名は
UIDevice.current.systemName
、OSバージョンはUIDevice.current.systemVersion
で取得可能です。(どちらもStringです)let os = "\(UIDevice.current.systemName)%20\(UIDevice.current.systemVersion)" let version = Bundle.main.object(forInfoDictionaryKey: "CFBundleShortVersionString") as? Stringこの文字列はURLの一部となるため、スペース(空白)は使ってはいけません。
代わりに%20
を使用すると空白になります。4. URLの書き換え
先ほどのURLのosversionの部分を
\(os)
に、appversionの部分を\(version)
に書き換えるだけです。let url = URL(string:"https://docs.google.com/forms/d/e/1FAIpQLScUGRP-Pbs0bpBw2MtwjN6OpxeoHgUjloGV-OMNVOBvEkLJ6A/viewform?usp=pp_url&entry.1009686721=\(os)&entry.1835889012=\(version!)") UIApplication.shared.open(url! as URL)動作確認
- 投稿日:2020-05-25T22:21:38+09:00
【Swift5】Googleフォームにあらかじめ自動入力する
はじめに
アプリの不具合の報告などで、OS情報やアプリのバージョンなどの情報があるととても助かりますよね。
そこで今回は、それらの項目を自動入力してくれるフォームの作り方を紹介します。方法
Googleフォームには、自動入力済みのURLを発行する機能があります。
このURLの一部をSwiftで書き換えればいいのです。1. フォームを作成
こちらからフォームを作成してください。
作成方法については説明を割愛させていただきます。2. URLを生成
フォーム作成画面の右上にある、・が縦に3つ並んだメニューボタンから、「事前入力したURLを取得」をクリックします。
するとフォーム入力画面に飛ぶので、事前入力しておきたい項目に分かりやすい文字列を入力しておきます。
「リンクを取得」をクリックするとURLが生成されます。
画面左下の「リンクをコピー」を押してコピーしておきましょう。
自分の場合はこのようなリンクになりました。
https://docs.google.com/forms/d/e/1FAIpQLScUGRP-Pbs0bpBw2MtwjN6OpxeoHgUjloGV-OMNVOBvEkLJ6A/viewform?usp=pp_url&entry.1009686721=osversion&entry.1835889012=appversion3. OS情報、アプリのバージョンを取得
OS名は
UIDevice.current.systemName
、OSバージョンはUIDevice.current.systemVersion
で取得可能です。(どちらもStringです)let os = "\(UIDevice.current.systemName)%20\(UIDevice.current.systemVersion)" let version = Bundle.main.object(forInfoDictionaryKey: "CFBundleShortVersionString") as? Stringこの文字列はURLの一部となるため、スペース(空白)は使ってはいけません。
代わりに%20
を使用すると空白になります。追記 機種の名前を取得する
正確な機種名(例えば「iPhone XS」など)を取得するため、YMTGetDeviceNameというライブラリを使用します。
var device = YMTGetDeviceName.share.getDeviceName() if let devName = device.range(of: " ") { device.replaceSubrange(devName, with: "%20") }rangeを使って、空白を
%20
に置き換えています。4. URLの書き換え
先ほどのURLのosversionの部分を
\(os)
に、appversionの部分を\(version)
に書き換えるだけです。let url = URL(string:"https://docs.google.com/forms/d/e/1FAIpQLScUGRP-Pbs0bpBw2MtwjN6OpxeoHgUjloGV-OMNVOBvEkLJ6A/viewform?usp=pp_url&entry.1009686721=\(os)&entry.1835889012=\(version!)") UIApplication.shared.open(url! as URL)動作確認
- 投稿日:2020-05-25T17:25:03+09:00
[swift5] ボタンにセットした画像の色をコントロールする.
結論
以下のように, 「画像の読み込み時」にRenderingModeにalwaysTemplateを指定します.
let img = UIImage(named: "test.png")?.withRenderingMode(.alwaysTemplate) self.imgButton.setImage(img, for: .normal) self.imgButton.tintColor = UIColor.green //ボタンにセットした画像の色を緑にする.注意事項
以下のように画像を取得してから, 違う行でRenderingModeを変更しても, 画像の色を変更することはできません. エラーは出ませんが, 画像取得時にRenderingModeを変更しなければならないようです.
////////////////////////////////////上手く行かない例/////////////////////////////// let img = UIImage(named: "test.png") //ここ. img?.withRenderingMode(UIImage.RenderingMode.alwaysTemplate) self.imgButton.setImage(img, for: .normal) self.imgButton.tintColor = UIColor.green補足
UIImage.RenderingMode : 画像生成時(レンダリング)のモードを特定する.
alwaysTemplate : 入力画像のカラー情報を無視して, 画像を描画するようにする.
[参考]https://developer.apple.com/documentation/uikit/uiimage/renderingmode
- 投稿日:2020-05-25T16:21:18+09:00
Swiftで幅優先探索をする際の注意点と工夫
はじめに
Swiftはデフォルトで一般的なキューが無いため、幅優先探索をする際は注意と工夫が必要です。
陥りがちなのはArray.removeFirst()を使うことによる計算量の増加です。
罠
Array.removeFirst()をキューのdequeueとして扱うのは好ましくありません。Array.removeFirst()は計算量が$O(N)$なので性能が悪いです。(https://developer.apple.com/documentation/swift/array/2884646-removefirst)
dequeueは$O(1)$である必要があります。(要検証)ArraySliceによるQueue
Collectionプロトコルの仲間であるSubSequence型のコレクションには、$O(1)$で先頭の要素を取り出せるpopFirst()が実装されます。
(Collection: https://developer.apple.com/documentation/swift/collection/1641417-popfirst)ArraySliceもSubSequenceの一つです。
SubSequenceのpopFirstはデフォルト実装されており、ArraySliceはそれをそのまま利用しています。
(ArraySlice: https://developer.apple.com/documentation/swift/arrayslice/1687956-popfirst)よってキューのenqueueのために、ArraySlice.popFirst()が使えそうです。
詳しいテストをしたわけではありませんので他の言語ほどの速度やメモリ効率は保証はできません。
詳しい仕様等をご存じの方がいらっしゃれば是非ご教授願います。
以下、実装例です。
実装
迷路のスタートからゴールまでの最短距離を求める問題とします。ゴールが見つからない場合は-1とします。
迷路を文字列の配列とし、
"#"
は壁、"."
は到達可能な道を表します。また、座標を[y, x]として、
- スタート = [0, 0]
- ゴール = [4, 4]
とします。
let maze = ["..###", "#..##", ".....", "#..#.", "##..."] .map { Array($0) } let start = [0, 0] let goal = [4, 4] let directions = [(0, 1), (1, 0), (0, -1), (-1, 0)] func bfs() -> Int { // ArraySliceによるキューの初期化 var queue = ArraySlice<(y: Int, x: Int, count: Int)>() queue.append((y: 0, x: 0, count: 0)) // 探索済みを保持するやつ var explored = Set<[Int]>() explored.insert(start) // 幅優先探索 while let (y, x, count) = queue.popFirst() { for (dy, dx) in directions { let sucY = y + dy let sucX = x + dx if goal == [sucY, sucX] { return count + 1 } // 到達可能な場合だけ進む guard !explored.contains([sucY, sucX]) && 0..<5 ~= sucX && 0..<5 ~= sucY && maze[sucY][sucX] == "." else { continue } queue.append((y: sucY, x: sucX, count: count + 1)) explored.insert([sucY, sucX]) } } return -1 } print(bfs())キューの初期化
// ArraySliceによるキューの初期化 var queue = ArraySlice<(y: Int, x: Int, count: Int)>() queue.append((y: 0, x: 0, count: 0))型指定に名前付きタプルを指定できるのはおもしろいところです。わかりやすくなると言える余地はありそうです。タイピング数とトレードオフですので、完全に好みになります。
let A = queue[0]
として値を取得したとき、A.x
、A.y
、A.count
というようにアクセスできるのは強みだと思います。探索済みの保持
// 探索済みを保持するやつ var explored = Set<[Int]>() explored.insert(start)本コードでは探索済みをSet型に保持しています。
[y, x]
のようなArrayが格納されます。
今回のような二次元座標の場合は二次元配列を使う方法がメジャーだと思いますが、
私は汎用性の高いSet型を好んで使います。while let 構文
while let (y, x, count) = queue.popFirst() {ArraySlice.popFirst()の戻り値の型はOptionalなのでnilチェックをする必要があります。Null安全言語であるSwiftはnilチェック用の構文が用意されています。
そのうちの一つがwhile let 構文で、変数にnilが代入されそうな場合はブロックを抜ける、nilでない場合は値を代入しブロック内で利用できるというような仕様です。
(他にも gurad let 構文, if let 構文などがあります。)
guard構文
// 到達可能な場合だけ進む guard !explored.contains([sucY, sucX]) && 0..<5 ~= sucX && 0..<5 ~= sucY && maze[sucY][sucX] == "." else { continue }guard構文はコードに「条件式を満たさない場合は以降の処理を行わない」という意味を強く持たせるために使える構文です。競技プログラミングのコーディングは混乱しがちなので、guard構文でまとまりのあるコーディングが可能です。
ちなみに
0..<5 ~= sucX
で「範囲以内」かどうかを求めることができます。
(Range: https://developer.apple.com/documentation/swift/range/2428743)
- 投稿日:2020-05-25T12:51:39+09:00
[Xcode]The maximum number of apps for free development profiles has been reachedと出てインストールできない。
原因
自作のアプリやプロビジョニングプロファイルを削除しても、インストールに失敗する場合は無料枠プロファイルのカウント処理がバグっている可能性が高いです。
対応
iPhoneをMacに繋いで、コンソールでログを表示しながら再度インストールを試してください。
- Window->Devices and Simulators->対象のデバイスを選択してOpen Consoleボタンをクリックでログを表示
- アプリを実行して対象デバイスへのインストールを試みてください。
MIFreeProfileValidatedAppTracker
でログを絞り込んでください。[MIFreeProfileValidatedAppTracker _onQueue_addReferenceForApplicationIdentifier:bundle:error:]: 182: This device has reached the maximum number of installed apps using a free developer profile: {( "<bundleid>", "<bundleid>", "<bundleid>" )}というようなエラーが出ているかと思います。
- その一覧に表示されているアプリを削除または、再インストール
- 自作アプリをインストール
の順番で実行してみてください。
情報源
3日ほど悩んだので取り急ぎ共有しました。
- 投稿日:2020-05-25T08:01:04+09:00
iOSアプリのローカライズとローカリゼーションのデバッグ - 開発言語の変更、ストーリーボードと文字列のローカライズ、ローカライズされていない文字列の特定とデバッグ
ローカリゼーションとは?
自分が作ったアプリが人気になり、英語など他の言語にそのアプリを翻訳してもっと多くの人が使えるようにしたいと思うときが来るかもしれません。iOSのシステムは、ユーザー設定に基づきコンテンツを適切な言語で表示するなど、大方のローカリゼーションを行ってくれます。
アプリをローカライズする方法
Development language
の変更開発言語とは、アプリの開発時に使用する言語のことを指します。まず、設定の内容を確認する必要があります:
プロジェクトファイルをクリックし、(ターゲットではなく)プロジェクトファイルを選択します。ローカリゼーションのセクションで、
Development Language
が日本語なのか英語なのかを確認します。日本語の場合、設定は正しいので、何もする必要はありません。英語の場合、引き続き開発言語の変更のガイドに従ってください。次に
Xcode
を閉じ、.xcodeproj
ファイルを右クリックして選択し、このファイルの内容を表示する:
project.pbxproj
ファイルを見つけ、テキストエディターで開く。
developmentRegion
を検索し、ja
に変更する。compatibilityVersion = "Xcode 9.3"; developmentRegion = en; hasScannedForEncodings = 0;上記のコードを以下に変更する
compatibilityVersion = "Xcode 9.3"; developmentRegion = ja; hasScannedForEncodings = 0;
knownRegions
を検索するknownRegions = ( en, Base, );上記のコードを以下に変更する
knownRegions = ( ja, en, Base, );ファイルを保存して、再度
Xcode
を開く:
Development Language
が日本語に変更されているのをご確認いただけます新しい言語の追加
- ファイルナビゲーター内のプロジェクトファイルをクリックします
- プロジェクトファイルをクリックします
- 情報タブを開いていることを確認してください
Localizations
セクションのプラスボタンをクリックしてください- 追加したい言語を選択してください
また、必ずスイッチ
Use Base Internationalization
を有効にしてくださいストーリーボードのローカリゼーション
初期設定
ローカリゼーションを最初にセットアップするとき、Xcodeはあなたのためにすべての文字列を抽出してそれらをファイルに入れる作業を行います。ストーリーボードのローカリゼーションをセットアップするには:
- Storyboard ファイルを選択します。
- 右側のパネルで、
Localizations
セクションを探し、追加したい言語のオプションをオンにします。これで、左側のファイルナビゲーターで、Storyboard ファイルを展開し、翻訳ファイルを確認できます:
Main.strings (English)
をクリックすると、原文と翻訳文のペアが表示されます:/* Class = "UILabel"; text = "こんにちは"; ObjectID = "ok2-tj-BN9"; */ "ok2-tj-BN9.text" = "こんにちは";さて、
ok2-tj-BN9
は、Storyboard 上にあるUILabel
の要素IDです。ストーリーボードインターフェースに戻り、そのラベルを選択すると、その要素IDが表示されます:また、
.text
はそのUILabel
のテキストプロパティを示しているにすぎません。さぁ、これで翻訳済みのストリングを
Main.strings (English)
に入れられるようになりました:/* Class = "UILabel"; text = "こんにちは"; ObjectID = "ok2-tj-BN9"; */ "ok2-tj-BN9.text" = "Hello";トランスレーションの追加
さらにトランスレーションを追加したい場合。
UIStoryboard
でエレメントのオブジェクトIDを見つけ、それをMain.strings (English)
ファイルに追加する。エレメントが異なる場合、プロパティは同じではない:
//9rM-vm-U1g is an UITextField. Placeholder means placeholder. "9rM-vm-U1g.placeholder" = "ユーザー名"; //GsY-2N-hIy is an UIButton, normalTitle means the title of the button at normal. "GsY-2N-hIy.normalTitle" = "ボタン"; //BYZ-38-t0r is an UIViewController "BYZ-38-t0r.title" = "登録画面";文字列のローカライズ
文字列ファイルを作成する
command-Nを押し、
Strings file
を作成するファイル
Localizable.strings
に名前を付けるXcodeでそのファイルを選択し、右側のローカライズボタンを選択する。ポップアップで英語を選択する。次に、右側のパネルで、すべての言語を選択する。
全ての文字列を
NSLocalizedString
に入れて下さいこの例では、元のコードは以下です:
@IBAction func actionPress(){ let alert = UIAlertController(title: "こんにちは", message: nil, preferredStyle: .alert) let actionClose = UIAlertAction(title: "はい", style: .cancel, handler: nil) alert.addAction(actionClose) present(alert, animated: true, completion: nil) }@IBAction func actionPress(){ let alert = UIAlertController(title: NSLocalizedString("こんにちは", comment: ""), message: nil, preferredStyle: .alert) let actionClose = UIAlertAction(title: NSLocalizedString("はい", comment: ""), style: .cancel, handler: nil) alert.addAction(actionClose) present(alert, animated: true, completion: nil) }コードファイルから文字列を抽出する
- Terminal を開く
cd
コマンドで.lproj
ファイルがある場所に移動し、以下のコマンドを実行するfind . -name \*.swift | xargs genstrings -o en.lproj find . -name \*.swift | xargs genstrings -o ja.lproj
en.lproj
ファイルを開く:ご覧の通り、すでにプログラムの文字列が含まれています:
/* No comment provided by engineer. */ "こんにちは" = "こんにちは"; /* No comment provided by engineer. */ "はい" = "はい";これで、英語の訳を追加することができます:
/* No comment provided by engineer. */ "こんにちは" = "Hello"; /* No comment provided by engineer. */ "はい" = "Yes";ローカリゼーションのデバッグ
シミュレーターの言語を変更する
シミュレーターの言語を変更するには、Edit Schemeをクリックします。
Options
タブを選択すると、Application Language
を編集できるようになります。プログラムを英語で実行すると次のようになります:
ローカライズされていない文字列の確認方法:
再度、設定
Edit Scheme
のタブOptions
を開き、Show non-localized strings
をオンに切り替える。この状態でプログラムをテストする。ローカライズされていない文字列があれば、コンソールにエラーが表示される:
Like this message:
localization[3921:116986] [strings] ERROR: pBD-R7-cpO.text not found in table Main of bundle CFBundle 0x7fdab8704210実際のデバイスでアプリケーションの言語を変更する:
実際のデバイスでアプリケーションをテストしてから、アプリケーションの言語を切り替える必要がある。そのためには:
システム設定を開く
アプリの名前を見つけ、アプリの設定を開く
言語オプションをクリックする
言語を切り替える
これで、自分のアプリをテストすることが可能となります。
- 投稿日:2020-05-25T03:02:39+09:00
『絶対に挫折しないiPhoneアプリ開発「超」入門』読書メモ
概要
以下の書籍を読み進めた際の読書メモです。
所要時間は約12時間でした。
前回読んだ本がかなりハンズオン重視で様々なアプリを次々と作っていく内容であったのに対して、こちらはより言語仕様や概念、各種機能の解説を重視した内容でした。(本書でもハンズオンでいくつかのアプリは作りますが、どちらかというと座学に近い印象)
変数・定数・配列といったかなり基本的なものからデリゲートなどの少し複雑なものまで例を挙げてしっかり解説されているので、プログラミング初心者はもちろん、プログラミング経験はあるがSwiftは初心者という人まで幅広く対応していると感じました。
前回の本と合わせて、Swiftの基礎概念とiPhoneアプリ作成の基本的な方法は一通り把握できた気がします。書籍概要
- 書籍名:絶対に挫折しないiPhoneアプリ開発「超」入門
- 著者 :高橋京介
- 発刊日:2019/05/10
- 頁数 :399ページ
読書ノート
Playground
XcodeのPlayground機能を使うと、生のSwiftを書いて色々試すことが出来る
- スライダー等のUIパーツを表示することもできる
PlaygroundではデフォルトではUIパーツのアニメーションを確認することはできない(UISliderのつまみの移動など)
- PlaygroundSupportというフレームワークを使うとアニメーションを確認できるようになる
変数・定数
Swiftでは変数宣言時に初期化することが推奨されている
Swiftでは変数名の大文字・小文字が区別される
Swiftでは変数名はキャメルケースで設定するのが慣例
- アッパーキャメルケース or ローワーキャメルケース
Swiftでは変数や定数の初期化時に型を自動判別してくれるので明示的に型を指定する必要はない(型推論)
- もちろん明示的に型を指定することもできる
- Swiftは静的型付け言語
ラベル
- 関数宣言時にラベルを設定すると、関数呼び出し時にラベルで引数を指定できる
- 関数内で処理する際には引数名を使う
- ラベルの設定を省略した場合でも、内部的に「引数名と同じ名前のラベル」が自動的に設定される
- 関数の宣言時にラベルにアンダースコアを指定すると、関数の呼び出し時にラベルを書けなくなる
// ラベルの指定を省略した場合 func areaOfTriangle(base:Int, height:Int) { print(base * height / 2) } areaOfTriangle(base:2, height:3)// ラベルを指定した場合 func areaOfTriangle(withBase base:Int, height height:Int) { print(base * height / 2) } areaOfTriangle(withBase:2, height:3)// ラベルにアンダースコアを指定した場合 func areaOfTriangle(_ base:Int, _ height:Int) { print(base * height / 2) } areaOfTriangle(2, 3)ドキュメントアウトライン
- ドキュメントアウトライン
- View Controller Scene:1画面に含まれる要素をまとめているフォルダ
- View Controller:ViewControllerクラス。この上にUIパーツを配置して画面レイアウトを作成する。コードもここに書く
- View:UIパーツを配置するための土台
- Safe Area:時刻表示エリアなど、他のUIパーツで隠れない範囲
- First Responder:ユーザーのアクションに最初に反応するクラスやインスタンス
- Exit:Storyboardのエディタエリアに複数の画面がある場合に使用する要素
- Storyboard Entry Point:最初に表示する画面を示す要素
ビュー
自身が乗っているビューのことを「スーパービュー」と呼び、自身が乗せているビューのことを「サブビュー」と呼ぶ
モーダルビュー
- 現在の画面の上に覆いかぶさって表示される一時的な画面のこと
- 背景がグレーアウトされて下の画面は操作不可能になる
- モーダルビューを操作するクラスを「モーダルビューコントローラ」と呼ぶ
UIパーツ
UIパーツは全てクラスとして実装されている
UIパーツは階層構造になっている
UIパーツをコードを接続する際は、右クリックまたはControlキーを押しながらパーツをエディタにD&Dする
- ドキュメントアウトラインからD&Dしても良い
UIパーツとコードの接続
パーツとコードの接続の種類
- Outlet:UIパーツを「プロパティ」として接続する
- パーツの見た目をコードで変更したい場合に指定する
- Action:UIパーツを「メソッド」として接続する
- パーツに対して何かしらの操作が発生した際に特定処理を実行したい場合に指定する
Action接続の設定値
- Event:様々な操作に応じたイベントが登録されている
- Arguments:多くの場合は「None」を選択するが、パーツそのものを引数として渡したい場合は「Sender」を選択する
- 例えば、ボタンの見た目をメソッドの中で変更したい場合などに「Sender」を選択する
- 見た目を変更するには、Action接続に加えてOutlet接続も行えば良いが、いちいちそれをやるのは面倒
Storageの設定値
- WeakとStrongがあるが、これらは参照の強さを表している
Auto Layout
- Auto Layout
- StoryboardにUIパーツを配置する際、適切な位置に配置することで、Xcodeで自動的にAutoLayoutの制約を設定してくれる
- もちろん自分で細かく制約を指定することもできる
- 各メニューの説明
- Update Frames:制約に沿っていないUIパーツを自動的に制約に沿った場所に再配置する(サイズ変更含む)
- Embed in Stack:複数のUIパーツを選択した状態でクリックすると、水平または垂直方向に整列してくれる(方向は自動)
- Align:各UIパーツの整列に関する制約を設定する
- Add New Constraints:UIパーツのサイズや他パーツとの距離といった、間隔に関する制約を設定する
- Resolve Auto Layout Issues:まだ設定されていない制約が自動的に追加される
- 通常はUIパーツの配置後に「Add Missing Constraints」を選択すれば事足りる
- UIパーツが赤色の線で囲まれている場合、「制約設定が不十分である」ことを示している(垂直方向しか制約が設定されていない等)
- スーパービューではなく、同ビュー上の別UIパーツとの位置関係の制約を設定することも出来る
- Controlキーを押しながら一方のUIパーツをもう一方のパーツにD&Dする
- 現状のXcodeでは、「Add Missing Constraints」を選択した際に余計な制約が付加されるので、手動で削除する
継承
- Swiftでの継承の記述方法
class (子クラス): (親クラス) { ... }
- 例:
class ViewController: UIViewController { ... }
ビューコントローラ
ビューコントローラ
- ビュー:テレビゲームで言うところの「ディスプレイ」のようなもの
- 実体はビューコントローラー(ViewController)のプロパティとして実装されている
- コントローラ:テレビゲームで言うところのコントローラ(そのまま)
- ビューコントローラは一つの画面に対して一つが基本
- 両者が分離されていることで、どちらか片方を変えてももう片方はそのまま使えるというメリットが生まれる
UIViewControllerクラスのviewプロパティにコードでUIパーツを追加することも可能だが、Storyboardを使う方がベター
ViewControllerクラスは、iOSが裏側で使っているので、使用するためのコードを書く必要は特にない
override・super・self
override
- 親クラスのメソッドを子クラスで上書きする機能
- 「メソッド名・引数の型・引数の数・引数名が同じ」である必要がある
- JavaなどのOverrideと比べて、引数名も同じでなければならないので注意
super
- 親クラスのメソッドの「一部分だけ」を子クラスで上書きする機能
self
- superと対になるキーワードで、「自分自身のインスタンス」を表している
viewDidLoad()
- アプリの初回起動時(メモリ上にロードされた時)に実行したい処理を記述する
- 例:Storyboardで作成した画面レイアウトをコード上で微調整する場合など
- ここにデフォルトで書かれている
super.viewDidLoad()
は上述したsuperの使い方の一例オプショナル型
- オプショナル整数型
- オプショナル整数型と通常の整数型はともに数値を表す型ではあるが別物なので、そのまま計算などは出来ない
- 変数名の後に「!」を付けてオプショナル整数型から整数型の値だけを取り出せば処理可能
- 変数にnilが代入されている状態で「!」を付けると実行時にアプリがクラッシュするので注意
- 他の型(StringやUISliderなど)のオプショナルについても同様
// エラーになる var age:Int? = 25 // オプショナル整数型 print(age + 1) // オプショナル整数型の変数に、整数型の値を足そうとしているのでエラーになる// エラーにならない var age:Int? = 25 print(age! + 1)
- オプショナル型変数の型を通常の方に戻す時に「!」を省略する書式
- 変数を初期化する際、変数名の後ろに「?」ではなく「!」を記述する
- ただし、この書き方は「変数にnilが入っていないことが保証されている」状況でしか使ってはいけない
// エラーにならない var age:Int! = 25 print(age + 1)
変数にnilを代入する
var (変数名):(型名)? = nil
オプショナル型変数の宣言時、初期化しない(初期値を代入しない)場合、自動的にnilが代入される
イニシャライザ・デフォルトイニシャライザ
イニシャライザ
- クラスのインスタンス生成時に初期値を強制的に設定する機能
- 他言語でいうところの「コンストラクタ」のこと
- 構文:
init( (ラベル1) (引数名1):(型), (ラベル2) (引数名2):(型) ... ) { ... }
デフォルトイニシャライザ
- クラス内の全てのプロパティに初期値が設定されている場合にSwiftが自動的に生成してくれるイニシャライザ
- コードを書く必要はないが、内部的には自動的に
init() {}
メソッドが追加されている- 他言語でいうところの「デフォルトコンストラクタ」のこと
クロージャ
- クロージャ
- 文の中に直接埋め込むことが出来る命令のかたまり
// 引数と戻り値があるクロージャの例 { (base:Int, height:Int) -> Int in let area = base * height / 2 return area }プロトコル
- プロトコル
- 他言語でいうところの「インタフェース」
- 宣言方法:
protocol (プロトコル名) { ... }
- プロトコル内にはメソッドやプロパティを書くが、メソッドの中身は書かない
protocol sample { func method() }
- プロトコルの使い方
- 宣言後、クラスがプロトコルに批准(参加すること)して始めて意味を成す
class (クラス名):(プロトコル名) { ... }
- プロトコルに批准したクラスでは、批准したプロトコルで定義されているメソッドを実装する必要がある
- プロトコル宣言時に
@objc optional
を付けると、必ずしも実装しなくて良いメソッドを定義できるprotocol sample { @objc optional func method() }
- 型としてのプロトコル
- 基本的にはAppleが提供しているプロトコルを使えば良く、自分でオリジナルのプロトコルを作ることはほぼない
- プロトコルには型としての機能があり、変数宣言時にプロトコルを指定すると、その変数にはそのプロトコルを批准しているクラスしか格納できなくなる
var (変数名):(プロトコル名) = (クラス名)()
- (変数名)には、(プロトコル名)を批准した(クラス名)しか格納できない
- 実装段階でエラーを見つけられるので、より安全なコードを書くことができる
- 複数のプロトコルを指定した場合は「&」で繋ぐ(全て批准しているクラスしか格納できなくなる)
var sampleVar:protocol1 & protocol2 ... = sampleClass()
- 親クラスとプロトコル
- 継承させる場合、クラス宣言時に親クラスを最初に書き、その後ろにプロトコル名を書く(複数可能)
class childClass:superClass, protocol1, protocol2 ... { ... }
列挙体
- 列挙体(Enum)
- Swiftに用意されていない型を独自に定義するための機能
- Enum名はアッパーキャメルケース、要素名はローワーキャメルケースで書くのが慣例
- 列挙体の値の取得:
var (変数名) = (列挙体名).(要素名)
extension
を使うと、既存の型に新しい機能を追加することができる
- また、
extension
には、「コードを機能別に分けることでコード管理をしやすくする」という役割もある- 列挙体の定義時に「: Int」を付与すると、列挙体の各要素に自動的に整数の番号が割り振られる
public enum Sample : Int { ... }
- 各要素に割り振られた値は、
(要素名).rawValue
プロパティで取得できる型メソッド
- 型メソッド
- 他言語でいうところの「クラスメソッド」のこと
- インスタンスを生成することなく、直接クラスから呼び出すことができるメソッドのこと(例:デバイスにカメラがあるか調べる)
- 厳密にはクラスだけでなく、列挙体や構造体でも使用できる
- インスタンスを生成した後にしか実行できないメソッドは「インスタンスメソッド」と呼ばれることもある
- クラス内のメソッドを型メソッドにする場合、メソッドの先頭に
class
を付与する
- 列挙体や構造体の場合は
static
を付与するclass Sample { class func method() -> Bool { ... } } Sample.method()デリゲート
- デリゲート
- あるクラスだけでは処理できない命令を、そのクラスの代わりに実行するクラスのこと(代理人のようなイメージ)
- 登場人物(例:弁護を行う場合)
- 被告人(Defenderクラス)
class Defender { // 弁護士は途中で変わる可能性もあるのでletではなくvarで定義 // また、弁護士を付けずに自分自身で弁護する(代理人がnilになる)可能性もあるのでオプショナルで定義 var delegate:Lawyer? }
- 弁護士(Lawyerクラス)※ここがデリゲート
class Lawyer { func defend() { print("異議あり") } }
- 裁判を行う場合
// Defender自身に弁護能力がなくても、代理人を介して弁護(defend)メソッドを呼び出せる let taro = Defender() taro.delegate = Lawyer() taro.delegate!.defend()
- デリゲート(弁護士)は必ずプロトコルを批准しなければならない
protocol LawyerLicense { func defend() }
- これを踏まえて元に登場人物のクラスを書き換える
- 被告人(Defenderクラス)
class Defender { // delegateにはLawyerLicenseプロトコルを批准したクラスしか格納できなくなる var delegate:LawyerLicense? }
- 弁護士(Lawyerクラス)※ここがデリゲート
// LawyerLicenseプロトコルを批准する class Lawyer:LawyerLicense { // プロトコルで定義されているメソッドの実装 func defend() { print("異議あり") } }
型キャスティング
- 型キャスティング
- 現在の変数の型を別の型に変換すること
- Any型の変数で受け取った値を、安全性を保つために値に適した型に変換するなど
- 型キャスティングの種類
- 階層が上のクラス(親側)の型に変換すること:アップキャスティング
- 階層が下のクラス(子側)の型に変換すること:ダウンキャスティング
- どちらの方向にキャスティングした場合でも、変数内のインスタンス自体は変わらない
- 型キャスティングは継承関係内でしか実行できない
- アップキャスティングは常に成功するが、ダウンキャスティングはそうとは限らない
- ダウンキャスティングを実行する際は
as!
演算子を使い、ダウンキャスティングであることを明示する
as!
演算子を使ったにも関わらず不適切なダウンキャスティングを行った場合、実行時にクラッシュするStoryboard
アプリ起動時にどのStoryboardを表示するかは、「General → Main Interface」項目で決定する
1つのStoryboard内に複数の画面がある場合、最初の開く画面を「Is Initial View Controller」で設定する
テーブルビュー
- テーブルビュー関連
- indexPathクラスのインスタンスには、tableView()メソッドが現在設定を行っているセルのセクション番号と行番号が保持される
dequeueReusableCell()
メソッドを使うと、自動的にセルの再利用を行える- ViewControllerクラスでUITableViewDataSourceプロトコルを批准していないのに問題がない理由
- ViewControllerクラスではUITableViewControllerクラスを親に持つ
- その親クラスがUITableViewDataSourceプロトコルを批准しており、かつプロトコルの必須メソッドを実装しているので問題ない
- 最終的にViewControllerクラスでは、その必須メソッドをoverrideして上書きして実装している
オプショナルバインディング・アンラップ
- オプショナルバインディング(アンラップ)
if let (定数) = (オプショナル型の値) { ... }
- オプショナル型の値がnil以外であれば
{}
内の処理が実行されるSegue
- Segue
- Storyboard上にある矢印のことで、画面遷移を表している
- Selection SegueとAccessory Actionがあり、それぞれ「セルがタップされた時」「アクセサリービューがタップされた時」に動作する
- アクセサリービュー:セルの右側に表示されるビュー(矢印アイコンやインフォーメーションアイコンなど)
- 基本的にはStoryboardにSegueを設定するだけで画面遷移を実装できるが、「画面間で値のやり取りをしたい場合」は独自の実装が必要
prepare()
メソッドにコードを書くアイコン・起動画面
アプリアイコン
- 登録できる画像はPNGのみ
- 色々なサイズのアイコン画像が必要だが、一枚の画像からそれらを生成してくれるWebサービスなどもあるので活用する
- アイコンの角丸処理はiPhone側が自動的に行なってくれるので、普通の正方形のファイルを登録すれば良い
起動画面
- アプリアイコンをクリックしてからアプリ画面が開くまでの間を繋ぐ画面
- 初期画面に近い画像または、アプリのロゴや会社のロゴなどが入った画像を指定するのが一般的
- Appleは前者を推奨している(ただし誤操作を防ぐためにボタン類は全て非表示にしておくのがベター)
情報の調べ方
- 何らかの機能を実現したいと考えた際の調べ方
- 前提として、Appleがフレームワークを提供していない場合、実現は難しい(特にハードウェア操作系)
- まずは Apple Developer でドキュメントやチュートリアルを調べるのが手っ取り早い
- Apple Developer Forums などを見るのも良い(デベロッパプログラム登録者のみ)
- 一応日本語ドキュメントもある 日本語ドキュメント - Apple Developer)
- Xcode内蔵の「Developer Documentationを見るのも良い
- 日本語ドキュメントをググるのも有り
- 最後に公式APIリファレンスで詳細情報を確認する
その他
アセットカタログ
- イメージセットをまとめているフォルダ
- イメージセット:各デバイスのディスプレイごとの画像を登録しておく機能
Any型
- 「どんな型でも」という意味で、「iOSが扱うことのできる全てのデータ(インスタンス)に対応する型」のこと