Rails マイグレーションを使ったcommentの変更
すでに存在するカラムのコメントを変更したいので、以下のマイグレーションファイルを作りました。
class ChangeColumnNameToTables < ActiveRecord::Migration[6.0] def change change_column :tables, :name, comment: "コメントを変更" end end
しかし、db:migrateするとエラーが発生、、、
rake aborted! StandardError: An error has occurred, this and all later migrations canceled: undefined method `to_sym' for {:comment=>"コメントを変更"}:Hash Did you mean? to_s to_set
change_columnに型も必要でした><
class ChangeColumnNameToTables < ActiveRecord::Migration[6.0] def change # :stringを追加 change_column :tables, :name, :string, comment: "コメントを変更" end end
上記でエラーの解決はしましたが、そもそもchange_column_commentを使うべきでした😰
class ChangeColumnNameToTables < ActiveRecord::Migration[6.0] def change change_column_comment :tables, :name, "コメントを変更" end end
Navigation BarとTab Barを非表示にする
きっとまた悩むのでメモ
やりたかったこと
一部の画面のみNavigationBarやTabBarを非表示にしたい
問題点
Storyborard上のSimulated Metricsで以下のようにTop BarとBottom BarをNoneに設定すると、Storyboard上ではバーが消えるのに実行すると消えない
原因
Storyborard上のSimulated Metricsは
あくまでStoryBoard上でのレイアウトを助けるもので、実行時には無視される
らしい😱
Xcode4.6のSimulated Metricsについてメモ。 - Object for cutie
解決法:NavigationBarを非表示にする
override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) self.navigationController?.setNavigationBarHidden(true, animated: animated) } override func viewWillDisappear(_ animated: Bool) { super.viewWillDisappear(animated) self.navigationController?.setNavigationBarHidden(false, animated: animated) }
解決法:TabBarを非表示にする
ViewControllerのLayoutにあるHide Button Bar on Pushにチェックを入れる
おまけ
ToolBarを非表示にしたいときは、上記のviewWillAppearとviewWillDisappearに以下を記述する
// viewWillAppear self.navigationController?.setToolbarHidden(true, animated: animated) // viewWillDisappear self.navigationController?.setToolbarHidden(false, animated: animated)
NavigationController とTabBarControllerを一緒に使う方法
Storyboard上でNavigationControllerとTabBarControllerを一緒に使う方法のメモです。 (Swift3, Xcode)
NavigationControllerを追加
1. ViewControllerを選択した状態で「Editor -> Embed In -> Navigation Controller」を選択する。
↓Navigation Controllerが追加されることを確認する。
2. ViewControllerを以下のように2つ追加する。
追加したViewControllerそれぞれに「Editor -> Embed In -> Navigation Controller」を選択し、NavigationControllerを追加する。
TabBarControllerを追加
3. 2で追加したNavigationControllerのうち一方に「Editor -> Embed In -> Tab Bar Controller」を選択し、TabBarControllerを追加する。
↓Navigation ControllerにTab Bar Controllerが追加されることを確認する。
4. 2で追加したNavigationControllerのもう片方をTab Itemに設定する。 3のTab Bar ControllerからSegueをひっぱり、「Relationship Segue」の[view controllers]を選択する。
↓TabBarControllerと紐付いていることを確認する。
5.先頭のViewControllerにButtonを配置し、TabBarControllerに遷移するようにSegueをひっぱる。
動作確認
Buttonタップ後の画面(Item1選択時)
Item2選択時
コマンドをバインディングする
コマンド・バインディングってなに
前回の記事のデータ・バインディングで作ったサンプルでは、ボタンを押した時の処理はClickイベントを使って、コードビハインドで行っていました。
こんな感じで Xaml
<Button x:Name="CountButton" Click="CountUp_Click">Count Up!</Button>
コードビハインド
private void CountUp_Click(object sender, RoutedEventArgs e) { //ボタンがクリックされた時の処理 viewmodel.Count++; }
このようなボタンを押した時などの処理をコマンドにバインディングして行うのが、コマンド・バインディングです。
イベントを使った場合はコードビハインドに処理を書いていましたが、コマンドを使う場合はViewModelに処理を書くというのが特徴です。
ここで私は、イベントの方が、HTMLとJavaScriptのような感覚で書けるから理解しやすいのに、なぜコマンドを使わなければいけないのかと思いました。
なぜコマンドをバインディングするの
コマンドを使うのには理由がありました。
それは、Viewにあるコントロール(Buttonなど)と、ViewModelで定義する処理(実際はコマンドを実装したクラスのプロパティ)を結び付けることができるからです。
ここの説明は後ほどします。
そもそもWPFのMVVMパターンでは、コードビハインドには「VisualStudioが自動生成する以外のコードを書かない」といった基本方針があります。
そのため、MVVMパターンを用いた開発では、コードビハインドに処理を書くイベントは好まれないのです。
イベントの場合
コマンドの場合
コマンドをバインディングする方法を理解する
ではここからはコマンドをバインディングする方法を、コードと一緒に説明します。
まず、ViewModelにはViewにあるコントロールと結び付けるために、コマンドを実装したクラスのプロパティを用意します。
コマンドを実装したクラスの作り方
コマンドの実体はICommandインターフェースを実装したクラスです。
ICommandインターフェースは以下のようなメンバを持っており、コマンドを実装する時はこれらをオーバーライドする必要があります。
- Executeメソッド
コマンドを実行する。 - CanExecuteメソッド
コマンドが実行可能な状態にあるかどうかを判定する - CanExecuteChangedイベント
NotifyPropertyChangedインターフェイスのPropertyChangedイベントと同様、コマンド実行の可否が変化したことを通知するためのイベント。
コマンドのサンプルコード
CountUpCommand.cs
public class CountUpCommand : ICommand { private MainViewModel vm; public CountUpCommand(MainViewModel viewmodel) { vm = viewmodel; } public bool CanExecute(object parameter) { return vm.Count > 0; } public event EventHandler CanExecuteChanged; // コマンドが実行された時の処理 public void Execute(object parameter) { vm.Count++; } }
ViewModelに定義するプロパティの指定方法
ViewModelで定義するプロパティには先ほど作ったコマンドを実装したクラス(CountUpCommand)のプロパティを定義します。
MainViewModel.cs
public class MainViewModel : INotifyPropertyChanged { //このプロパティをView上のコントロールと結び付ける public ICommand CountUp { get; set; } public MainViewModel() { // コマンドを実装したクラスをプロパティに代入 CountUp = new CountUpCommand(this); } //・・・以下省略 }
Xaml上のコントロールとViewModelのプロパティをバインディングする方法
Xamlには Command="{Binding プロパティ名}"
と指定して、ViewModelのプロパティとバインディングすることを宣言します。
こうすることで、View上のボタンが押された時に、結び付いているViewModelのコマンドが実行されます。
MainWindow.xaml
<Button x:Name="CountButton" Command="{Binding CountUp}">Count Up!</Button>
コードビハインドには・・・
View上のコントロールとViewModelのコマンドを結び付けたことによって、コードビハインドにクリックされた時の処理を書かなくて済みます。
そのため、以下のように、DataContextの初期設定のみで良いので、余計なコードがなくシンプルです。
MainWindow.xaml.cs
public partial class MainWindow : Window { private MainViewModel viewmodel; public MainWindow() { InitializeComponent(); var viewmodel = new MainViewModel() { Count = 0 }; this.DataContext = viewmodel; } }
補足:ICommandが実装してある仕組み
今回のようなICommandを使ったコマンド実装のやり方では、毎回用途に合わせてコマンドを実装しなければならず、めんどくさいです。
そこで、RelayCommandまたはDelegateCommandというクラスがあるのですが、これは.NET Frameworkの標準クラスではありません。
PrismやMVVMLightやLivetなどのMVVMフレームワークを使うとこのクラスが含まれています。
ですが、テスト的にRelayCommandの実装を確認したい場合は、ここを確認します。
RelayCommandの使い方はこんな感じです。
ViewModelのコンストラクタに記述してください。
MainViewModel.cs
CountUp = new RelayCommand(() => { Count++; });
独自に実装したコマンドとは違い、クリックされた時の処理をViewModel側で実装しています。
フレームワークを使う時はこっちを使った方が便利そうです。
まとめ
- コマンドを使うとViewにあるコントロールと、ViewModelにある「コマンドを実装したクラスのプロパティ」を結び付けることができる
- ICommandインターフェースを実装したクラスをコマンドとして扱う。
とても参考になったサイト
サンプルコードを見ながら理解するMVVMの基礎的な実装 - Neutral Scent
連載:WPF入門:第6回 「コマンド」と「MVVMパターン」を理解する (1/3) - @IT
データ・バインディングを理解する
データ・バインディングとは
バインディングには「結合」という意味があり、データ・バインディングとは、MVVMパターンでいうViewとViewModelを結び付けるために提供されている仕組みのことです。
データ・バインディングの特徴は、一度だけ値を代入して終わりというわけではなく、値が変化するたびに結び付いている先の値もその都度即座に変化することです。
MVVMパターンにおけるXamlはView部分に相当しますが、Xaml内に{Binding プロパティ名}を記述し、ViewModelが保持するプロパティ値と結び付けることで、ViewにViewModelの値を反映することができます。
では実際のところどのようにして、ViewとViewModelの値をやりとりしているのでしょうか。
DataContextを介して値をやりとりする
ViewとViewModel間のやりとりはDataContextというプロパティを介してやりとりします。
Viewで表示したいViewModelのデータ・ソースをDataContextプロパティに渡すだけで、結び付けることができるのです。
WPFのアプリケーションにはUI層とデータ層の2つの層があり、UI層はMVVMパターンでいうViewで構成されます。
データ層はMVVMパターンでいうViewModelとModelで構成され、最初はnullとして始まり、DataContextプロパティを使用することで値を設定できます。
Button,Label,DataGrid,WindowなどのUIオブジェクトは、DataContextを介すことで簡単にやりとりすることができます。
DataContextはこのように設定します。
Xamlで設定する場合
(名前空間にxmlns:vm="clr-namespace:WpfApp.ViewModel
を指定しておく。)
<Window.DataContext> <vm:MainViewModel /> </Window.DataContext>
コードビハインドで設定する場合
(usingにWpfApp.ViewModel;
を追加しておく。)
this.DataContext = new MainViewModel();
XamlでDataContextの設定を行う時は、初期値の設定など細かい設定は行えませんが、コードビハインドで行う場合は以下のように初期値の設定を行うことができます。
InitializeComponent();の下に記述します。
var viewmodel = new MainViewModel() { hoge = "hogehoge" }; this.DataContext = viewmodel;
では実際のアプリケーションではどのように使うのか、次に説明していきます。
コードを見ながらデータ・バインディングを理解する
ここからは、ViewとViewModelを使ったデータ・バインディングのサンプルコードを見ながら理解を深めていきます。
ボタンをクリックすると、カウントアップする簡単なアプリケーションです。
1.まずはXaml(View)からです。
XamlのTextBlockには{Binding プロパティ名}と書いておき、ここにViewModelプロパティ値を表示する!というように宣言しておきます。
MainWindow.xaml
<StackPanel> <TextBlock Text="{Binding Count}"/> <Button x:Name="CountButton" Click="CountUp_Click">Count Up!</Button> </StackPanel>
2.次にViewModelです。
ViewModelにはViewに表示するプロパティ名を定義しておきます。
Bindingを使うためのDataContextとなるクラスのメンバ変数は常にgetter/setterを持ったプロパティでなければいけません。
なので、このように定義します。
private int countVal; public int Count { get { return countVal; } set { countVal = value; } }
でも、これだけでは値に変化があったときにViewの値も更新するようなコードを書いていないので、バインディングで値が反映されることはありません。
なので、ViewModelのプロパティ値に変化があった時に通知するようなイベントを用意します。
INotifyPropertyChanged(System.ComponentModel名前空間)というインターフェイスを継承して「プロパティ値が変更されたことをクライアントに通知する」イベントを実装しましょう。
MainViewModel.cs
public class MainViewModel : INotifyPropertyChanged { private int countVal; public int Count { get { return countVal; } set { countVal = value; // 通知するプロパティを引数に指定する。 // これを書かないと通知されない。 NotifyPropertyChanged("Count"); } } public event PropertyChangedEventHandler PropertyChanged; private void NotifyPropertyChanged(String info) { if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(info)); } } }
3.最後にコードビハインドです。
DataContextに初期値として0を設定し、ボタンクリックのイベントにインクリメントの処理を書いておきます。
ViewModelのプロパティ値をインクリメントさせると、バインディングで結び付いている先のViewの値もインクリメントします。
MainWindow.xaml.cs
public partial class MainWindow : Window { private MainViewModel viewmodel; public MainWindow() { InitializeComponent(); var viewmodel = new MainViewModel() { Count = 0 }; this.DataContext = viewmodel; } private void CountUp_Click(object sender, RoutedEventArgs e) { viewmodel.Count++; } }
以上でサンプルは完成です。
ボタンをクリックするとテキストブロックの数値がカウントアップされたらOKです。
まとめ
- データ・バインディングとはViewとViewModelを結び付けるために提供されている仕組み
- Viewに{Binding プロパティ名}と書き、ViewModelで定義しているプロパティ名と結び付ける
- ViewとViewModelの値はDataContextを介してやりとりする
- ViewModelのプロパティ値に変化があったことを通知するINotifyPropertyChangedインターフェースを継承し、「プロパティ値が変更されたことをクライアントに通知する」イベントを実装する
とても参考になったサイト
サンプルコードを見ながら理解するMVVMの基礎的な実装 - Neutral Scent
【WPF基礎】脱WPF初心者のための基礎知識 その1〜DataContextってなんぞ?〜: おっさんどりーむ 〜日本語で理解するプログラミング技術〜
連載:WPF入門:第5回 WPFの「データ・バインディング」を理解する (1/3) - @IT
Swift2.2でInstagram,Twitter,Facebookにシェアする方法
Swift2.2でInstagram,Twitter,Facebookにシェアする方法
UIActivityViewControllerのデフォルトではTwitterやFacebookへのシェアはうまくいくのに、Instagramのシェアがうまくいきませんでした。
調べてみるとInstagramは独自にアクティビティに追加してやらなければいけないようで、検索して出てきたコードを実際に試してみたのですが、InstagramへシェアすることはできてもTwitterやFacebookがアクティビティに表示されなくなってしまいました。
その解決策として、以下のコードにすることでTwitter,Facebook,Instagramへシェアする事ができました。
//シェアしたいImageView var imageViewForShare: UIImageView! //TwitterとFacebookはUIActivity let controller = UIActivityViewController(activityItems: [imageViewForShare.image!], applicationActivities: nil) //アクティビティに表示したくない機能やアプリを指定 let excludedActivityTypes = [ UIActivityTypePostToWeibo, UIActivityTypeMessage, UIActivityTypeMail, UIActivityTypePrint, UIActivityTypeCopyToPasteboard, UIActivityTypeAssignToContact, UIActivityTypeSaveToCameraRoll, UIActivityTypeAddToReadingList, UIActivityTypePostToFlickr, UIActivityTypePostToVimeo, UIActivityTypePostToTencentWeibo, UIActivityTypeAirDrop] controller.excludedActivityTypes = excludedActivityTypes self.presentViewController(controller, animated: true, completion: nil) let imageData = UIImageJPEGRepresentation(imageViewForShare.image!, 0.9) let fileURL = NSURL(fileURLWithPath: NSHomeDirectory()).URLByAppendingPathComponent("Documents/image.igo") imageData?.writeToURL(fileURL, atomically: true) documentInteractionController = UIDocumentInteractionController.init(URL: fileURL) // TwitterとFacebookと一緒にアクティビティに表示させたいのでUTIに"com.instagram.shareextension"を指定する documentInteractionController.UTI = "com.instagram.shareextension" if UIApplication.sharedApplication().canOpenURL(NSURL.init(string: "instagram://app")!) { documentInteractionController.presentOpenInMenuFromRect(self.view.frame, inView: self.view, animated: true) } else { print("Could not find Instagram app.") }
ポイント
UTIの指定
exclusivegramにすると共有先をInstagramのみに限定して表示することができる
"com.instagram.exclusivegram"
shareextensionにするとアクティビティにInstagramを追加表示することができる
"com.instagram.shareextension"
結果
AWSでWordPressのWebサイトを起動させる手順
1「インスタンスの作成」を押下
2 AWS Market Placeを選択し、検索欄にWordPressと入力
3 WordPress powered by Bitnami を選択
4 無料利用枠を選択し、「確認と作成」を押下
5 「作成」を押下
6 既存のキーペアを選択するか、新しいキーペアを作成する。
選択したプライベートキーファイルへのアクセス権があり〜・・・にチェックをいれ、「インスタンスの作成」を押下
7 作成されるのを待つ。
作成ログの表示をクリックして、「作成が完了しました」と表示されていたらOK!! 「次のインスタンスの作成が開始されました」の横のインスタンスIDをクリックする。
8 WordPress Webサイトをテストします。以下のページのパブリック IPにアクセスします。
9 WordPressのサイトが開きます
10 管理者画面にいきたいので、IPアドレスの後に「/admin」をつけます
11 ログイン画面
ユーザー名とパスワードが必要です。確認しましょう。
12 パスワードを確認するには、先ほどのAWSの画面で作成したインスタンス上で右クリックします。 インスタンスの設定 ->システムログの取得を選択してください。
13 システムログが表示されるので、スクロールしていくと下の方にpasswordが書いてあります。
14 では、WordPressのログイン画面に戻ってログインしてみます。
Username: user
Password: 先ほどシステムログで確認したパスワード
※ 初期状態のUsernameはuserです
14 管理者画面が表示されます
以上で完了です。
今回は試験的に作成しただけなのでインスタンス削除してます..