swift3 Back segueの設定 オブジェクト持って遷移(書きかけ読まないこと!)

画面が戻ったら、またオブジェクトを持ったまま、自動的に遷移する方法を実装

f:id:happy_teeth_ago:20180718162615p:plain

まず遷移元①この画面から移動する

 @IBAction func backScene(_ sender: UIBarButtonItem) {
        performSegue(withIdentifier: UnwindToMsSurvey, sender: self)
    }

UnwindToMsSurveyはただリテラル入力ミスを防ぐため

    private let UnwindToMsSurvey = "UnwindToMsSurvey"

戻る画面②

    //MSEntryMapから引き継ぐオブジェクト
    weak var datPerson: DatPerson?
    /// 遷移先ViewControllerからの復帰時の処理
    @IBAction func unwindToTop(sender: UIStoryboardSegue) {
        print("unwindToTop() sender:\(sender)")
        if sender.identifier == "fromMsEntry" {
            //新規登録画面へpersonオブジェクトを持って遷移する.
            performSegue(withIdentifier: "toDatPersonWithGPS", sender: nil)
        }
    }

ここでバックセグエのメソッドを記載しておく。

これにより①画面にて、StoryboardのExitタブにメソッドが表示される。

黄色いマークから出口マークへ Ctr + ドラッグ f:id:happy_teeth_ago:20180721201344p:plain

メソッド名は自由、引数は指定。 (sender: UIStoryboardSegue) 強制移動が多いケースなので
画面遷移するかの判定処理メソッドは必要ない。
そのままこのアクションでprepar for segue を呼べば良い。
//通常はこのメソッドで、遷移するかしないかの判定をする。
//ピッカーで選択した値が空とかの場合このメソッドで判定することが多い。

    override func shouldPerformSegue(withIdentifier identifier: String, sender: Any?) -> Bool {

Swift3 Pickerの実装について(未完成読まないこと!)

Pickerの実装について、説明する。 実際のソースコードからおっていきたい。

 @IBOutlet weak var uiBirthday: MsDateTextField!

Picker用のクラスを作成する②

class MsDateTextField: UITextField, UITextFieldDelegate {

viewDidLoadで自作のクラスのプロパティへアクセス①

override func viewDidLoad() {
~
uiBirthday.textUiPlaceAdjust = textUiPlaceAdjuster

textUiPlaceAdjustプロパティとは②の中で宣言

  weak var textUiPlaceAdjust: TextUiPlaceAdjustDelegate?

TextUiPlaceAdjustDelegate型③は

そのプロトコルでは入力の始まりと終わりの関数を用意

protocol TextUiPlaceAdjustDelegate: class {
    func textUiDidBeginEditing(_ textUi: UIView)
    func textUiDidEndEditing(_ textUi: UIView)
}

プロコルはどこにでも記載できるので関係する③

class TextUiPlaceAdjuster: NSObject,
    UITextFieldDelegate, UITextViewDelegate, TextUiPlaceAdjustDelegate {

のクラスの上に宣言

error lossof ke chiang access

f:id:happy_teeth_ago:20180716133542p:plain このエラーが出たので調査。 問題はキーチェーンアクセスを変更したこと。 それに伴い、テストフライトでのインストールしたアプリが、一部の機種で起動しない。

f:id:happy_teeth_ago:20180716134400p:plain

xcode>preference>accounts>

appleIDを選択>右下のTeamから選択したものをダブルクリック

f:id:happy_teeth_ago:20180716134723p:plain:w300

これで、appdeveloperにアクセスして、よしなにやってくれるみたい。

とりあえず、testflightでは落ちなくなった。

Swift nilエラーの修正 (ほとんど読む価値なし)

CoreData保存時に personPhotoにnilが入る。 それでエラーになるため修正。

  personPhoto = UIImageJPEGRepresentation(personImageView.image!, 1)

修正後

    var personPhoto:Data? = nil
        if let image = personImageView.image{
            personPhoto  = UIImageJPEGRepresentation(image, 1)
        }

オプショナルバインディングを利用 personImageView.imageがnilなら{  }の中が実行されない。

そもそもなぜこのようにするのかというと

UIImageJPEGRepresentationの関数定義を見ると

public func UIImageJPEGRepresentation(_ image: UIImage, _ compressionQuality: CGFloat) -> Data?

引数に?がついていないし、エラーハンドリングもされていない。 つまりこの関数はnilをセットしてはいけないということ。

personImageViewはこのようにメンバで宣言

@IBOutlet weak var personImageView: UIImageView!

personImageViewは画面接続なのでnilの可能性はないが personImageView.image!は、画像登録しないと当然nilになる。

nilになる可能性のあるものを、引数にnilは許さない関数にセットしていたのが問題。

以上

Rails 実装 検索して表示 小ネタ

f:id:happy_teeth_ago:20180715013614p:plain:w300

  <%= link_to :of_today_plan_items, class: 'list-group-item' do %>
    <i class='fa fa-list'></i> 今日の予定表
  <% end %>

なぜ do で回すのか?
link_to定義は

link_to ( HTMLで表示する文字列 , url(routes)に記載しているもの,[option])

というふうに記載する

classはcssの設定をオプションで指定。
この書き方のメリットはアイコンと表示文字を次の行にて記載できる点である。

Font Awesomeのアイコンを埋め込む場合はブロックのほうがいい。

コードが、簡潔でわかりやすい。

Urlヘルパーはroutesで resources を利用しているので自動的に生成される。

resources :plan_items do
    get :of_today, on: :collection
  end

ちなみにここではネストしている。 plan_itemsの配下に of_todayがネストされている。
アドレスを見るとわかりやすい。
結局ルーティングはアドレス。

f:id:happy_teeth_ago:20180715013004p:plain:w300

plan_itemsは基本7つのメソッドを持つ その配下にof_todayがあるということ。
このof_todayも7つのメソッド、

index,show,new,create,edit,update,destroy

を持っている。
これがルーティングの基本なのでここはしっかりと抑えること。

次に画面について、これが

f:id:happy_teeth_ago:20180715014652p:plain:w300

このように切り替わるのは、なぜだろう?さあ考えてみよう!

f:id:happy_teeth_ago:20180715014728p:plain:w300

まず、予定表と今日の予定表のボタンがある。 これはボタンだと推定される。
その下には、toDoリストがあるが、今日の予定を押すと その日のToDoに絞られて表示される。 これはおそらくコントローラでその日のToDoを取得する関数を書いているはず。 そして、その変数をブロックで回して、その下に表示しているに違いない。 まずは、viewから見ていきたい。 まず予定表のボタン

<%= render 'index_nav' %>

これはパーシャル部分テンプレートを呼び出している。その部分は

<ul class='nav nav-pills my-2'>
  <% %i(index of_today).each do |action| %>
    <li class='nav-item'>
      <%= plan_items_nav_link(action) %>
    </li>
  <% end %>
</ul>

%i は要素がシンボルの配列を生成する。

index と of_todayが入って来るので、2回ループする。

index: '予定表',
of_today: '今日の予定表'

これはヘルパーで宣言されている。

ACTION_LABEL_MAPはハッシュ、swiftで言うところの列挙体の様なイメージ。

文字列がどれに対応しているか示す。

  ACTION_LABEL_MAP = {
    index: '予定表',
    of_today: '今日の予定表'
  }

どうしてこれが表示されるのかと言うと、ヘルパーに書いてある関数を見ればわかる。

<%= plan_items_nav_link(action) %>
  def plan_items_nav_link(action)
    label = ACTION_LABEL_MAP[action]
    url = (action == :index ? :plan_items : [ action, :plan_items ])
    html_classes = %w(nav-link)
    html_classes << 'active' if action.to_s == params[:action]
    link_to label, url, class: html_classes.join(' ')
  end

つまりactionには、予定表、今日の予定表がブロックで入ってくる

かっこいい!

 # GET (collection)
  def of_today
    t0 = Time.current.beginning_of_day
    t1 = t0.advance(hours: 24)
    @plan_items = PlanItem
      .where('starts_at >= ? AND starts_at < ?', t0, t1)
      .or(PlanItem.where('ends_at > ? AND ends_at <= ?', t0, t1))
      .natural_order
    @continued_plan_items = PlanItem
      .where('starts_at < ? AND ends_at > ?', t0, t1)
      .natural_order
    render action: :index
  end

render action

このオプションは、他のアクションのテンプレートを表示する。 .advance(hours: 24)は日付関連のrailsのメソッドここでは24時間後を取得。 求めたい日付の、最初の時刻と最後の時刻を取得する。 その後where句にて絞り込みをする。1日の範囲を指定。

where句とは、SQL文

検索や、更新するときのいちばん大切な句 結果が必ず真か偽となる条件を記載しないといけません。 例文

DELETE FROM テスト WHERE 得点 < 20
テストテーブルから、得点が20点以下のものを削除する という意味 

**もう一度plan_items_nav_linkを振り返る。

  def plan_items_nav_link(action)
    label = ACTION_LABEL_MAP[action]
    url = (action == :index ? :plan_items : [ action, :plan_items ])
    link_to label, url, class: html_classes.join(' ')
  end

ちなみに、この記事で参考にしているソースは、

Ruby on Rails 5 初級④ です
MITライセンスでした。
この本はRailsの勉強には、とてもいい本でした。
是非!Railsの勉強をしている方は購入してみてください。

swift3 Pickerの実装方法 (未完成、見るべからず)

まずcomponentとrowの理解から f:id:happy_teeth_ago:20180713081715j:plain

componentは左から0,1,2,3とindexが割り当てられる

rowは上から0,1,2,3とindexが割り当てられる

addTarget(_:action:for:)とは

ターゲットオブジェクトとアクションメソッドをコントロールに関連付けます。

func addTarget(_ target: Any?, 
        action: Selector, 
           for controlEvents: UIControl.Event)

target:

アクションメソッドが呼び出されたオブジェクト。

action:

呼び出されるアクションメソッドを識別するセレクタ。

controlEvents:

アクションメソッドが呼び出されるコントロール固有のイベントを指定する valueChanged: UIControl.Event

 UIBarButtonItem(
                barButtonSystemItem: UIBarButtonSystemItem.cancel,
                target: self, action: #selector(PickerTextField.cancel))

selector(PickerTextField.cancel))

には対象オブジェクトを指定して、それに対してのプロパティを設定する

@objc

Objective-Cには2つの隠し引数がありましたが、Swift4.0には1つしかありません。 これはつまり相互に利用することができないということです。 よってswiftから利用するときは@objをつける すると利用できるようになる。

Swift3 CollectionViewの中にTableViewを配置

f:id:happy_teeth_ago:20180711223527p:plain

CollectionViewは横向きに並べるのが得意

TableViewは縦向きに並べるのが得意

最近のはやりでフリックで横方向に、画面遷移するにはCollectionViewの中にTableViewを配置しなくてはいけない。
ではまず、イメージしましょう? collectionViewのインスタンス作ってそこにどのようにTableを配置するのか?

//collectionのセルを生成 ifの条件2つ   
if let cell = mainCollectionView.dequeueReusableCell(withReuseIdentifier: R.nib.mainCollectionViewCell,for: indexPath),
                let viewController: NewsListViewController = tabInfo.viewController as? NewsListViewController {
                //ここから条件が真のときの処理、ちなみに必ず条件は真になる。                 
                viewController.delegate = self
                cell.setView(viewController: viewController)
                return cell
            }

tabInfoの宣言は
let tabInfo = viewModel.sourceTabInfo[indexPath.row]

viewModel

var viewModel = UpTabViewControllerViewModel()

class UpTabViewControllerViewModel {
    /// タブ情報
    var sourceTabInfo:[(viewController: UIViewController, title: String)] = []  

sourceTabInfoはタプルで宣言。
辞書型とは違う。
タプルのほうが、型を決めれるので、優れている。
特別な理由がなければ、必ずタプルを利用すること。

つまりtabInfoは, UIViewController型のviewControllerとString型のtitleの配列で構成されている。

そのviewControllerをNewsListViewController にキャストしなさいといっている。

ここがポイント

class NewsListViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, NewsListCellDelegate, ArticleDetailViewControllerDelegate, SpeechModelDelegate {
   
   var viewModel: NewsListViewModel?
   
   override func viewDidLoad() {
        super.viewDidLoad()
        
        guard let viewModel = viewModel else { return }
        viewModel.bind {
            self.refreshView()
        }
    }

viewModelは

やっとここで出てきました。しかし、拡張性の高いコードです。かっこいいですね。

class NewsListViewModel {
    
    var newsListCellViewModel: [NewsListCellViewModel]! {
        didSet {
            didChange?()
        }
    }
    
    /// ジャンル
    var genre: String = ""
    /// ニュース一覧
    var newsList: [Article] = []
    /// 最大読み込み件数
    let maxNewsList = 100
    // 1ページあたりの読み込み件数
    let numReadOfPage = 25
    /// 現在のページ
    var page = 1

なお参考にさせていただいているソースはRyutaMiyamotoさんのものです。

Copyright (c) RyutaMiyamoto Released under the MIT license

github.com