storyboardを使わずに姓名診断アプリを作る
ドットインストールの以下のレッスン
を、storyboardを使わずにswiftのみで作ってみる。
storyboardを使わないプロジェクト作成
これ参照
画面に必要な部品
姓名診断画面
診断結果画面
NavigationControllerを設置してタイトルを表示
参考
012 UINavigationControllerの表示 - Swift Docs
コード
AppDelegate.swift 抜粋
var window: UIWindow? var myNavigaionController: UINavigationController? func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: bject]?) -> Bool { // ViewControllerを生成 let myFirstViewController: ViewController = ViewController() // Navigation Controllerを生成 myNavigaionController = UINavigationController(rootViewController: myFirstViewController) self.window = UIWindow(frame: UIScreen.mainScreen().bounds) self.window?.backgroundColor = UIColor.whiteColor() // Navigation ControllerをrootViewControllerに設定 self.window?.rootViewController = myFirstViewController self.window?.makeKeyAndVisible() return true }
ViewController.swift(姓名診断画面) 抜粋
override func viewDidLoad() { super.viewDidLoad() // タイトルを設定する self.title = "姓名診断" }
実行結果
ViewControllerの追加と部品の配置
参考
- 012 UINavigationControllerの表示 - Swift Docs
- 004 UITextFieldで文字を入力 - Swift Docs
- 001 UILabelで文字を表示 - Swift Docs
- 002 UIButtonでボタンを表示 - Swift Docs
- UITextfieldで文字を編集する - Swiftサラリーマン
コード
ViewController.swift(姓名診断画面)
import UIKit class ViewController: UIViewController, UITextFieldDelegate { let sendButton: UIButton = UIButton() override func viewDidLoad() { super.viewDidLoad() // Controllerのタイトルを設定する self.title = "姓名診断" // UITextFieldを作成する let name: UITextField = UITextField(frame: CGRectMake(0, 0, 300, 30)) // 最初に表示する文字を設定 name.text = "名前を入力" // Delegateを設定する name.delegate = self // 枠を表示する name.borderStyle = UITextBorderStyle.RoundedRect // UITextFieldの表示する位置を設定 // name.layer.position = CGPoint(x: self.view.frame.width/2, y: 150) name.layer.position = CGPoint(x: 160, y: 150) // Viwに追加する self.view.addSubview(name) // 送信ボタンを設定する sendButton.frame = CGRectMake(0, 0, 50, 40) // 背景色を設定する sendButton.backgroundColor = UIColor.lightGrayColor() sendButton.showsTouchWhenHighlighted = true // 枠を丸くする sendButton.layer.masksToBounds = true // タイトルを設定する sendButton.setTitle("Send", forState: UIControlState.Normal) sendButton.setTitleColor(UIColor.blackColor(), forState: UIControlState.Normal) // コーナーの半径を設定する sendButton.layer.cornerRadius = 10.0 // ボタンの位置を指定する sendButton.layer.position = CGPoint(x: 340, y: 150) // タグを設定する sendButton.tag = 1 // イベントを追加する sendButton.addTarget(self, action: "showResult:", forControlEvents: .TouchUpInside) // ボタンをViewに追加する self.view.addSubview(sendButton) } /* ボタンイベント */ func showResult(sender: UIButton) { // 移動先のViewを定義する let resultViewController = ResultViewController() // ResultViewに移動する self.navigationController?.pushViewController(resultViewController, animated: true) } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() // Dispose of any resources that can be recreated. } }
ResultViewController(診断結果画面)
import UIKit class ResultViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() // Controllerのタイトルを設定 self.title = "診断結果" // Labelを作成 let label: UILabel = UILabel(frame: CGRectMake(0, 0, 200, 40)) // 背景色を付ける label.backgroundColor = UIColor.lightGrayColor() // 文字を代入 label.text = "あなたの点数は…" // テキストを中央寄せにする label.textAlignment = NSTextAlignment.Center // ラベルを配置する座標を設定する label.layer.position = CGPoint(x: self.view.bounds.width / 2, y: 200) // ViewにLabelを追加 self.view.addSubview(label) // 診断結果表示Labelを作成 let resultLabel: UILabel = UILabel(frame: CGRectMake(0, 0, 200, 80)) // 枠線を付ける resultLabel.layer.borderColor = UIColor.redColor().CGColor // 枠線の太さ resultLabel.layer.borderWidth = 2.0 // 文字を代入 resultLabel.text = "100点" // テキストを中央寄せ resultLabel.textAlignment = NSTextAlignment.Center // フォントサイズを大きくして太字に resultLabel.font = UIFont.boldSystemFontOfSize(64) // ラベルを配置する座標を設定する resultLabel.layer.position = CGPoint(x: self.view.bounds.width / 2, y: 300) // ViewにLabelを追加 self.view.addSubview(resultLabel ) } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() } }
実行結果
ここまでで、
Send
ボタンをクリックして診断結果画面に遷移- 診断結果画面から姓名診断画面に遷移
は出来た
姓名診断のText Fieldで入力した値のチェック・値を診断結果画面に表示・点数を表示
参考
010 UIAlertControllerでアラートを表示 - Swift Docs
ここではまってしまった。。。
segueを使わないで画面間で値を受け渡すやり方がわからずに2時間くらい悩んでいた。
名前受け渡し用の変数を作り、そこに値をセットするようにしたら出来たが、これでいいのか・・
コード
ViewController.swift(姓名診断画面)
import UIKit class ViewController: UIViewController, UITextFieldDelegate { let sendButton: UIButton = UIButton() var nameField: UITextField! override func viewDidLoad() { super.viewDidLoad() // Controllerのタイトルを設定する self.title = "姓名診断" // UITextFieldを作成する nameField = UITextField(frame: CGRectMake(0, 0, 300, 30)) // 最初に表示する文字を設定 nameField.placeholder = "名前を入力" // Delegateを設定する nameField.delegate = self // 枠を表示する nameField.borderStyle = UITextBorderStyle.RoundedRect // UITextFieldの表示する位置を設定 nameField.layer.position = CGPoint(x: 160, y: 150) // Viwに追加する self.view.addSubview(nameField) // 送信ボタンを設定する sendButton.frame = CGRectMake(0, 0, 50, 40) // 背景色を設定する sendButton.backgroundColor = UIColor.lightGrayColor() sendButton.showsTouchWhenHighlighted = true // 枠を丸くする sendButton.layer.masksToBounds = true // タイトルを設定する sendButton.setTitle("Send", forState: .Normal) sendButton.setTitleColor(UIColor.blackColor(), forState: .Normal) // コーナーの半径を設定する sendButton.layer.cornerRadius = 10.0 // ボタンの位置を指定する sendButton.layer.position = CGPoint(x: 340, y: 150) // タグを設定する sendButton.tag = 1 // イベントを追加する sendButton.addTarget(self, action: "showResult:", forControlEvents: .TouchUpInside) // ボタンをViewに追加する self.view.addSubview(sendButton) } /* ボタンイベント */ func showResult(sender: UIButton) { // 空文字チェック if nameField.text == "" { showAlert() } else { // 移動先のViewを定義する let resultViewController = ResultViewController() // ResultViewに移動する self.navigationController?.pushViewController(resultViewController, animated: true) // TODO これで渡せるな・・? resultViewController.myName = nameField.text } } /* エラーアラートを表示 */ func showAlert() { // UIAlertControllerを作成する let myAlert = UIAlertController(title: "エラー", message: "診断したい名前を入力してください", preferredStyle: .Alert) // OKのアクションを作成する let myOkAction = UIAlertAction(title: "OK", style: .Default, handler: nil) // OKのアクションを追加する myAlert.addAction(myOkAction) // UIAlertを発動する presentViewController(myAlert, animated: true, completion: nil) } /* 改行ボタンが押された際に呼ばれる */ func textFieldShouldReturn(textField: UITextField) -> Bool { // returnを押すとキーボードが隠れる textField.resignFirstResponder() // sendButtonと同じ処理を sendButton.sendActionsForControlEvents(.TouchUpInside) return true } /* UITextFieldが編集された直後に呼ばれる */ func textFieldDidBeginEditing(textField: UITextField) { println("textFieldDidBeginEditing: \(textField.text)") } /* UITextFieldが編集終了する直前に呼ばれる */ func textFieldShouldEndEditing(textField: UITextField) -> Bool { println("textFieldShouldEndEditing: \(textField.text)") return true } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() // Dispose of any resources that can be recreated. } }
ResultViewController(診断結果画面)
import UIKit class ResultViewController: UIViewController { // 入力された名前を入れる var myName: String = "" override func viewDidLoad() { super.viewDidLoad() // Controllerのタイトルを設定 self.title = "診断結果" // Labelを作成 let label: UILabel = UILabel(frame: CGRectMake(0, 0, 200, 40)) // 背景色を付ける label.backgroundColor = UIColor.lightGrayColor() // 名前を代入 label.text = "\(myName)の点数は…" // テキストを中央寄せにする label.textAlignment = NSTextAlignment.Center // ラベルを配置する座標を設定する label.layer.position = CGPoint(x: self.view.bounds.width / 2, y: 200) // ViewにLabelを追加 self.view.addSubview(label) // 診断結果表示Labelを作成 let resultLabel: UILabel = UILabel(frame: CGRectMake(0, 0, 200, 80)) // 枠線を付ける resultLabel.layer.borderColor = UIColor.redColor().CGColor // 枠線の太さ resultLabel.layer.borderWidth = 2.0 // 点数を表示 let score = arc4random_uniform(101) resultLabel.text = "\(score)点" // テキストを中央寄せ resultLabel.textAlignment = NSTextAlignment.Center // フォントサイズを大きくして太字に resultLabel.font = UIFont.boldSystemFontOfSize(64) // ラベルを配置する座標を設定する resultLabel.layer.position = CGPoint(x: self.view.bounds.width / 2, y: 300) // ViewにLabelを追加 self.view.addSubview(resultLabel) } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() } }
ここまでで大体機能は出来たので、細かいところを変更
修正点
結果画面から姓名診断画面に戻った時に名前が入ったままなので消す
姓名診断画面を表示した時にテキストフィールドにフォーカスをセットする
参考
UITextField、UITextViewのフォーカス制御 - iOSアプリ開発トピック
ViewController.swift(姓名診断画面)
/* Viewが表示される直前に行う処理 */ override func viewWillAppear(animated: Bool) { super.viewWillAppear(animated) // 名前入力テキストフィールドを空にする nameField.text = "" // 名前入力テキストフィールドにフォーカスする nameField.becomeFirstResponder() }
キーボートのreturn
をSend
に変える
参考
UITextField/UITextViewの文字入力で覚えておくと便利なこと
ViewController.swift(姓名診断画面)
override func viewDidLoad() { // 〜省略〜 // キーボードのReturnキーをSendに変える nameField.returnKeyType = UIReturnKeyType.Send // Viwに追加する self.view.addSubview(nameField) // 〜省略〜 }
結果画面を表示したときに姓名診断画面の「テキストフィールドとボタン」が少しの間表示されているので消す
こんなやつ
ResultViewController(診断結果画面)
/* Viewが表示される直前に行う処理 */ override func viewWillAppear(animated: Bool) { super.viewWillAppear(animated) // TODO viewの背景色を設定 // これで前の画面の残像(テキストフィールドとボタン)は消えたけど・・・?? self.view.backgroundColor = UIColor.whiteColor() }
残像が消えたようだけどこれでいいのか・・・??
完成
画面
コード
やりたいことは出来ましたが、何だかスッキリしない(>_<)
storyboardを使わずにiOSでおみくじを作る
ドットインストールの以下のレッスン
を、storyboardを使わずにswiftのみで作ってみる。
storyboardを使わないプロジェクト作成
を参考にして作成。ありがとうございます。
Single View Application
を選択してプロジェクト作成- プロジェクトの設定ファイルの変更
Deployment Info
->Main Interface
の中のMain
削除
Main.storyboard
とLaunchScreen.xib
を削除AppDelegate.swift
のapplication
メソッドにコードを追加
- プロジェクトの設定ファイルの変更
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool { // ====↓追加===== self.window = UIWindow(frame: UIScreen.mainScreen().bounds) self.window?.backgroundColor = UIColor.whiteColor() // 背景白 self.window?.rootViewController = ViewController() // ViewControllerを指定 self.window?.makeKeyAndVisible() // ====↑追加==== return true }
画面に必要な部品
- ラベル1(あなたの運勢は・・・)
- ラベル2(フォント大きめ。おみくじの結果を表示)
- ボタン1(クリックすると占いを実行し、占い結果をラベルに表示)
ラベルとボタンを配置してみる
参考
001 UILabelで文字を表示 - Swift Docs
002 UIButtonでボタンを表示 - Swift Docs
感謝。
ボタンをクリックした時の処理を追加
ボタンをクリックした時にコンソールに出力するようにしてみる
// イベントを追加する divineButton.addTarget(self, action: "onClickDivineButton", forControlEvents: .TouchUpInside) /* 占うイベント */ func onClickDivineButton(sender: UIButton) { println("onClickDivineButton: ") println("sender.currentTitle: \(sender.currentTitle)") }
ボタンをクリックしてみるとエラーが(ヽ'ω`)・・・??
MyOmikujiSwiftOnly[12918:4911213] -[MyOmikujiSwiftOnly.ViewController onClickDivineButton]: unrecognized selector sent to instance 0x7fdba25bdeb0 2015-03-27 11:24:26.043 MyOmikujiSwiftOnly[12918:4911213] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[MyOmikujiSwiftOnly.ViewController onClickDivineButton]: unrecognized selector sent to instance 0x7fdba25bdeb0'
メソッド指定時にコロン:
が付いていなかった!!
// メソッド指定にコロンを追加 // イベントを追加する divineButton.addTarget(self, action: "onClickDivineButton:", forControlEvents: .TouchUpInside)
onClickDivineButton: sender.currentTitle: Optional("占った!")
成功!!
適当に装飾
- Labelの背景色
- 枠線、角丸、フォントサイズ
- ボタンの背景色、角丸
完成
ソースコード
はまったところ
- storyboardを使わずにプロジェクを作る方法がわからなかったので検索していくつか試した。時間かかった
- 乱数を取得し、その数でenumから値を引っ張ろうとしたがやり方がわからずなかなかうまくいかなかった
storyboard使わないと位置の調整とか大変そう。
使ったら使ったで、複雑になってくると大変なのかな。。。
enumとか上手く使えるようになりたいです。
少しずつ作っていってコツを掴んでいきたい所存です。
Swift入門メモ -ジェネリクス-
24 ジェネリクス
- 抽象化されたデータ型
- どのような型が来てもOK
<T>
->T
は何の型が来てもいいという意味
// ある整数を指定した個数分だけ集めた配列を返す関数 func getIntArray(item: Int, count: Int) -> [Int] { var result = [Int]() for _ in 0..<count { result.append(item) } return result } getIntArray(5, 5) // [5, 5, 5, 5, 5] /* getIntArrayを拡張してString型やFloat型にも対応させたい -> そのために使えるのがジェネリクス */ // 関数の後に型の名前を書く // 「T」は何の型が来てもいいという意味 func getArray<T>(item: T, count: Int) -> [T] { var result = [T]() for _ in 0..<count { result.append(item) } return result } // 整数型 getArray(3, 4) // [3, 3, 3, 3] // String型 getArray("EBC", 8) // ["EBC", "EBC", "EBC", "EBC", "EBC", "EBC", "EBC", "EBC"] // Float型 getArray(3.1419, 2) // [3.1419, 3.1419]
Javaにもこんなのあったような。。
一先ずドットインストール終了(^ν^)
Swift入門メモ -extension-
23 extensionで機能拡張
- 既存のデータ型、クラス、構造体を拡張する
String
,Int
の基本データ型以外にもクラス・構造体・列挙型にも使える
- 何らかの機能を追加したいときに使う
extension String { var size: Int { // 自身の文字数を返す return countElements(self) } func ebc() -> String { // 何が何でも"EBC!!!!"しか返さない return "EBC!!!!" } } var s: String = "hoge" s.size // 4 s.ebc() //EBC!!!! extension Int { func fjhss() -> Int { // 自身の数から12を引く(中学何年生?) return self - 12 } } var age: Int = 12 age.fjhss() // 0 age = 33 age.fjhss() // 21
Swift入門メモ -構造体-
22 構造体
構造体とは
クラスとの違い
- メソッドの中で自信のプロパティを書き換えることが出来ない
- 書き換える場合は明示的に指示
func
の前にmutating
- 継承が出来ない
- コピーするときの挙動が違う
- クラス:参照渡し
- メモリを効率的に使うため
- 構造体:値渡し
- わかりやすいが、大きな構造体だとコピーするたびにメモリを圧迫するので注意が必要
- クラス:参照渡し
struct UserStruct { var name: String var score: Int = 0 init(name: String) { self.name = name } mutating func upgrade() { score++ } } class User { var name: String var score: Int = 0 init(name: String) { self.name = name } func upgrade() { score++ } } // ====クラスのインスタンスコピー var hoge = User(name: "Hoge") var hoge2 = hoge // hoge2にはhogeの実体を指し示す参照先がコピーされる hoge2.name = "hoge2" // hoge2のnameプロパティ書き換え hoge.name // hoge2 に変わる // ※hogeとhoge2は全く同じデータを指し示している // ====構造体のインスタンスコピー var huga = UserStruct(name: "Huga") var huga2 = huga // huga2にはhugaの値をそのまま丸ごとコピーしている huga2.name = "huga2" // huga2のnameプロパティ書き換え huga.name // Huga のまま(hugaとhuga2は別物だから)
クラスと構造体の使い分け
- 継承が必要、大きなデータを扱う -> クラス
- シンプルなデータ -> 構造体
Swift入門メモ -get/set,willSet/didSet・Optionl Chaining・Type Casting-
19 get/set,willSet/didSet
get/set
- プロパティを動的に計算
- プロパティの値を取得する時と設定する時に計算式が書ける
class Student { var name: String var number: Int var attendance: Int = 0 var rate: Int { get { // selfは省略出来る return Int(Float(attendance) / 10.0 * 100) } set { // 渡された値を「newValue」でとることが出来る // attendanceの値を書き換える attendance = Int(newValue / 10) } } init(name: String, number: Int) { self.name = name self.number = number } func attended() { attendance++ } } var mayama = Student(name: "rika", number: 3) mayama.attendance // 0 mayama.rate // 0 mayama.attendance = 1 mayama.rate // 10 mayama.attendance = 3 mayama.rate // 30 // rateを更新してattendanceの値を変える mayama.rate = 100 mayama.attendance // 10 mayama.rate = 80 mayama.attendance // 8
- getしか使わない場合
class Student { var name: String var number: Int var attendance: Int = 0 var rate: Int { // getだけの場合はreturnを書くだけ return Int(Float(attendance) / 10.0 * 100) } init(name: String, number: Int) { self.name = name self.number = number } func attended() { attendance++ } }
willSet/didSet
- プロパティの状態監視
- 値が変わった時、変わる前と変わった後で何らかの処理を入れることができる
- プロパティが最初に初期化されるときには実行されない
class Student { var name: String var number: Int var attendance: Int = 0 { willSet { // 変わる前にしたい処理 // 新しい値は「newValue」 println("willSet: \(attendance) -> \(newValue)") } didSet { // 変わった後にしたい処理 // 古い値は「oldValue」 println("didSet: \(oldValue) -> \(attendance)") } } var rate: Int { return Int(Float(attendance) / 10.0 * 100) } init(name: String, number: Int) { self.name = name self.number = number } func attended() { attendance++ } } var mayama = Student(name: "rika", number: 3) mayama.attendance = 9 // willSet: 0 -> 9 / didSet: 0 -> 9 mayama.attendance = 10 // willSet: 9 -> 10 / didSet: 9 -> 10
20 Optionl Chaining
- クラスなどが複雑に入り組んでいた時に、プロパティやメソッドがあるか安全に確かめる方法
- 先頭から調べて行って、
nil
だった場合はそこで評価を止め、安全に終了させてnil
を返す - 複雑になると、
?
マークがたくさんついていく
class User { var blog: Blog? // Blog型のOptional } class Blog { var title = "My Blog" } var gawao = User() // ================ // ★blogプロパティが存在する場合は以下のように取れる // gawao.blog = Blog() // Unwrapしてtitleを取得 // gawao.blog!.title // My Blog // ================ // ★blogプロパティがnilの場合 「gawao.blog!.title」でエラーになる // Optional Chainingを使う gawao.blog?.title // nil // ================ // ★よく書くやり方 // titleがある場合はtを表示する // - nilの場合はエラーなく終了 // - gawao.blogがある場合は「My Blog」と表示される if let t = gawao.blog?.title { println(t) }
21 Type Casting
- クラスの型をチェックしたり、あるクラスを親クラス・子クラスとして扱うための方法
- クラスの型チェックは
is
as
を使ってあるクラスを子クラスに変換することをダウンキャストする
という- どのようなオブジェクトのインスタンスでもいいという特殊なデータ型がある
AnyObject
class User { var name: String init(name: String) { self.name = name } } class AdminUser: User {} let kashiwagi = User(name: "hinata") let matsuno = AdminUser(name: "rina") // クラスの型が違うがエラーにならない let users: [User] = [kashiwagi, matsuno] // クラスごとに処理を分けたいとき // 型チェック、変換に使える-> is / as for user in users { // AdminUserクラスか調べる->is if user is AdminUser { // UserクラスからAdminUserクラスにダウンキャスト->as let u = user as AdminUser println(u.name) // rina } }
// userをダウンキャスト->うまくいったらブロックの処理。うまくいかなかったらnilを返す if let u = user as? AdminUser { println(u.name) // rina } // ====↓の部分は↑のように書き換えることが出来る==== // // AdminUserクラスか調べる->is // if user is AdminUser { // // UserクラスからAdminUserクラスにダウンキャスト->as // let u = user as AdminUser // println(u.name) // rina // }
- 親子関係のない別のクラスのインスタンスを配列に含めたいとき
class User { var name: String init(name: String) { self.name = name } } class AdminUser: User {} class SomeUser {} let kashiwagi = User(name: "hinata") let matsuno = AdminUser(name: "rina") let nakayama = SomeUser() // 継承関係にないインスタンスが含まれているのでエラーになる // let users: [User] = [kashiwagi, matsuno, nakayama] // どのようなオブジェクトのインスタンスでもOKなAnyObjectを使う let users: [AnyObject] = [kashiwagi, matsuno, nakayama]
Swift入門メモ -クラス・クラスの継承・プロトコル-
16 クラスを使う
クラスとは
- 関連のある変数や関数をまとめて管理出来る
- クラスの中の変数->
プロパティ
- クラスの中の関数->
メソッド
- クラスを使う時は
インスタンス
というデータの実体を作る
クラスを作る
- クラスのプロパティは初期化されるかOptionalになる必要があるので、以下はエラーになる
class Student { var name: String // TODO 初期化されていない var number: Int // TODO 初期化されていない var appear: Int = 0 func sayHello() { println("出席番号 \(number), \(name) say hello") } func update() { appear++ } }
- イニシャライザーを作って初期化
- インスタンス化してプロパティ等にアクセス
class Student { var name: String var number: Int var appear: Int = 0 // イニシャライザーを作る init (name: String, number: Int) { // selfはそのクラスのインスタンス self.name = name self.number = number } func sayHello() { println("出席番号 \(number), \(name) say hello") } func update() { appear++ } } // インスタンス化 var yasumoto = Student(name: "ayaka", number: 5) yasumoto.name // ayaka yasumoto.number // 5 yasumoto.sayHello() // 出席番号 5, ayaka say hello yasumoto.appear // 0 yasumoto.update() yasumoto.appear // 1
17 クラスの継承
継承とは
- あるクラスを元に別のクラスを作る
できること
- 親クラスで定義されたプロパティ、メソッドを全部使うことが出来る
- 親クラスのメソッドを
override
できる override
を禁止することが出来る- 親クラスのメソッドの前に
final
を付ける
- 親クラスのメソッドの前に
- 親クラスのメソッド、プロパティを呼ぶことが出来る
super
を付ける
class Student { var name: String var number: Int var appear: Int = 0 // イニシャライザーを作る init (name: String, number: Int) { // selfはそのクラスのインスタンス self.name = name self.number = number } func sayHello() { println("出席番号 \(number), \(name) say hello") } // override禁止 / final final func update() { appear++ } } // Studentクラスを継承 class ExchangeStudent: Student { func reset() { appear = 0 } // 親クラスのメソッドを上書き override func sayHello() { super.sayHello() // 親クラスのメソッドを呼ぶ println("出席番号 \(number) は永久欠番です!!") } } var yasumoto = Student(name: "ayaka", number: 5) yasumoto.name // ayaka yasumoto.number // 5 yasumoto.sayHello() // 出席番号 5, ayaka say hello yasumoto.appear // 0 yasumoto.update() yasumoto.appear // 1 // yasumoto.reset() // エラーになる。ExchangeStudentしか使えない var hirono = ExchangeStudent(name: "suzuki", number: 8) hirono.name // suzuki hirono.number // 8 hirono.appear // 0 hirono.update() hirono.appear // 1 hirono.reset() hirono.appear // 0 // 出席番号 8, suzuki say hello // 出席番号 8 は永久欠番です!! hirono.sayHello()
18 プロトコル
プロトコルとは
- 以下のような問題を解決してくれて便利なもの
- すべてのプロパティを受け継がなくていい
- クラスの継承では親クラスを複数持つことができない
- 継承関係がないような複数のクラスに似たような機能を持たせることを保証することができる
protocol Idol { // プロパティは読み取り専用か、読み取り&設定両方可能かを指定出来る var group: String {get set} func sayGroupName() // プロトコルの中では実際の実装を書かない } // プロトコルを適用 // プロトコルが複数ある場合はカンマで区切る // 親クラスを継承してからプロトコルを適用する場合は、継承してからカンマで区切ってプロトコルを適用 class Student: Idol { var name: String var number: Int var appear: Int = 0 var group: String = "EBC" // プロトコルのプロパティ func sayGroupName() { // プロトコルのメソッド println("We are \(group)!!!") } // イニシャライザーを作る init (name: String, number: Int) { // selfはそのクラスのインスタンス self.name = name self.number = number } func sayHello() { println("出席番号 \(number), \(name) say hello") } // override禁止 / final final func update() { appear++ } } var yasumoto = Student(name: "ayaka", number: 5) yasumoto.group // EBC yasumoto.sayGroupName() // We are EBC!!!