Swift iPad iPhone(別StoryBoard)実装方法
ストーリーボードは当然、iphone,ipad2つ用意する必要があります。
問題は、ソース。
同じ処理を書くことが多い。
それで、同じファイルに記述する方法を記載します。
まずファイル名。これは何でも良いが理解しやすい名前が良い。関係する名前がいいと思います。
この場合は
MsPersonData.swift
でファイル名を作成。
iPhone,iPadともにMsPersonDataUniを継承する。 MsPersonDataUniに共通処理を記載。
import UIKit import MapKit import CoreData /// 訪問先(iPad用ペイン) class MsPersonDataPad: MsPersonDataUni { @IBAction ~
ここで画面接続もする。 その際ストーリーボードはそれぞれiPhone,iPad違うファイルに接続可能。
iPhone画面 MsPersonDataUniを継承する。
/// 訪問先(iPhone用画面) class MsPersonDataPhn: MsPersonDataUni { //.......................................................................... //MARK: Outlets & Properties @IBOutlet weak var mkMap: MKMapView!
基本的に、画面接続のところが大多数を占めると思われる。 共通の処理はここに記載
ここでは画面接続もiPhone,iPadともに接続可能。
はじめは驚くかもしれない. 同じuiMailを接続している。
MsPersonDataUniはここで初めてUIViewControllerを継承している。
/// 訪問先本体(iPad & iPhone 共通) class MsPersonDataUni: UIViewController, MsMapAgentDelegate { @IBOutlet weak var uiMail: UITextField!
こちらはiPad
こちらはiPhone
それぞれ接続できている。
これはstoyBoardでそれぞれiPhone,iPadのクラスを指定している為
注意!segueでのprepar for segue はそれぞれiPhone,iPadのクラスに記載する必要があります。
次にAppDelegateの実装
//自作の関数 func storyboardName()->String{ if ((UIDevice.current).userInterfaceIdiom) == UIUserInterfaceIdiom.phone { //iPhoneのストーリーボードを返す return "iPhone" }else{ return "Main" } }
上記を呼ぶのがここ
//デリゲートメソッド func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { //画面の向きの設定 上記関数を呼んでいる let storyboard: UIStoryboard = UIStoryboard(name: storyboardName(), bundle: nil) self.window = UIWindow(frame: UIScreen.main.bounds) self.window?.rootViewController = storyboard.instantiateInitialViewController() //表示の更新 uiwindowは更新しないといけない self.window?.makeKeyAndVisible() let path = NSSearchPathForDirectoriesInDomains( .applicationSupportDirectory, .userDomainMask,true).first! return true } //初回起動時の画面の向き func application(_ application: UIApplication, supportedInterfaceOrientationsFor window: UIWindow?) -> UIInterfaceOrientationMask { if ((UIDevice.current).userInterfaceIdiom) == UIUserInterfaceIdiom.phone { //iPhoneのストーリーボードを返す return [.portrait , .portraitUpsideDown] }else{ //ipadのストーリーボードを返す //.landscapeでアクセスできるのは、readonlyのプロパティーだから return .landscape } }
デバイスの回転や、画面の向きの制御を行う UIInterfaceOrientationMask 定義
構造体で取得のみです。だから .portrait でアクセスできます。
public struct UIInterfaceOrientationMask : OptionSet { public static var portrait: UIInterfaceOrientationMask { get } public static var landscapeLeft: UIInterfaceOrientationMask { get }
Swift 起動時間が長いとクラッシュする問題と対策
iOSは読み込み時に、時間がかかりすぎるとクラッシュする。
今回はアプリのCoreDataのCSV読み込み時に時間がかかり、実機だと、起動できない問題が発生。
ちなみにシミュレーターだと、スペックが良いせいか、クラッシュしない。
対策
起動時間をへらす必要がある。しかし、CoreDataへの読み込み時間は変更のしようがない。 それで、StoryBoardで、別ウインドウを作成し、そこへ処理を投げることにした。
override func viewDidAppear(_ animated: Bool) { super.viewDidAppear() if datDics.count == 0 { //起動時間が15秒すぎるとアプリが落ちるのでIndicatorViewにて読む込処理をする. performSegue(withIdentifier: "toIndicator" , sender: nil) } }
viewDidAppearで処理をするところがポイント
viewDidLoadはまだ画面が生成されていない。そこでsegueで画面遷移しても、望む結果は得られなかった。
遷移先の画面ではインジケーターを表示して、先程起動時間が長いと言われていた関数を読み込む。
override func viewDidAppear(_ animated: Bool) { //インジケーター表示 label_indicator.startAnimating() //問題の読み込みに時間がかかる関数。約1万件のデータをマイグレーションしている。coredataは関数内でsave しているので問題ない。 Dic_iphone().setupDatDicFromCsv() //終わったらすぐに元画面に戻る。 performSegue(withIdentifier: "backToTop", sender: nil) }
viewDidAppearとviewDidLoadは同じキューでは無いようです。 わかる方いたら教えていただけますか.
Rails チュートリアル following followerの関係について
この画面の左側のfollowing followerの数はどのようにして表示されているか?を説明します。
とりあえずアドレスがusers/4なのでUsersControllerのshowが呼ばれているはず
UsersController
def show @user = User.find(params[:id]) @microposts = @user.microposts.paginate(page: params[:page]) end
userのidを取得
Viewの左画面
<aside class="col-md-4"> <section> <%= render 'shared/user_info' %> </section> <section> <%= render 'shared/stats' %> </section> <section> <%= render 'shared/micropost_form' %> </section> </aside> <div class="col-md-8"> <h3>Micropost Feed</h3> <%= render 'shared/feed' %> </div>
真中部分のここが数字を表示している。
<%= render 'shared/stats' %>
'shared/stats'が呼ばれている
Viewの中のパーシャル(部分テンプレート)を見て見ます。
_shared/stats
<% @user ||= current_user %> <div class="stats"> <a href="<%= following_user_path(@user) %>"> <strong id="following" class="stat"> <%= @user.followed_users.count %> </strong> following </a> <a href="<%= followers_user_path(@user) %>"> <strong id="followers" class="stat"> <%= @user.followers.count %> </strong> followers </a> </div>
<%= @user.followed_users.count %>で数字を表示
followed_usersはどこから来たのか?このブログのポイント
Userモデルに関連を宣言
class User < ApplicationRecord has_many :microposts, dependent: :destroy has_many :relationships, foreign_key: "follower_id", dependent: :destroy has_many :followed_users, through: :relationships, source: :followed
最後の行has_many :followed_users, through: :relationships, source: :followed
この行がポイント Userモデルはfollowed_usersという、プロパティを持つようになる。 それはrelationshipsテーブルを用いて関連付けられる。
そしてfollowedモデルを所有できるようになります。
followedモデルって何?
どこにも書いてないよ?
userテーブルのどこにもその様なカラムはない。
ないのになぜ、アクセスできるのか?
create_table "users", force: :cascade do |t| t.string "email", default: "", null: false t.string "encrypted_password", default: "", null: false t.string "reset_password_token" t.datetime "reset_password_sent_at" t.datetime "remember_created_at" t.integer "sign_in_count", default: 0, null: false t.datetime "current_sign_in_at" t.datetime "last_sign_in_at" t.string "current_sign_in_ip" t.string "last_sign_in_ip" t.datetime "created_at", null: false t.datetime "updated_at", null: false t.string "name" t.boolean "admin", default: false t.index ["email"], name: "index_users_on_email", unique: true t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true
Relationshipのモデル
followedというテーブルと関連づいているように見える。しかし実態はUser
class Relationship < ApplicationRecord belongs_to :follower , class_name: "User" belongs_to :followed , class_name: "User" validates :follower_id, presence: true validates :followed_id, presence: true end
has_many :followed_users, through: :relationships, source: :followed つまりこれは
relationshipsという中間テーブルを通して、followedを所有することになります。
少し難しいかも?
ポイントは、follower followedも同じuserテーブルを参照していることにあります。
自分自身のテーブルを参照しているのですね。
2つのユーザーが同名の外部キーを持つことはできません。
それで、何らかの方法でkeyの名前を変更しないといけない。
followerとしてフォローするのと
followedとしてフォローされているものは違う2つの値をそれぞれ持つのです。
routesファイルを見てみます。
Rails.application.routes.draw do devise_for :users, :controllers => { :registrations => "registrations" } resources :users, only: [:show, :index, :destroy] do member do get :following, :followers end end
これによりルーティングにより下記のrouteが定義されます。
画像参照元 Ruby on Rails チュートリアル:実例を使って Rails を学ぼう
なぜこれで行けるかというと、
Userコントローラーfollowingメソッドにより、followed_idカラムを取得しています。
def following?(other_user) relationships.find_by(followed_id: other_user.id) end def follow!(other_user) relationships.create!(followed_id: other_user.id) end
これにより、取得できるのですね。
Relationshipテーブルにはfollowed_idというカラムがあります。
また、余談ですがindexも設定してあります。
create_table "relationships", force: :cascade do |t| t.integer "follower_id" t.integer "followed_id" t.datetime "created_at", null: false t.datetime "updated_at", null: false t.index ["followed_id"], name: "index_relationships_on_followed_id" t.index ["follower_id", "followed_id"], name: "index_relationships_on_follower_id_and_followed_id", unique: true t.index ["follower_id"], name: "index_relationships_on_follower_id" end
インデックスを簡単に言うと対象のカラムのデータを取り出し、高速に検索できるように手を加えて保存しておいたものです
何度も頻繁に使うカラムだから、高速で検索できるようにしておいたのですね。
マイグレーションファイル生成時に設定しています。
class CreateRelationships < ActiveRecord::Migration[5.1] def change create_table :relationships do |t| t.integer :follower_id t.integer :followed_id t.timestamps end add_index :relationships, :follower_id add_index :relationships, :followed_id add_index :relationships, [:follower_id, :followed_id], unique: true end end
ここで
add_index :relationships, [:follower_id, :followed_id], unique: true
とあるので、あるユーザーが同じユーザーを複数回フォローすることを防ぐ事ができます。
流れをまとめると
UsersControllerのshowが呼ばれる
Viewには<%= @user.followed_users.count %>とある
followed_usersはどこから来たのかというと
Userモデルに関連付けがある。
has_many :followed_users, through: :relationships, source: :followed
Userモデルはfollowed_usersにアクセスできる。 それはrelationshipsテーブルを用いて関連付けられる。
そしてfollowedモデルを所有できるようになります。
relationshipsモデル。 実態はUserモデルとなっている。
belongs_to :followed , class_name: "User"
Userコントローラーの関数にて取得できる。
def following?(other_user) relationships.find_by(followed_id: other_user.id) end
ソースをきちんと読むのは大切ということがわかった。 間違えている点がありましたら、ご指導よろしくおねがいします。
Swift GoogleBook APIで書籍検索
作業フロー
1-エンドポイントの調査
2-API Keyの取得
3-jsonデータの分析
4-コード作成
5-iOSにてView作成
1-エンドポイント、(接続するアドレス)はここに記載されている
https://developers.google.com/books/docs/v1/using
具体的にはここ
https://www.googleapis.com/books/v1/volumes?q=flowers+inauthor:keyes&key=yourAPIKey
&keyのkey部分へ自分のキーを配置する
2-keyを取得してエンドポイントへアクセスする。するとこの様なjsonデータが返却される。
これを解析する。 今回はここに時間をかける。
{ "kind": "books#volumes", "totalItems": 100, "items": [ { "kind": "books#volume", "id": "_oG_iTxP1pIC", "etag": "fPjIOwmEaME", "selfLink": "https://www.googleapis.com/books/v1/volumes/_oG_iTxP1pIC", "volumeInfo": { **//ここの配下のtitleとdescriptionを取得したい** "title": "Flowers for Algernon", "authors": [ "Daniel Keyes" ], "publisher": "Houghton Mifflin Harcourt", "publishedDate": "2007-12-01", "description": "The beloved, classic story of a mentally disabled man whose experimental quest for intelligence mirrors that of Algernon, an extraordinary lab mouse.", "industryIdentifiers": [ { "type": "ISBN_10", "identifier": "0547539630" }, {
今回はtitleとdescriptionを取得することを目的とする。
構造体を宣言
欲しいものだけ最低限取得する。
型はエンコード、デコードできる、Codable型
まずitemsの取得、items配下は配列になっているのでItem型の配列とする。 nilになる可能性のないものはオプショナル型でなくても良い。
jsonデータ
"items": [ //ここで、配列ということがわかります。 { "kind": "books#volume", "id": "_oG_iTxP1pIC",
Swiftでの記載
struct SearchResut: Codable { let kind:String let items:[Item] // Item型の配列 }
更にたどっていくと、volumeInfoの下に目的のtitleとdescriptionがある。
ここはjsonオブジェクトなので、配列ではない。
VolumeInfoはVolumeInfo型にする。このようにして、型だけを継承していくイメージ。
あくまでもイメージ。構造体は継承できない。
struct Item: Codable{ let volumeInfo:VolumeInfo }
VolumeInfoの下に目的のtitleとdescriptionがある。
それは、String型
これ以上の階層は追っかけない。
struct VolumeInfo: Codable{ let title:String let description:String }
これで、受け取る用意はできた。 あとはjsonの取得。
//リクエストURL作成 guard let req_url = URL(string: "https://www.googleapis.com/books/v1/volumes?q=\(keyword_encode)") else{ return books
nilになる可能性があるので、guard文にしておく。
httpの取得は、通信エラーなどでnilが返却されることがあるので注意が必要。
本当は非同期処理がいい。 NSURLSessionクラスを利用すれば、非同期ででききる 他にもAlamofireという非同期処理のライブラリがある。これがいいらしい。
エラーの可能性があるので, Swiftのエラー処理である、do~catchで囲む nilでも落ちないので便利。
static func searchBooks(keyword:String)->[BookInfo]{ //返却したい型 var books:[BookInfo] = [] do{ //urlをデータ型へ変換 let data = try Data(contentsOf: req_url) //これで非同期処理にならない。 //jsonをデコード let searchResult = try JSONDecoder().decode(SearchResut.self, from: data) //デコードしたjsonを詰め込むsearchResultがトップなので、itemsはそのプロパティとして持っている for item in searchResult.items{ //item配下のvolumeInfoを詰め込む let volumeInfo = item.volumeInfo //volumeInfo配下のtitleを詰め込む let title = volumeInfo.title //volumeInfo配下のdescriptionを詰め込む。同じ階層にある let description = volumeInfo.description //最後に返却したい型を別の箇所で宣言している。bookInfo型 下記に記載 let bookInfo = BookInfo(title: title, description: description) //配列に詰め込んでいく books.append(bookInfo) } }catch{ print("エラーが出ました") } //BookInfo型の配列で返却 return books }
bookInfo型
struct BookInfo{ var title:String var description:String? }
これで戻り値でほしい書籍データを取得できた。
必要ないと思うが,TableViewへの表示も記載しておく。
//add var bookList:[BookInfo]? override func viewDidLoad() { super.viewDidLoad() //先程の関数 bookList = searchBooks(keyword: keyword!) //tableViewのデリゲートメソッドを2つ記載しないとエラーになる。 func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return bookList!.count } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell:UITableViewCell = tableView.dequeueReusableCell(withIdentifier: "bookCell", for: indexPath) cell.textLabel?.text = bookList![indexPath.row].title cell.textLabel?.font = UIFont.systemFont(ofSize: 15) return cell }
Pyton3 cloud9 環境設定 |未完|
cloud9のpythonバージョンが2から3へ切り替わらない
ここを参照、しかし問題は未解決。
Cloud9でpython3を動かす時にやっておきたいこと
pipとは
ピップと読むらしい。 パイソンのパッケージ管理ツール RailsのBundlerのようなものか。 またPyPI(パイピーアイ)というライブラリを集めたサイトがある。 Jupyter Notebookをインストールしておくとサンプルコードを追いやすくなる。
そのためにAnacondaをインストールする。
https://www.anaconda.com/download/#macos
ec2-user:~/environment $ python --version
Python 2.7.14
バージョンが2なので3へ変更
まずメニュー[Cloud9]→[Preferences]
which pythonでするとエイリアスだった。 よってpython3へエイリアスを設定。 python3のフォルダは以下
ec2-user:/usr/bin $ ls -l| grep python lrwxrwxrwx 1 root root 24 Aug 7 13:11 python -> /etc/alternatives/python lrwxrwxrwx 1 root root 17 Aug 7 13:11 python2 -> /usr/bin/python27 -rwxr-xr-x 1 root root 5120 May 2 18:32 python27 -rwxr-xr-x 1 root root 5120 May 2 18:32 python2.7 -rwxr-xr-x 1 root root 1846 May 2 18:31 python2.7-config lrwxrwxrwx 1 root root 25 Aug 7 13:11 python3 -> /etc/alternatives/python3 -rwxr-xr-x 3 root root 6872 Apr 26 00:16 python36 -rwxr-xr-x 3 root root 6872 Apr 26 00:16 python3.6 lrwxrwxrwx 1 root root 17 Aug 7 13:11 python3.6-config -> python3.6m-config -rwxr-xr-x 3 root root 6872 Apr 26 00:16 python3.6m -rwxr-xr-x 1 root root 173 Apr 26 00:16 python3.6m-config -rwxr-xr-x 1 root root 3373 Apr 25 23:57 python3.6m-x86_64
確認して
ec2-user:/usr/bin $ alias python='/etc/alternatives/python3' ec2-user:/usr/bin $ python --version Python 3.6.5
と思いきやpipのバージョンが違う
ec2-user:/usr/bin $ readlink -f /usr/bin/python /usr/bin/python36 ec2-user:/usr/bin $ pip --version pip 9.0.3 from /usr/lib/python2.7/dist-packages (python 2.7) ec2-user:/usr/bin $ python3 --version Python 3.6.5 ec2-user:/usr/bin $ pip3 --version bash: pip3: command not found
pip3がないと言われているのでインストールしようと思ったが
AWS Cloud9でPython3を使う方法 のサイトによると
書き換え後はsourceコマンドをお忘れなく あとある。もうちょっと書いてくれると嬉しい sourceとは、環境設定フアイルをカスタマイズしたときには、現在のシェル環境に反映させるためのもの シェルファイルは.tcshrcなどrcの文字が最後についていることが多い。 ~/.bashrc のようである。
ec2-user:/usr/bin $ source ~/.bashrc function
戻っているいみがわからない。
ec2-user:/usr/bin $ python --version Python 2.7.14
今度はpython36にリンクを貼る
ec2-user:/usr/bin $ alias python='/usr/bin/python36' ec2-user:/usr/bin $ python --version Python 3.6.5
sourceコマンドを実行するとバージョンが戻ってしまう。??
ec2-user:/usr/bin $ source ~/.bashrc function ec2-user:/usr/bin $ python --version Python 2.7.14
??
今度はパスを指定せずにエイリアスを設定。
うまく行った。??
理由がわかならいので、良くない。
どなたか理由教えていただけますか?
ec2-user:/usr/bin $ alias python=python36 ec2-user:/usr/bin $ python --version Python 3.6.5
そしてどちらのバージョンを使うかを決めるコマンドを入力。
ec2-user:/usr/bin $ sudo update-alternatives --config python ec2-user:/usr/bin $ pip --version pip 9.0.3 from /usr/lib/python3.6/dist-packages (python 3.6)
モデルが詰まった機械学習パッケージをインストール
pip install scikit-learn
しかしエラー
Exception: Traceback (most recent call last): File "/usr/lib/python3.6/dist-packages/pip/basecommand.py", line 215, in main status = self.run(options, args) File "/usr/lib/python3.6/dist-packages/pip/commands/install.py", line 342, in run
Rails 環境格闘記 !未完成!
Ignoring executable-hooks-1.4.2 because its extensions are not built. Try: gem pristine executable-hooks --version 1.4.2
このエラーが出る。 nokogirを指定の通り入れてもだめ。 この場合pathが通っていないことが多い よってgem の環境を確認.
$ gem environment
RubyGems Environment: - RUBYGEMS VERSION: 2.7.6 - RUBY VERSION: 2.5.1 (2018-03-29 patchlevel 57) [x86_64-linux] - INSTALLATION DIRECTORY: /usr/local/rvm/gems/ruby-2.4.1 - USER INSTALLATION DIRECTORY: /home/ec2-user/.gem/ruby/2.5.0 - RUBY EXECUTABLE: /home/ec2-user/.rbenv/versions/2.5.1/bin/ruby - EXECUTABLE DIRECTORY: /usr/local/rvm/gems/ruby-2.4.1/bin - SPEC CACHE DIRECTORY: /home/ec2-user/.gem/specs - SYSTEM CONFIGURATION DIRECTORY: /home/ec2-user/.rbenv/versions/2.5.1/etc - RUBYGEMS PLATFORMS: - ruby - x86_64-linux - GEM PATHS: - /usr/local/rvm/gems/ruby-2.4.1 - /usr/local/rvm/gems/ruby-2.4.1@global - GEM CONFIGURATION: - :update_sources => true - :verbose => true - :backtrace => false - :bulk_threshold => 1000
rbenvで入れたのに実行環境がRVMになっている?? 再度rvmで入れることが可能なのか? このサイトを見ると不可能にちかい。というかエラーになること間違いない。
RVMからrbenvに移行した手順まとめ · GitHub rvmインストールしていないのだから、pathをrbenvに向けてあげればいいだけ。 以下明日に続く
Swift httpsサイトへ接続設定
infoPolistへ以下を追加
App Transport Security Settings
を入力して三角マークが出ている同じ行でプラスボタン押すのがポイント
Allow Arbitrary Loadsを更に入力。
しかしシミュレーターがアマゾンへ接続できない。
他のサイトならできるのに。??
アバストセキュリティーが邪魔してた。
ここから
WEBシールドを無効化する。