まずApple IDを取得する。
→持ってる。
Xcodeをインストールする
→入れたぞ。
もくじ
Swiftの型
- Int型
整数型 - UInt型
符号なし整数。普通はInt型を利用する - Float型
実数型。小数点を含むものに利用します、有効桁数7桁 - Double型
実数型。小数点を含むものに利用します、有効桁数16桁 - String型
文字列型 - Bool型
真偽値を扱う。
シミュレータを起動してHello World
import UIKit class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view. // (1) ラベルに文字を入れる outputLabel.text = "Hello Swift!" } @IBOutlet weak var outputLabel: UILabel! }
→できたぞ。
じゃんけんアプリつくるぞ!
じゃんけん画像の素材
https://drive.google.com/file/d/1OfAkCAU47v1DZ5gPxjh-V35DH3eWN2e9/view
Control + パーツをソースコードにドラッグアンドドロップで関連づけができる。
// // ViewController.swift // MyJanken // // Created by 金広優 on 2020/02/14. // Copyright © 2020 Swift-Beginners. All rights reserved. // import UIKit class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view. } @IBOutlet weak var answerImageView: UIImageView! @IBOutlet weak var answerLabel: UILabel! // じゃんけん (数字) var answerNumber = 0 @IBAction func shuffleAction(_ sender: Any) { if answerNumber == 0 { answerLabel.text = "グー " answerImageView.image = UIImage(named: "gu") } else if answerNumber == 1 { answerLabel.text = "チョキ " answerImageView.image = UIImage(named: "choki") } else if answerNumber == 2 { answerLabel.text = "パー " answerImageView.image = UIImage(named: "pa") } // 次のじゃんけんへ answerNumber = answerNumber + 1 } }
import UIKit class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view. } @IBOutlet weak var answerImageView: UIImageView! @IBOutlet weak var answerLabel: UILabel! // じゃんけん (数字) var answerNumber = 0 @IBAction func shuffleAction(_ sender: Any) { answerNumber = Int.random(in: 0..<3) if answerNumber == 0 { answerLabel.text = "グー " answerImageView.image = UIImage(named: "gu") } else if answerNumber == 1 { answerLabel.text = "チョキ " answerImageView.image = UIImage(named: "choki") } else if answerNumber == 2 { answerLabel.text = "パー " answerImageView.image = UIImage(named: "pa") } } }
import UIKit class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view. } @IBOutlet weak var answerImageView: UIImageView! @IBOutlet weak var answerLabel: UILabel! // じゃんけん (数字) var answerNumber = 0 @IBAction func shuffleAction(_ sender: Any) { var newAnserNumber = 0 repeat { newAnserNumber = Int.random(in: 0..<3) } while answerNumber == newAnserNumber answerNumber = newAnserNumber if answerNumber == 0 { answerLabel.text = "グー " answerImageView.image = UIImage(named: "gu") } else if answerNumber == 1 { answerLabel.text = "チョキ " answerImageView.image = UIImage(named: "choki") } else if answerNumber == 2 { answerLabel.text = "パー " answerImageView.image = UIImage(named: "pa") } } }
アイコンについて
1枚の画像から複数のアイコンを設定してくれるMakeAppIconというツールを利用する。
音楽アプリをつくろう
SimulatorはCommand + 矢印キーで横にしたり、回転できる。
音源のサンプルをドラッグアンドドロップでセットする。
AVFoundationを読み込む
import UIKit import AVFoundation class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view. } // シンバルの音源ファイルを指定 let cymbalPath = Bundle.main.bundleURL.appendingPathComponent("cymbal.mp3") // シンバル用ぷのプレイヤーインスタンスを作成 var cymbalPlayer = AVAudioPlayer() @IBAction func cymbal(_ sender: Any) { } }
import UIKit import AVFoundation class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view. } // シンバルの音源ファイルを指定 let cymbalPath = Bundle.main.bundleURL.appendingPathComponent("cymbal.mp3") // シンバル用のインスタンス作成 var cymbalPlayer = AVAudioPlayer() @IBAction func cymbal(_ sender: Any) { do { // シンバル用のプレイヤーに、音源ファイル名を指定 cymbalPlayer = try AVAudioPlayer(contentsOf: cymbalPath, fileTypeHint: nil) // シンバルの音源再生 cymbalPlayer.play() } catch { print("🐱シンバルでエラーが発生したにゃん!") } } // シンバルの音源ファイルを指定 let guitarPath = Bundle.main.bundleURL.appendingPathComponent("guitar.mp3") // シンバル用のインスタンス作成 var guitarPlayer = AVAudioPlayer() @IBAction func guitar(_ sender: Any) { do { // ギター用のプレイヤーに、音源ファイル名を指定 guitarPlayer = try AVAudioPlayer(contentsOf: guitarPath, fileTypeHint: nil) guitarPlayer.play() } catch { print("🐱ギターでエラーが発生したにゃん!") } } // BGMの音源ファイルを指定 let backmusicPath = Bundle.main.bundleURL.appendingPathComponent("backmusic.mp3") // BGM用のインスタンス作成 var backmusicPlayer = AVAudioPlayer() @IBAction func play(_ sender: Any) { do { // BGM用のプレイヤーに、音源ファイル名を指定 backmusicPlayer = try AVAudioPlayer(contentsOf: backmusicPath, fileTypeHint: nil) backmusicPlayer.numberOfLoops = -1 // リピート設定 backmusicPlayer.play() } catch { print("🐱BGMでエラーが発生したにゃん!") } } // Stopボタン @IBAction func stop(_ sender: Any) { // BGM停止 backmusicPlayer.stop() } }
リファクタリング
共通処理のsoundPlayer()を定義する
fileprivate func soundPlayer(player:inout AVAudioPlayer, path: URL, count: Int) { do { player = try AVAudioPlayer(contentsOf: path, fileTypeHint: nil) player.numberOfLoops = count player.play() } catch { print("エラーが発生したにゃん🐱") } }
共通関数で置き換える。
import UIKit import AVFoundation class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view. } // シンバルの音源ファイルを指定 let cymbalPath = Bundle.main.bundleURL.appendingPathComponent("cymbal.mp3") // シンバル用のインスタンス作成 var cymbalPlayer = AVAudioPlayer() @IBAction func cymbal(_ sender: Any) { soundPlayer(player: &cymbalPlayer ,path:cymbalPath, count: 0) } // シンバルの音源ファイルを指定 let guitarPath = Bundle.main.bundleURL.appendingPathComponent("guitar.mp3") // シンバル用のインスタンス作成 var guitarPlayer = AVAudioPlayer() @IBAction func guitar(_ sender: Any) { soundPlayer(player: &guitarPlayer ,path:guitarPath, count: 0) } // BGMの音源ファイルを指定 let backmusicPath = Bundle.main.bundleURL.appendingPathComponent("backmusic.mp3") // BGM用のインスタンス作成 var backmusicPlayer = AVAudioPlayer() @IBAction func play(_ sender: Any) { soundPlayer(player: &backmusicPlayer ,path:backmusicPath, count: -1) } // Stopボタン @IBAction func stop(_ sender: Any) { // BGM停止 backmusicPlayer.stop() } fileprivate func soundPlayer(player:inout AVAudioPlayer, path: URL, count: Int) { do { player = try AVAudioPlayer(contentsOf: path, fileTypeHint: nil) player.numberOfLoops = count player.play() } catch { print("エラーが発生したにゃん🐱") } } }
アクセス修飾子
- public
どこからでもアクセス可能 - internal
同じモジュール内のみアクセス可能 - fileprivate
ファイル内のみアクセス可能 - private
定義内の中でのみアクセス可能
地図アプリをつくる
Text Fieldを貼り付ける
Return KeyにSearchを設定
mapで検索してMap Kit Viewを選択する
Text FieldのConstraintを設定
Map kit Viewも同様に設定します。
Assistantを設定
Controlボタンを押しながらドラッグアンドドロップをします。
ConnectionをOutletに設定してConnect
Connectionの種別
- Outlet … そのUI部品をどこかで参照したい時
- Action … そのUI部品の動作を決めたい時
import UIKit class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view. } @IBOutlet weak var inputText: UITextField! // ←● 追加された }
Map kit Viewも同様に設定します。
import UIKit class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view. } @IBOutlet weak var inputText: UITextField! @IBOutlet weak var dispMap: MKMapView! }
MapKitをimport
import UIKit import MapKit // ←● 追加 class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view. } @IBOutlet weak var inputText: UITextField! @IBOutlet weak var dispMap: MKMapView! }
import UIKit import MapKit class ViewController: UIViewController, UITextFieldDelegate { override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view. // Text Fieldのdelegate通知先を設定 inputText.delegate = self } @IBOutlet weak var inputText: UITextField! @IBOutlet weak var dispMap: MKMapView! func textFieldShouldReturn(_ textField: UITextField) -> Bool { // キーボードを閉じる textField.resignFirstResponder() // 入力された文字を取り出す(2) if let searchKey = textField.text { // 入力された文字をデバッグエリアに表示(3) print(searchKey) } return true } }
viewDidLoad()は画面が描画された時に最初に実行されるメソッド。
func textFieldShouldReturn(_ textField: UITextField) -> Bool { // キーボードを閉じる(1) textField.resignFirstResponder() // 入力された文字を取り出す(2) if let searchKey = textField.text { // 入力された文字をデバッグエリアに表示(3) print(searchKey) } // デフォルトで動作を行うのでtrueを返す(4) return true }
検索を押したらキーボード閉じればOK
このコードあるから入力後にキーボードが閉じられる。
textField.resignFirstResponder()
textFieldShouldReturn()は予め規定されているdelegateメソッド。
Xcodeを日本語化していないことに気づく
緯度経度を取得して表示
import UIKit import MapKit class ViewController: UIViewController, UITextFieldDelegate { override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view. // Text Fieldのdelegate通知先を設定 inputText.delegate = self } @IBOutlet weak var inputText: UITextField! @IBOutlet weak var dispMap: MKMapView! func textFieldShouldReturn(_ textField: UITextField) -> Bool { // キーボードを閉じる(1) textField.resignFirstResponder() // 入力された文字を取り出す(2) if let searchKey = textField.text { // 入力された文字をデバッグエリアに表示(3) print(searchKey) // CLGeocoderインスタンスを取得(5) let geocoder = CLGeocoder() // 入力された文字から位置情報を取得 geocoder.geocodeAddressString(searchKey, completionHandler: { (placemarks, error) in // 位置情報が存在する場合は、unwrapPlacemarksに取り出す(7) if let unwrapPlacemarks = placemarks { // 1件目の情報を取り出す(8) if let firstPlacemark = unwrapPlacemarks.first { // 位置情報を取り出す(9) if let location = firstPlacemark.location { // 位置情報から緯度経度をtargetCordinateに取り出す(10) let targetCoordinate = location.coordinate // 緯度経度をデバッグエリアに表示(11) print(targetCoordinate) } } } }) } // デフォルトで動作を行うのでtrueを返す(4) return true } }
住所などの文字列から位置情報を取得するメソッド
geocoder.geocodeAddressString(searchKey, completionHandler: { (placemarks, error) in // 以下略 })
クロージャが利用されていて、位置情報が取得できたタイミングで{}内が動きます。
import UIKit import MapKit class ViewController: UIViewController, UITextFieldDelegate { override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view. // Text Fieldのdelegate通知先を設定 inputText.delegate = self } @IBOutlet weak var inputText: UITextField! @IBOutlet weak var dispMap: MKMapView! func textFieldShouldReturn(_ textField: UITextField) -> Bool { // キーボードを閉じる(1) textField.resignFirstResponder() // 入力された文字を取り出す(2) if let searchKey = textField.text { // 入力された文字をデバッグエリアに表示(3) print(searchKey) // CLGeocoderインスタンスを取得(5) let geocoder = CLGeocoder() // 入力された文字から位置情報を取得(6) geocoder.geocodeAddressString(searchKey, completionHandler: { (placemarks, error) in // 位置情報が存在する場合は、unwrapPlacemarksに取り出す(7) if let unwrapPlacemarks = placemarks { // 1件目の情報を取り出す(8) if let firstPlacemark = unwrapPlacemarks.first { // 位置情報を取り出す(9) if let location = firstPlacemark.location { // 位置情報から緯度経度をtargetCordinateに取り出す(10) let targetCoordinate = location.coordinate // 緯度経度をデバッグエリアに表示(11) print(targetCoordinate) // ピンを生成(12) let pin = MKPointAnnotation() // ピンの置く場所に緯度経度を設定(13) pin.coordinate = targetCoordinate // ピンのタイトルを設定(14) pin.title = searchKey // ピンを地図を置く(15) self.dispMap.addAnnotation(pin) // 緯度経度を中心にして半径500mの範囲を表示(16) self.dispMap.region = MKCoordinateRegion(center: targetCoordinate, latitudinalMeters: 500.0, longitudinalMeters: 500.0) } } } }) } // デフォルトで動作を行うのでtrueを返す(4) return true } }
アンラップ
if let 変数名 = ターゲット
if let searchKey = textField.text { }
textField.textの値がsearchKey変数に代入できたら … 値があるなら {}を実行
delegate
- クラスで行いたい処理の一部を他のクラスに任せる
- 任せた処理を指定したクラスに通知
仲介する仕組み🐱✨
登場するもの
- ①処理を依頼するクラス
ViewController
・UITextFieldクラスから作ったインスタンスのinputTextに対して、「inputText.delegate = self」と記述してdelegateの通知先を自分自身と設定。
・通知したい内容はtextFieldShoudReturnメソッドを指定している - ②依頼する、依頼されるクラスを仲介するプロトコル
UITextFieldDelegate
検索キーワードが入力されてReturn(Search)が押された時の情報を取得して、通知先がViewControllerであることをUITextFieldに教える - ③処理を依頼されるクラス
UITextField
・UITextFieldDelegateから通知を受けたら、UITextFieldは、View Controllerに「return」がタップされた時の情報を教える
マップの種別を切り替え
Buttonを選択してドラッグアンドドロップ
ButtonのTypeをDetail Disciosureに設定
Constraintを右端になるように設定
Actionを指定してConnect
・・・ } // デフォルトで動作を行うのでtrueを返す(4) return true } @IBAction func changeMapButton(_ sender: Any) { } }
トグルボタンにする
// デフォルトで動作を行うのでtrueを返す(4) return true } @IBAction func changeMapButton(_ sender: Any) { // mapTypeプロパティ一値をトグル // 標準 → 航空写真 → 航空写真+標準 // 3D Flyover → 3D Flyover+標準 // → 交通機関 if dispMap.mapType == .standard { dispMap.mapType = .satellite } else if dispMap.mapType == .satellite { dispMap.mapType = .hybrid } else if dispMap.mapType == .hybrid { dispMap.mapType = .satelliteFlyover } else if dispMap.mapType == .satelliteFlyover { dispMap.mapType = .hybridFlyover } else if dispMap.mapType == .hybridFlyover { dispMap.mapType = .mutedStandard } else { dispMap.mapType = .standard } }