Skip to content

Instantly share code, notes, and snippets.

@ohtsuchi
Last active September 5, 2018 02:10
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ohtsuchi/3964f9edaefacea581856867a1dbc8b5 to your computer and use it in GitHub Desktop.
Save ohtsuchi/3964f9edaefacea581856867a1dbc8b5 to your computer and use it in GitHub Desktop.
iOSアプリ開発勉強会 2018-08-18

iOSアプリ開発勉強会 2018-08-18

内容

  • 某勉強会 で 教わった内容 の 復習用まとめ
  • 講師
    • 馬谷 さん

目標

  • Main.storyboard ではなく, xib ファイル上で 画面レイアウト する
  • エンドユーザが画面を操作した時のアクション(@IBAction) を 実装 する
  • ロジックを共通化 するために メソッド を 定義 する
  • Navigation Controller を 利用して 画面遷移 する
  • 遷移元画面 から 遷移先画面 へ データ の 受け渡し を 実装 する

1. Project 作成

  • File - New - Project を 【選択】
    • Single View App を 【選択】
    • Next
      • Product Name: 任意の名前 を 【入力】
      • Next
        • 任意のディレクトリ を 指定
        • Create

2. 新規 UIViewController 作成. TopViewController.swift

  • Cmd + N
    • Cocoa Touch Class を 【選択】
    • Next
      • Class: TopViewController と 【入力】
      • Subclass of: UIViewController のまま
      • Also create XIB file に チェック
      • Next
        • 任意のディレクトリ を 指定
        • Create

3. TopViewController.swift コード 編集

  • didReceiveMemoryWarning() は 【削除】
  • extension TopViewController {} を 3個 【追加】
    • MARK: - 付きの コメント を 4個 【追加】
  • viewDidLoad()
    • MARK: - Life Cycle 内 に 【移動】
import UIKit
// MARK: - Property
class TopViewController: UIViewController {
}
// MARK: - Life Cycle
extension TopViewController {
  override func viewDidLoad() {
    super.viewDidLoad()
  }
}
// MARK: - Protocol
extension TopViewController {
}
// MARK: - Method
extension TopViewController {
}
import UIKit
+// MARK: - Property
class TopViewController: UIViewController {
-
-    override func viewDidLoad() {
-        super.viewDidLoad()
-
-        // Do any additional setup after loading the view.
-    }
-
-    override func didReceiveMemoryWarning() {
-        super.didReceiveMemoryWarning()
-        // Dispose of any resources that can be recreated.
-    }
-    
-
-    /*
-    // MARK: - Navigation
-
-    // In a storyboard-based application, you will often want to do a little preparation before navigation
-    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
-        // Get the new view controller using segue.destinationViewController.
-        // Pass the selected object to the new view controller.
-    }
-    */
-
}
+// MARK: - Life Cycle
+extension TopViewController {
+  override func viewDidLoad() {
+    super.viewDidLoad()
+  }
+}
+// MARK: - Protocol
+extension TopViewController {
+}
+// MARK: - Method
+extension TopViewController {
+}
  • Ctrl + 6 を 押して class 内の構造 を 確認

4. TopViewController.xib 画面レイアウト 編集

  • TopViewController.xib を 【ファイル選択】
  • 部品 を 配置
    • Label 【追加】
    • Button 【追加】

5. Main.storyboard から 起動される UIViewController を 変更

Cmd + R で 動作確認 (Label と Button が 表示される事の確認)

 


6. TopViewController.xib 画面レイアウト, TopViewController.swift コード 編集

  • TopViewController.xib を 【ファイル選択】
  • Alt + Cmd + Enter で 【アシスタント エディタ 起動】
    • (Cmd + Enter で アシスタント エディタ 閉じる)
  • Label 【選択】
    • Ctrl + マウスドラッグ -> コード 内に
      • Connection: Outlet のまま
      • Name: label1 と 【入力】
      • Connect 【押下】
  • Button 【選択】
    • Ctrl + マウスドラッグ -> コード 内に
      • Connection: Action に 【変更】
      • Name: touchButton と 【入力】
      • Type: Any -> UIButton に 【変更】
      • Connect 【押下】
  @IBOutlet weak var label1: UILabel!
  @IBAction func touchButton(_ sender: UIButton) {
  }
import UIKit
// MARK: - Property
class TopViewController: UIViewController {
+  @IBOutlet weak var label1: UILabel!
+  @IBAction func touchButton(_ sender: UIButton) {
+  }
}
// MARK: - Life Cycle
extension TopViewController {
  override func viewDidLoad() {
    super.viewDidLoad()
  }
}
// MARK: - Protocol
extension TopViewController {
}
// MARK: - Method
extension TopViewController {
}

7. TopViewController.swift コード 編集. Label の 文字色 を 変更する ロジック を 追加

  • Button が タップされたら Label の 文字色 を 変更
  @IBAction func touchButton(_ sender: UIButton) {
    label1.textColor = UIColor.red
  }
// MARK: - Property
class TopViewController: UIViewController {
  @IBOutlet weak var label1: UILabel!
  @IBAction func touchButton(_ sender: UIButton) {
+    label1.textColor = UIColor.red
  }
}
// (以下略)

Cmd + R で 動作確認 (Button を押した時に Label の テキスト の 色 が 変わる 事の確認)

8. TopViewController.swift コード 編集. Label の 文字色 を 変更する メソッド を 定義

  • メソッド を定義して, ロジックを共通化 する練習をする
  • メソッド の シグニチャ は 以下の仕様 とする
    • 英語文型: SVOCOC の 部分
  func setColor(label: UILabel, color: UIColor) {
    // TODO 実装
  }
import UIKit
// MARK: - Property
class TopViewController: UIViewController {
  @IBOutlet weak var label1: UILabel!
  @IBAction func touchButton(_ sender: UIButton) {
-    label1.textColor = UIColor.red
+    setColor(label: label1, color: UIColor.brown)
  }
}
// MARK: - Life Cycle
extension TopViewController {
  override func viewDidLoad() {
    super.viewDidLoad()
  }
}
// MARK: - Protocol
extension TopViewController {
}
// MARK: - Method
extension TopViewController {
+  func setColor(label: UILabel, color: UIColor) {
+    label.textColor = color
+  }
}
  func setColor(label: UILabel, color: UIColor) {
    label.textColor = color
  }

Cmd + R で 動作確認 (Button を押した時に Label の テキスト の 色 が 変わる 事の確認)

 


9. Main.storyboard から 起動される UIViewController に Navigation Controller を 追加

  • Main.storyboard を 【ファイル選択】
  • Editor - Embed in - Navigation Controller を 【選択】
    • Navigation Controller の Is Initial View Controller に チェック が 入っている 事を確認
    • Top View Controller の Is Initial View Controller の チェック が 外れている 事を確認

10. 新規 UIViewController 作成. 画面遷移用. SecondViewController.swift

  • 手順 2. と 以下の項目 以外 は 同じ
    • Class: SecondViewController と 【入力】
import UIKit
// MARK: - Property
class SecondViewController: UIViewController {
}
// MARK: - Life Cycle
extension SecondViewController {
  override func viewDidLoad() {
    super.viewDidLoad()
  }
}
// MARK: - Protocol
extension SecondViewController {
}
// MARK: - Method
extension SecondViewController {
}
  • 部品 を 追加
    • Label 2個 【追加】
      • nameLabel
      • ageLabel
// MARK: - Property
class SecondViewController: UIViewController {
+  @IBOutlet weak var nameLabel: UILabel!
+  @IBOutlet weak var ageLabel: UILabel!
}
// (以下略)

11. 遷移元(TopViewController) に 画面遷移 の ロジック 追加

  • ↑で定義した setColor() を削除
  • navigationController を 利用して 画面遷移 する ロジック を 追加

TopViewController.swift

  @IBAction func touchButton(_ sender: UIButton) {
    let nextVC = SecondViewController()
    navigationController?.pushViewController(nextVC, animated: true)
  }
// MARK: - Property
class TopViewController: UIViewController {
  @IBOutlet weak var label1: UILabel!
  @IBAction func touchButton(_ sender: UIButton) {
-    setColor(label: label1, color: UIColor.brown)
+    let nextVC = SecondViewController()
+    navigationController?.pushViewController(nextVC, animated: true)
  }
}
// (中略)
```diff
// MARK: - Method
extension TopViewController {
-  func setColor(label: UILabel, color: UIColor) {
-    label.textColor = color
-  }
}

Cmd + R で 動作確認 (Button を押した時に 画面遷移 する 事の確認)

画面遷移 ロジック を メソッド外出し

TopViewController.swift

  func transitionVC(to: UIViewController)  {
    navigationController?.pushViewController(to, animated: true)
  }
class TopViewController: UIViewController {
  @IBOutlet weak var label1: UILabel!
  @IBAction func touchButton(_ sender: UIButton) {
    let nextVC = SecondViewController()
-    navigationController?.pushViewController(nextVC, animated: true)
+    transitionVC(to: nextVC)
  }
}
// (中略)
// MARK: - Method
extension TopViewController {
+  func transitionVC(to: UIViewController)  {
+    navigationController?.pushViewController(to, animated: true)
+  }
}

Cmd + R で 動作確認 (Button を押した時に 画面遷移 する 事の確認)

 


12. Model 新規作成. UserModel.swift

  • Controller 間の データの受け渡し 用の Model クラスを定義
  • 手順 2. と 以下の項目 以外 は 同じ
    • Class: UserModel と 【入力】
    • Subclass of: UIViewController -> NSObject に 【変更】
import UIKit

class UserModel: NSObject {
  var name: String?
  var age: Int?
}

13. 遷移元(TopViewController), 遷移先(SecondViewController) の Property に UserModel を 追加

  • varUserModel 型 の プロパティ を 定義

TopViewController.swift

// MARK: - Property
class TopViewController: UIViewController {
  @IBOutlet weak var label1: UILabel!
  @IBAction func touchButton(_ sender: UIButton) {
    let nextVC = SecondViewController()
    transitionVC(to: nextVC)
  }
+  var userModel: UserModel = UserModel()
}
// (以下略)

SecondViewController.swift

// MARK: - Property
class SecondViewController: UIViewController {
  @IBOutlet weak var nameLabel: UILabel!
  @IBOutlet weak var ageLabel: UILabel!
+  var userModel: UserModel = UserModel()
}
// (以下略)

14. 遷移元(TopViewController) から 遷移先(SecondViewController) に データ受け渡し

  • 遷移元(TopViewController) の
    • viewDidLoad()
      • 【1】 UserModel に データ(固定値) を セット
    • ボタン の タップ イベント(touchButton()) で
      • 【2】 UserModel を 遷移先(SecondViewController) の Property に セット
        • UserModel参照型(class) のため, この時点で
          • 遷移元(TopViewController) の userModel Property と
          • 遷移先(SecondViewController) の userModel Property は
            • 同じ instance を 参照 するようになる
  • 遷移先(SecondViewController) の
    • viewDidLoad()
      • 【3】 UserModel から データ を 受け取り
      • 【3】 Label に 表示

TopViewController.swift

// MARK: - Property
class TopViewController: UIViewController {
  @IBOutlet weak var label1: UILabel!
  @IBAction func touchButton(_ sender: UIButton) {
    let nextVC = SecondViewController()
+    nextVC.userModel = userModel // 【2】
    transitionVC(to: nextVC)
  }
  var userModel: UserModel = UserModel()
}
// (中略)
// MARK: - Life Cycle
extension TopViewController {
  override func viewDidLoad() {
    super.viewDidLoad()
+    userModel.name = "xxxxx" // 【1】
+    userModel.age = 100      // 【1】
  }
}
// (以下略)

SecondViewController.swift

// MARK: - Life Cycle
extension SecondViewController {
  override func viewDidLoad() {
    super.viewDidLoad()
+    nameLabel.text = userModel.name // 【3】
+    ageLabel.text = userModel.age   // 【3】
  }
}
  • ageLabel.text = userModel.age
  • ↑ の 1行 が コンパイルエラー
    • 代入先/代入元 の 型が異なる ため
      • ageLabel.textString?
      • userModel.ageInt?
  • Int? -> String?, または Int? -> String の 型変換 が 必要
    • 参考: Int -> String への 型変換 の コード例 ↓
      • let a = String(11)
        • Int 型の値(この場合は11) を 引数 に取る String の initializer を 呼ぶ
        • 変数 aString 型 となる

変更(まだコンパイルエラー)

    ageLabel.text = String(userModel.age)
   // Cannot invoke initializer for type 'String' with an argument list of type '(Int?)'
  • userModel.age の 型 が Int? のため, String の initializer の 引数 には渡せない
    • unwrap して Int? -> Int 型 に 変換してから 引数 に渡す

解決案

    if let age = userModel.age {   //   `userModel.age` は `Int?` 型
      ageLabel.text = String(age)  // if let 内の `age` は `Int`  型
    }

SecondViewController.swift

// MARK: - Life Cycle
extension SecondViewController {
  override func viewDidLoad() {
    super.viewDidLoad()
+    nameLabel.text = userModel.name
+    if let age = userModel.age {
+      ageLabel.text = String(age)
+    }
  }
}

Cmd + R で 動作確認 (画面遷移先 の 画面 に UserModel の値が表示される 事の確認)

 


Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment