Rails 開発-1 Bootstrapの導入
application.html.erb
BootstrapとJavaScriptを読み込み
Bootstrap本家サイト GitHub - twbs/bootstrap-rubygem: Bootstrap 4 rubygem for Rails / Sprockets / Hanami / etc
gemはこんな感じ
gem 'bootstrap', '~> 4.0.0' gem 'mini_racer' gem 'autoprefixer-rails', '~> 9.3', '>= 9.3.1' gem 'sprockets-rails' gem 'jquery-rails'
app/assets/stylesheets/application.scss
に書き加える。cssファイルしなかに場合は、scssにリネイムする。
@import "bootstrap";
を加えて = require and = require_treeは削除 Bootstrapを利用できなくなるので
application.jsに下記を追加
//= require jquery3 //= require popper //= require bootstrap-sprockets
ここでBootstrapがきいているかチェック 画面がおかしかったの
app>asset.stylesheetの中におかしなファイルができていた
application.css
application.css.map
これを削除すると、画面がもとに戻る。 Bootstrapも効いている。
でエラー
config/initializers/assets.rb に読み込むcssを記載 ディレクトリごと読み込みたい場合は
config.assets.precompile += ['directory/*']
config>initializer>asset.rb
Rails.application.config.assets.precompile += ['clients/*']
Bootstrapも効きました。
しかしrequire rails-ujsが2回呼ばれるエラー
画面のファイルの
//= require rails-ujs
を削除すると呼ばれなくなった。 bootstrapとかぶっているgemのようだ。
めでたし、めでたし
Rails 日付を日本語表示にする。 strftimeメソッド
このように表示されてしまう。
これを日本語表記で表示したい。
strftimeメソッドを利用する。
Rubyの組み込みオブジェクト
strftime(フォーマット引数)
これだけ
<%= datperson.updated_at.strftime('%Y年%m月%d日 %H時%M分') %>
config>initializer>time_formats.rbファイルを作成。
そこに記載しておく そうするとどのファイルからでも、引数でとってこれる。
引数はdatetime_jp
#Time型のDATE_FORMATSに[:datetime_jp]のキーでフォーマットを設定している = '%Y年' Time::DATE_FORMATS[:datetime_jp] = '%Y年%m月%d日 %H時 %M分'
めでたしめでたし。
Swift getter setterの処理-セッターゲッターとは
ゲッター、セッターとはそもそも何か?
コンピューテッドプロパティといって、値を保持せずに、算出するプロパティのこと。
??となるので、まずコードを参照。
コンピューテッドプロパティの定義はこのように記載
var プロパティ名:型名{ get{ 値を返す処理 } set{ 値を更新する処理 代入された値にはnewValueとしてアクセス可能 } }
実際に書いてみる
class Item { // 税抜き価格 var price:Double = 0 // 税込み価格 var intaxPrice:Double { // 値を取得するときに呼ばれる。 get{ return self.price*1.08 } // 値がセットされるときに呼ばれる。 set{ price = newValue/1.08 } } }
これを実行するとこうなる。
var item = Item() item.intaxPrice = 108.0 //intaxPriceはコンピューテッドプロパティで値を持たない、算出する //よってここでは、itemに100がセットされる print(item.price) //100になっている item.price = 150 //今度はpriceに値150をセット print(item.intaxPrice) //162になっている //intaxPriceには何も変更を加えていないのに、162になる //これが値を保持せずに、値を算出するということ
参考サイト 【Swift】セッター(Setter)、ゲッター(Getter)の処理を書く - Qiita
これができたから何?となると思います。
それで、実際に利用したソースを示します。
画面を越えて、値を状態を保持したいときなどに、利用します。
プロトコルを宣言して、そのプロトコルにゲッター、セッターを定義しておけば、そのプロトコルを利用したクラスでは、このゲッターセッターが利用できます。 左画面から右画面へテーブルビューへ表示する、配列を渡したいとします。 別Viewになっていますので、そのままでは値を渡せません。 ここで登場するのが、先程のゲッターセッター、そしてプロトコルです。
まずプロトコルの宣言 どこに記載しても良いです。
protocol wordDelegate { var ofDics:[DatDic]?{get set} }
ofDics という変数名で型は DatDic型の配列になっています。
そして{ }でゲッターセッターが宣言されています。 一つの値を変更すれば、もう片方に値がセットされると言うことです。
目的は左画面のtableViewに表示され、タッチしたところから下にあるものすべての配列を、右画面に渡します。
では、左画面のソースを見てみましょう。
//デリゲートのインスタンス var ofWord: wordDelegate? //テーブルをタッチしたときの処理 func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { //配列の特定の行から最後までをdetailDicへ移動する ofWord?.ofDics = Array(datDics![indexPath.row..<datDics!.endIndex]) //ここでofWordを利用して、プロトコルのプロパティにアクセスしています。 //別の画面のプロパティになります。 }
では遷移先、右画面では、どのように記載するのでしょうか?
var ofDics: [DatDic]?{ set{//set newvalu=左でsetした値が入る datDics = newValue tableView.reloadData() //左でセットした値が入る。それでtableをリロードする。 } get{//setしたdatDicsを表示しているだけ return datDics } } //メンバ変数 ゲッターで帰ってきた値が詰め込まれるため。 var datDics:[DatDic]?
あとはTableViewにdatDicsを表示するだけ。 左から右に値を渡せました。
swift git ignore Mac 特殊ファイルの除外追加
MAC独特の特殊ファイルの除外を追加 それだけです。
# Xcode # # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore ## Mac File System specific files .DS_Store ## Build generatedビルドファイルは無視 build/ DerivedData/ ## Various settings *.pbxuser !default.pbxuser *.mode1v3 !default.mode1v3 *.mode2v3 !default.mode2v3 *.perspectivev3 !default.perspectivev3 xcuserdata/ ## Other *.moved-aside *.xccheckout *.xcscmblueprint ## Obj-C/Swift specific *.hmap *.ipa *.dSYM.zip *.dSYM ## Playgrounds timeline.xctimeline playground.xcworkspace # Swift Package Manager # # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. # Packages/ # Package.pins # Package.resolved .build/ # CocoaPods # # We recommend against adding the Pods directory to your .gitignore. However # you should judge for yourself, the pros and cons are mentioned at: # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control # Pods/ # Carthage # # Add this line if you want to avoid checking in source code from Carthage dependencies. # Carthage/Checkouts Carthage/Build # fastlane # # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the # screenshots whenever they are needed. # For more information about the recommended setup visit: # https://docs.fastlane.tools/best-practices/source-control/#source-control fastlane/report.xml fastlane/Preview.html fastlane/screenshots fastlane/test_output
Swift XMLParserでCDATAをparseをする !編集途中読まないこと!
まずCDATAとは
XMLはマークアップ言語であるため、XML文書を記述する際には、マークアップ用として指定された記号を直接文字として記述することができない。これを記述してもマークアップの指定と解釈され、文字として解釈されないためである。 しかし、マークアップ専用の記号であって、通常の文字として扱う特例的な部分をつくることができる。これを、CDATAセクションという。
CDATAセクションは、<![CDATA[という文字列で始まり、]]>という文字列で終わる。
CDATAセクションの中にはHTMLタグも埋め込むことができるので、RSSを配信している側にとっては便利。
でもXMLパーサーを回避するので、XML解析する側にとっては嫌な存在です。 ただ、XMLParserにはCDATAを解析するメソッドが用意されている。
(初心者向け)NSXMLParserでCDATAを解析する - Qiita
中身はbitコード。
ちなみにエンドポイントはこんなかんじ。
<?xml version="1.0" encoding="UTF-8"?>
螢イ荳企ォ倥′20��い繝��縲・PS縺ッ41��い繝��縺ィ縲�7縲�9譛域悄縺ョ譁ー險倬鹸繧帝#謌�
この中からtitleとid(リンク先)を取得したい。
まず普通にparseを始める。
func startDownload() { self.items = [] if let url = URL( string: "http://www.apple.com/jp/pr/feeds/pr.rss"){ //インスタンスの生成 if let parser = XMLParser(contentsOf: url) { self.parser = parser self.parser.delegate = self //これにより下記メソッドが呼ばれる self.parser.parse() } } }
ここで3つのメソッドがポイント
一つは、要素の開始ごとに呼ばれるメソッド
もう一つは、CDATAごとに呼ばれるメソッド
もう一つは、要素の終了ごとに呼ばれるメソッド
//要素の開始タグごとに呼ばれる func parser(_ parser: XMLParser, didStartElement elementName: String, namespaceURI: String?, qualifiedName qName: String?, attributes attributeDict: [String : String]) // CDATAを見つけるごとに呼ばれる エンコードする必要があり func parser(_ parser: XMLParser, foundCDATA CDATABlock: Data) { //要素の終了ごとに呼ばれるメソッド func parser(_ parser: XMLParser, didEndElement elementName: String, namespaceURI: String?, qualifiedName qName: String?) {
エンドポイントを見ると、要素の開始タグ
var readOrNot:Bool = false //他のメンバ変数 var parser:XMLParser! var items = [Item]() var item:Item? var currentString:String?
これにより、CDATA部分で読み込むかどうかを判断させる方針とする。
これは上記要素の開始ごとに呼ばれるメソッドに記載する。
それによりCDATAの読み込み時に切り分けをする。 すべてのタグを読み込んだら大変なことになる。
if elementName == "title" || elementName == "id" { readOrNot = true
Swift TabBarでの値の受け渡し
TabbarControllerでの値の受け渡しの記事がなかったのでまとめておきます。
Swift Geocode googlemap からappleの関数へ変更
このソースが動かなくなった
/// 住所ボタン=住所の位置を登録して、登録した位置を中央に表示してピンを落とす。 @IBAction func centerLocationFromAddress(_ sender: Any) { //住所のnilチェック guard let addr = uiJusho.text, !addr.isEmpty else { return } //Google geolocation関数呼び出し、ここが2018.2よりGoogleの仕様変更で利用できない //軽度緯度をAPIで取得しているだけ 下記に示す guard let location = GmsUtils.getLocationOf(addr) else { MsAlert.confirmOk(self,"MsgMapGmsGeocodeNoResponse") return } //返却された、経度、緯度をMAPの中心に表示する関数を呼び出し msMapAgent?.center(point:location, animated:true) registarPersonLocation(location) //キーボードを閉じる textUiPlaceAdjuster.endEditing() }
getLocationOf(addr)
/// 住所の位置を検索し返す static func getLocationOf(_ address: String) -> CLLocationCoordinate2D? { // address は query部分に当たるので所定のエンコードを行う guard let encAddr = address.addingPercentEncoding( withAllowedCharacters: NSCharacterSet.urlQueryAllowed), !encAddr.isEmpty else { return nil } // goole geocode サービス要求:dataが返されない事もありうる。 let strUrl = "https://maps.google.com/maps/api/geocode/json?address=\(encAddr)&sensor=false" guard let url = URL(string: strUrl), let data = try? Data(contentsOf: url) else { return nil } //JSONSerializationにて解析 // 結果を解析 guard let json = try? JSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions.allowFragments) as! NSDictionary, (json["status"] as? String) == "OK" else { return nil } guard let result = json["results"] as? NSArray, result.count > 0, let geometry = (result[0] as! NSDictionary)["geometry"] as? NSDictionary, let location = geometry["location"] as? NSDictionary, let lat = location["lat"] as? Double, let lng = location["lng"] as? Double else { //print("\n\(lat), \(lng)") return nil } // ここで返却している CLLocationDegrees型で //init(latitude: CLLocationDegrees, longitude: CLLocationDegrees) return CLLocationCoordinate2D(latitude: lat, longitude: lng) }
Appleの関数のみでの実装
/// 検査ボタン押下 func searchBarSearchButtonClicked(_ searchBar: UISearchBar){ // キーボードを納め、探す場所が入力されていれば、それを探す self.view.endEditing(true) guard let adress = uiSearchLocation.text, !adress.isEmpty else { return } // 検索は非同期で、時間がかかることもあるのでキャンセルボタンを表示し // 結果が返されるまでの間でキャンセル可能とする searchBar.showsCancelButton = true CLGeocoder().geocodeAddressString( adress, completionHandler:geocodeComplete) }
func geocodeComplete(places:[CLPlacemark]?, error:Error?) { self.uiSearchLocation.showsCancelButton = false if error != nil { // localizedDescription には次のような文字列が入っていた // The operation couldn’t be completed. (kCLErrorDomain error 8.) // そのまま出すとオペレータの混乱を招くので MagMsgGmsGeocodeNoResponse // を使う MsAlert.confirmOk(self,"MagMsgGmsGeocodeNoResponse") return } if let _places = places, !_places.isEmpty { if let ll = _places[0].location?.coordinate { self.msMapAgent.center(point:ll, animated:true) } } }