ロリポップcoda2接続方法
coda2で接続する方法が公式ページになかったので書きます。 まずSSHの設定をします。
設定すると
codaは
プロトコルはSFTP
サーバーアドレスはロリポップSSHの説明のサーバーを記載
ユーザー名は ロリポップSSHのアカウント
パスワードは ロリポップSSHのSSHパスワード
ポート番号は 2222
Swift3 iPhone iPad画面切り分け 実装方法
Swift3 iPhone iPad画面切り分け 実装方法について
iPhoneとiPadのファイル分ける方法は、実際的ではない。
同じ処理を何度も書かないといけない。
DRYの原則に反する
ポイントは同じファイル内に複数のクラスを記載し、そのクラスを継承するだけ。
下記は同じswiftファイル内の記載
ipadだけに接続する必要があるものは、接続する。
MsPersonDataUniに共通処理を記載し、継承するだけ。
/// 訪問先(iPad用ペイン) class MsPersonDataPad: MsPersonDataUni { //.......................................................................... //MARK: Outlets & Properties @IBOutlet var uiTapGesture: UITapGestureRecognizer! //
つづいてiPhone
同じくiPhoneのみの部品(ここはMAP)は接続する
/// 訪問先(iPhone用画面) class MsPersonDataPhn: MsPersonDataUni { //.......................................................................... //MARK: Outlets & Properties @IBOutlet weak var mkMap: MKMapView!
最後に共通処理を書く。 こうすることによりDRYの原則に沿ったコーディングが可能。
/// 訪問先本体(iPad & iPhone 共通) class MsPersonDataUni: UIViewController, MsMapAgentDelegate { //.......................................................................... //MARK: Types & Constants /// 画面使用シーンを表す
StoryBoardはきちんと分けましょう! 以上
Swift3 Pickerの実装
まず接続
TextFieldを扱う専用のクラスを作成
@IBOutlet weak var uiBirthday: MsDateTextField!
呼び出し
uiBirthday.date = datPerson?.birthday as Date?
プロパティオブザーバー
プロパティオブザーバー とは、ストアドプロパティの値を変更を監視し、変更前と変更後に文を実行するもの ここではdidSetなので、プロパティの変更後に実行する処理を記載。
/// TextField I/O(注意.setFormatが実行されている事) public var date: Date? { didSet { text = date == nil ? "" : dateFormatter.string(from: date!) onResetDate?() } }
text = date == nil ? "" : dateFormatter.string(from: date!)
は3項演算子
textにはdateをいれてもしそれがnilなら””(空白)をいれてそうでないなら、dateFormatter.string(from: date!)を入れましょうね。
class MsDateTextField: UITextField, UITextFieldDelegate { ~~ /// date のTextFieldとピッカーでの表現形式を設定する public func setFormat(_ forText: DateFormatter, forPicker: UIDatePickerMode) { self.delegate = self dateFormatter = forText // 左から右にピッカー ツールバーのボタンを順に作成 var barItems = [UIBarButtonItem]() let spaceButton = UIBarButtonItem( barButtonSystemItem: UIBarButtonSystemItem.flexibleSpace, target: nil, action:nil) barItems.append( UIBarButtonItem( barButtonSystemItem: UIBarButtonSystemItem.cancel, target: self, action: #selector(PickerTextField.cancel)) ) barItems.append(spaceButton) barItems.append( UIBarButtonItem( barButtonSystemItem: UIBarButtonSystemItem.done, target: self, action: #selector(PickerTextField.done)) ) let toolbar = UIToolbar(frame: CGRect(x: 0, y: 0, width: 10, height: toolBarHeight)) toolbar.setItems(barItems, animated: true) let datePicker = UIDatePicker() datePicker.datePickerMode = forPicker self.inputView = datePicker self.inputAccessoryView = toolbar uiDatePicker = datePicker } //.
var barItems = UIBarButtonItemは上部のバーに表示するcancel ,doneを生成している。 そしてtoolbarを生成し、中に詰め込む
let toolbar = UIToolbar(frame: CGRect(x: 0, y: 0, width: 10, height: toolBarHeight)) toolbar.setItems(barItems, animated: true)
let datePicker = UIDatePicker() datePicker.datePickerMode = forPicker self.inputView = datePicker self.inputAccessoryView = toolbar uiDatePicker = datePicker
let datePicker = UIDatePicker()
でピッカーを生成
datePicker.datePickerMode = forPicker
datePickerModeはデフォルトで日付と時間を表示する
forPickerは引数で持ってきた値
uiDatePickerは
private weak var uiDatePicker: UIDatePicker!
このクラスのインスタンス
ここから明日へ続く。。。
DateFormatterとは
つまりDateFormatterでいろんな表現をして、NSDateオブジェクトを作成するということ
UIDatePickerModeとはUIDatePickerクラス(ピッカーを実装する本体のクラス)の列挙体、名前つけているだけですね。
このモードでは、日付、時刻、または両方の日付と時刻が表示されるかどうかが決まります。また、それを使用してカウントダウンタイマーの外観を指定することもできます。 これで外観を決める つまり外観を整えるには、
public enum UIDatePickerMode : Int { case time // Displays hour, minute, and optionally AM/PM designation depending on the locale setting (e.g. 6 | 53 | PM) case date // Displays month, day, and year depending on the locale setting (e.g. November | 15 | 2007) case dateAndTime // Displays date, hour, minute, and optionally AM/PM designation depending on the locale setting (e.g. Wed Nov 15 | 6 | 53 | PM) case countDownTimer // Displays hour and minute (e.g. 1 | 53) }
ちょっと大変なことになるので、書き方改めます。 まず外観を俯瞰して、それから詳細を記載するように努めます。 今しばらくお待ち下さい。
Swift 新規入力か編集モードの切り分け実装方法
ある画面が、新規入力かそれとも既存データの更新かを切り分ける実装について
// 名前:新規の場合、デフォルト名を見本として表示する if editMode == .New { uiName.placeholder = datPerson?.name lastMeeting = datPerson?.lastMeeting } else { uiName.text = datPerson?.name }
なんだか見慣れない書き方と思うかもしれない。
if editMode == .New
これは何かと言うと
EditMode列挙体の型でインスタンスを生成しておく。
private var editMode: EditMode = .Empty
これを列挙体で持っておく
こうすると間違いが少ない。いちいち数字を入れていると間違えのもと。
宣言はこのようにする。
/// - Empty: 該当無し(検索と訪問先の画面で検索結果が空,...等) /// - New: 新規登録 /// - Existing: 既存訪問先(詳細表示と編集) enum EditMode: Int { case Empty case New case Existing }
ちなみに列挙体ではInt型のrawValueには、デフォルト値が存在し、値を指定しなければ、デフォルトが割り当てられる。 便利ですね。
はじめは0から1.2.3と割り当てられます。
なので
EditMode.Empty.rawValue // 0が入っています。
これで新規入力か更新の処理は分けた。 その後値を代入していくのだが、更新の場合、nilが怖い。 そのような場合
nilの可能性があるものは中値演算子 ??を利用する。
例えば
ptfMstBunrui.selectedMstItem = datPerson?.mstBunrui ?? nil
中値演算子とはなんぞや?
定義
まず左辺がオプショナル型、それが値を持っていれば、アンラップした値を入れる。
左辺が値を持っていなければ ?? のあとの数値を代入する。
datPerson?.mstBunrui (オプショナル型)が値を持っていれば、それをアンラップして左辺へ代入する。 そうでなければnilを入れましょう。という事 書き直せば
if let _ = datPerson?.mstBunrui{ ptfMstBunrui.selectedMstItem = datPerson!.mstBunrui }else{ ptfMstBunrui.selectedMstItem = nil }
こういうことになります。
Swift CoreDataの中身を確認する方法
まずappdelegateに記載する アプリが起動したら呼ばれるところ
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
print("AppDelegate.application()")
let path = NSSearchPathForDirectoriesInDomains(
.applicationSupportDirectory, .userDomainMask,true).first!
print("- applicationSupportDirectory:(path)")
return true
}
NSFileManagerクラスのこの関数を利用する
NSSearchPathForDirectoriesInDomains
このクラスは,ファイルやディレクトリの検索、作成、コピー、および移動を行います。また、ファイルやディレクトリに関する情報を取得したり、属性の一部を変更したりするためにも使用します。 この関数は、指定されたドメイン内の指定されたディレクトリのパス文字列のリストを作成します。
定義
public func NSSearchPathForDirectoriesInDomains(_ directory: FileManager.SearchPathDirectory, _ domainMask: FileManager.SearchPathDomainMask, _ expandTilde: Bool) -> [String]
SearchPathDirectoryは列挙体です。その中に .applicationDirectory があります。
public struct SearchPathDomainMask の構造体の中には、 public static var userDomainMask: FileManager.SearchPathDomainMask { get } // user's home directory --- place to install user's personal items (~ ここを見るとユーザーの個人的ファイル、ここではcoredataへのパスが取得できます。
print("- applicationSupportDirectory:(path)")
これでログにはいています。
場所がわかったらDB接続してみていきましょう。 おすすめのアプリはDBBrowser for SQLiteです。MAC版ですし、無料で使いやすいです。
swift テーブル1画面2つ上下表示
それぞれのテーブルを判別できるようにしておく必要がある。 そのためクラスを2つ作成した。同じファイル内に(継承はしていない) まずアクセスするために下のテーブル(別クラス)のインスタンス生成
上のテーブルのクラス
class Dic_iphone: UIViewController, UITableViewDelegate, UITableViewDataSource,UISearchBarDelegate{ //下のテーブルのクラスのインスタンス var table_down = Table_down() //テーブルの名前を分けて接続、 //上のテーブル @IBOutlet weak var listTable: UITableView! //下のテーブル @IBOutlet weak var detailTable: UITableView! //viewDidloadにデリゲート宣言 override func viewDidLoad() { super.viewDidLoad() //上のテーブル listTable.delegate = self listTable.dataSource = self //下のテーブルのデリゲートここがポイントといえばポイント detailTable.delegate = table_down detailTable.dataSource = table_down
下のテーブルクラス
class Table_down :NSObject, UITableViewDelegate, UITableViewDataSource{ 通常のデリゲートメソッドを記載 接続は上のテーブルのクラスにて行う。 //セルに表示 func numberOfSections(in tableView: UITableView) -> Int { return 1 } //datasource func func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return datDics_down.count } //セルの内容表示 func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "Cell") as! DetailCustomCell
この下のテーブルを扱うクラスには特別な処理はなにもない。
以上
swift CoreData 保存 カメラで取得した画像
まずカメラの利用には
UIImagePickerController
が必要。何をするかというとアップルのサイトより
ユーザーのメディアライブラリから画像を撮影し、ムービーを記録し、アイテムを選択するためのシステムインタフェースを管理するビューコントローラ。 のは、(メディアキャプチャをサポートするデバイス上で)新しい画像やムービーを撮るためのユーザインタフェースを提供します。
ふむふむ、大切ですね。カメラや、動画、写真へのアクセスする際にはこのメッソドを利用します。
クラスはUINavigationControllerDelegateとUIImagePickerControllerDelegateを継承します。
UIViewController , UINavigationControllerDelegate, UIImagePickerControllerDelegate {
sourceTypeUIImagePickerController.SourceType.camera //関数定義をみるとゲッタセッタをセットするとあります。 var sourceType: UIImagePickerController.SourceType { get set }
UIImagePickerController.SourceType には3つある
case photoLibrary //iphoneの写真から取り込む
case camera //カメラから取り込む
case savedPhotoAlubm //デバイスがカメラ持っていない時のフォトアルバム
表示するときはpresentメソッドを利用する これはuiviewControllerのインスタンスメソッド
func present(_ viewControllerToPresent: UIViewController, animated flag: Bool, completion: (() -> Void)? = nil)
ここではimagePickerControllerを指定しているが、他のクラスも指定できます
// (1)撮影が終わったときに呼ばれるdelegateメソッド
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) { // (2)撮影した写真を、配置したpictureImageに渡す pictureImage.image = info[UIImagePickerControllerOriginalImage] as? UIImage // (3)モーダルビューを閉じる dismiss(animated: true, completion: nil) } }
このデリゲートメソッドはinfo: [String : Any])にカメラで撮影された情報が格納されているとある。 どういう意味かというと、関数定義では
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary<NSString *,id> *)info; // info dictionary keys public let UIImagePickerControllerOriginalImage: String // a UIImage
フレームワークの中では、辞書型 string とidで持っている
info: [String : Any]
のなかで辞書型のデータを渡している。辞書型とはkeyと値のペアで情報を持つ、配列だと思えば良い。 Rubyのハッシュですね。 辞書にアクセスるるのはkeyでアクセスする。 定義を見ると
UIImagePickerControllerOriginalImage: String
とある。つまり文字列のkeyを渡しているのだ。値は、iphoneのカメラで撮られたデータが入っている。そこから先はは知らなくていい。 目的達成のために、写真が利用できればいいのだ。
あとは保存処理。上記デリゲートメソッドの中の
pictureImage.image = info[UIImagePickerControllerOriginalImage] as? UIImageのあとに書いていこう
//jpegデータに変換するメソッド 第1引数は画像、第二引数は解像度 let img = UIImageJPEGRepresentation(pictureImage.image!, 1) //save関数に保存は委ねる saveData(imageData: img!) func saveData(imageData:Data){ //persistcontainerを作る。入れ物を用意しておく let appdelegate = UIApplication.shared.delegate as! AppDelegate let context = appdelegate.persistentContainer.viewContext //ここポイントです。Photoテーブルの型でpersistcontainerでもってオブジェクトをつくる. let photo_core = Photo(context: context) //そうするとphotoテーブルのプロパティ(カラム)にアクセスできる。 photo_core.pic = imageData //エラー処理 後述します。 do{ try context.save() } catch { print(error) } }
Photoはcoredataのテーブルのことです。
ちなみにcoredataのテーブルのカラムの型は BinaryData型になります。
エラー処理について
do ~ catch文 基本形 do { try エラーが予想される処理(throwキーワードが指定された処理を呼び出す場合) }catch{ エラーが 発生したときの処理(普通print(error)を書きます。 }
ただ、CoreDataの中に、バイナリで持つのはおすすめしません。 画像データは大きいので、DBが遅くなります。 String型で参照を持つほうがいいです。 そのあたりは、また記載していきます。