ロリポップcoda2接続方法

coda2で接続する方法が公式ページになかったので書きます。 まずSSHの設定をします。

f:id:happy_teeth_ago:20180709213618p:plain:h300

設定すると f:id:happy_teeth_ago:20180709213557p:plain

codaは

f:id:happy_teeth_ago:20180709213838p:plain:w500

プロトコルは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
    }
    //.

f:id:happy_teeth_ago:20180708224521p:plain

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)")

これでログにはいています。 f:id:happy_teeth_ago:20180702171407p:plain

場所がわかったらDB接続してみていきましょう。 おすすめのアプリはDBBrowser for SQLiteです。MAC版ですし、無料で使いやすいです。 f:id:happy_teeth_ago:20180702171543p:plain

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型になります。 f:id:happy_teeth_ago:20180628003710p:plain

エラー処理について

do ~ catch文
基本形
do {
     try  エラーが予想される処理(throwキーワードが指定された処理を呼び出す場合)
    
 
    }catch{
     エラーが 発生したときの処理(普通print(error)を書きます。
}

ただ、CoreDataの中に、バイナリで持つのはおすすめしません。 画像データは大きいので、DBが遅くなります。 String型で参照を持つほうがいいです。 そのあたりは、また記載していきます。