もぶわさおのSWIFTマンブログ

プログラミング言語「Swift」を習得していく段階の備忘録として記載していきます

xcodeで関数などが補完された際に、補完された候補で確定する場合

xcodeでは関数などで以下のように補完候補が出てきます

f:id:mob-wasao:20170322172939j:plain

水色の部分は自分で入力する箇所で、エディタが候補として表示してくれてます

しかし今まで、その候補を使いたいのに、どのように確定するかわかりませんでした

(消しては同じ文字を入力していました。アホらしいですよね)

 

確定の仕方なのですが、まず、候補の前にカーソルを合わせ、Shiftキーと→を押します

すると以下のようになります

f:id:mob-wasao:20170322172556j:plain

で、Enterキーをおしますと、以下のようになります

f:id:mob-wasao:20170322173140j:plain

ほら、「String?」が入力されました

自分で入力する必要はありません

 

・・・こういうのって、誰か教えてくれないもんですかねぇ

聞くまでもなく使えるものなのでしょうか(汗)

SearchBarに入力中の文字を使い、リストから候補を探したい場合(インクリメンタルサーチ)

SearchBarに日本語を入力する際、変換候補が現れると入力が楽ですよね

それを実装する際にはまったので、その実装を説明します

 

まず、SearchBarに「確定前の入力内容」が飛んでくるイベントは

func searchBar(_ searchBar: UISearchBar, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool 

です

入力中の検索バーの内容(いままさに入力している文字以外)は、searchBar.textから取得可能です

今まさに入力している文字は、textに入っていますし、textが編集される文字列のインデックスはrangeに入っています

 

じゃあ、searchBar.textのrange位置にtextを入れてやれば、今まさに入力されている状態と同じ文字がくるかと思うじゃないですか

やってみたソースが以下

let searchText = searchBar.text! as NSString).replacingCharacters(in: range, with: text)

でも、これ、うまくいかないんです

 

やってみてもらえればわかりますが、rangeでくるデータが意外に微妙なのです

日本語入力の場合、「あ」→「い」という風に、押す回数によって文字が変わりますよね?(フリック入力でない場合)

その際にやってくるrangeの中身がみんな同じなので、使えません

文字を足そうとしているのか、置き換えようとしているのか、判別つかないんです

 

じゃあ、どうしましょう?

結果として、遅延実行という手を取りました

ちょっと時間をあけて、処理を行うという手法です

複数回キーを押されたにしても、遅延して処理を実行することにより、searchBarには入力後の値が入っているだろう想定でやるのです

だので、以下のようになります

// searchBarに文字入力されたら呼ばれるメソッド(確定前でも呼ばれる)
func searchBar(_ searchBar: UISearchBar, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
        
    DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
        // リストを全消去する
        self.list?.removeAll()
            
        // 検索文字列を生成する
        let searchText = searchBar.text!
            
        // 検索対象の文字列を絞り込んで、リストにする
        self.list = self.stationList?.filter({($0 as! StationMaster).stationNameHiragana!.contains(searchText)})
            
        // リストを再描画する
        self.tableView.reloadData()
    }
    return true
}

わかりますかね

DispatchQueue.main.asyncAfterの中の処理は0.1秒ほど遅れて実行されます

その中でやっているのは、searchTextの中の文字を信用して検索しているのです

やってみた実績として、上記の方法をオススメします

xcodeのプロジェクトにテキストファイルを追加して読み書きしたいのだが・・・

プロジェクトにファイルを追加して、それを読んだり書いたりしたとき、以下のようにするといいらしい

(Simulatorの方にファイルが転送されていたので正しいものと思う)

①Resourcesフォルダを切って、送りたいファイルを保存する

②プロジェクトを選択し、「New Group」から「Resouces」を作成

右側のペインの「Full Path」を①で作成したフォルダに設定する

(Full Pathの右上にあるフォルダアイコンを押下するとフォルダを変更できるよ)

③②で作成したGroupにファイルを追加する

(Groupを選択し、「Add File to …」から①のファイルを追加してやる

④一応確認だが、ビルド設定の「Build Phases」の「Copy Bundle Resources」に①のファイルが出ていることを確認する

f:id:mob-wasao:20170316110155j:plain

(※上の例では、Station.csvというファイルを追加して転送できるように設定した)

あとは下のソース

let bundle = Bundle.main

print((bundle.path(forResource: "Station", ofType: "csv"))!)

print("bundle:" + bundle.bundlePath)

これで設置されたファイルのパスがわかるのでファイルを開いてやればいいようだ

segueによる画面遷移の際、右から左への画面への画面遷移ができない

画面遷移を行う際、segueという仕組みを利用する

で、その遷移の話として「右方向の画面へ遷移していく」表示方式を取りたかったのだが、「下から上へ出てくる方式」しかできなかった

まずは以下のようになっている

f:id:mob-wasao:20170315113242j:plain

検索ボタンを押下すると、下の画面へ遷移するようにsegueを配置した

しかし、この方法では、「下から上へ画面が出てくる」方式の遷移しかできない

 

他のサイトに「show」は右から出てくる、みたいに書いてあって、なんども思考錯誤したけど、うまくいかなかった

するとどうも、「Navigation Controller」というものが挟まっていないことが原因だとわかってきた

 

だので、ナビゲーションコントロールを開始したいSceneの一番上のViewを選択し、メニューから[Editor]→[Embed In]→[Navigation Controller]と選択してやる

f:id:mob-wasao:20170315114723j:plain

すると以下のように「Navigation Controller」が挟まってくる

f:id:mob-wasao:20170315114845j:plain

そうすると、ようやく「右から左への画面遷移」ができるようになった

なんというか、大前提として、Navigation Controllerが必要だったようだ

CoreDataを使う際にいきなりエラー

CoreDataというiOSのO/Rマッパ(かな?)を使用する際、いきなりエラーが出てたじろいだ

 

手順はこんな感じ

まずはDataModelというのを追加

f:id:mob-wasao:20170313142901j:plain

つぎにEntity(エンティティ)を追加

エンティティというのは、テーブルとカラムの対応づけ、みたいなものです(雑な説明で恐縮ですが)

f:id:mob-wasao:20170313143120j:plain

んで、これを実際にソース(swift)に変換する

f:id:mob-wasao:20170313143253j:plain

実際にできたファイルはこんな感じ

f:id:mob-wasao:20170313143728j:plain

いきなりエラーでますやん

ツールで作ってんのにいきなりエラーってなに?

Invalid redeclaration of 'Entity' とか
'Entity7 is ambigous for type lookup in this context とか

 

解決法

f:id:mob-wasao:20170313144133j:plain

画面右端の「Codegem」を「Manual/None」にすればよいようだ・・・

詳細は後日調べる

UIButtonで余白があるのに文字が全部表示されない件

UIButtonにタイトルを指定している際、幅は十分あるにもかかわらず

文字が全部表示しきれなかった

 

初期画面はこちら

f:id:mob-wasao:20170310151628p:plain

 

次の画面で選択した文字列に置き直した結果がこちら

f:id:mob-wasao:20170310151726p:plain

 

省略されてますよね?

省略される位置の問題でなく、表示する努力を怠った感じで省略されてますよねw

 

以下の方法で暫定解決としました

f:id:mob-wasao:20170310151906p:plain

 

最初に長い文字列を設定しておくと、十分なサイズが確保される模様

(たぶん、本来的には中に入っているUILabelをごにょごにょするんだろうけど)

 

segueによる画面間データの受け渡し(失敗例)

親画面に表示する内容を子画面で選択。その後、親画面でそれを表示したい

子画面にはTableViewがあり、テーブルを選択した際にUnwindActionが走る

失敗例:

子画面側ソース

    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        
        let viewConroller = segue.destination as! SearchViewController
        
        switch segueName {
        case "selectDestination":
            viewConroller.destinationButton.titleLabel?.text = selectedItem
        case "selectDeparture":
            viewConroller.departureButton.titleLabel?.text = selectedItem
        default: break
        }
    }


のように、親画面をいじってやるつもりだったが、なぜか空文字になっていた

それは、TableViewの選択時イベント

    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        selectedItem = list[indexPath.row]
    }

が後に走るため、登録する文字が空文字になっていた為

なんでやねん