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上ではバーが消えるのに実行すると消えない

f:id:marikooota:20171127221344p:plain

原因

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にチェックを入れる

f:id:marikooota:20171127222134p:plain

おまけ

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」を選択する。 f:id:marikooota:20170910200129p:plain

↓Navigation Controllerが追加されることを確認する。 f:id:marikooota:20170910200642p:plain

2. ViewControllerを以下のように2つ追加する。 f:id:marikooota:20170910201128p:plain

追加したViewControllerそれぞれに「Editor -> Embed In -> Navigation Controller」を選択し、NavigationControllerを追加する。 f:id:marikooota:20170910201443p:plain

TabBarControllerを追加

3. 2で追加したNavigationControllerのうち一方に「Editor -> Embed In -> Tab Bar Controller」を選択し、TabBarControllerを追加する。 f:id:marikooota:20170910205441p:plain

↓Navigation ControllerにTab Bar Controllerが追加されることを確認する。 f:id:marikooota:20170910205627p:plain

4. 2で追加したNavigationControllerのもう片方をTab Itemに設定する。 3のTab Bar ControllerからSegueをひっぱり、「Relationship Segue」の[view controllers]を選択する。

↓TabBarControllerと紐付いていることを確認する。 f:id:marikooota:20170910210143p:plain

5.先頭のViewControllerにButtonを配置し、TabBarControllerに遷移するようにSegueをひっぱる。 f:id:marikooota:20170910210419p:plain

動作確認

f:id:marikooota:20170910210628p:plain

Buttonタップ後の画面(Item1選択時)
f:id:marikooota:20170910210644p:plain


Item2選択時
f:id:marikooota:20170910210703p:plain

コマンドをバインディングする

コマンド・バインディングってなに

前回の記事のデータ・バインディングで作ったサンプルでは、ボタンを押した時の処理は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で定義する処理(実際はコマンドを実装したクラスのプロパティ)を結び付けることができるからです。
ここの説明は後ほどします。

そもそもWPFMVVMパターンでは、コードビハインドには「VisualStudioが自動生成する以外のコードを書かない」といった基本方針があります。
そのため、MVVMパターンを用いた開発では、コードビハインドに処理を書くイベントは好まれないのです。

イベントの場合
f:id:marikooota:20170530233822p:plain

コマンドの場合
f:id:marikooota:20170530233845p:plain

コマンドをバインディングする方法を理解する

ではここからはコマンドをバインディングする方法を、コードと一緒に説明します。
まず、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の値を反映することができます。

f:id:marikooota:20170530001607p:plain

では実際のところどのようにして、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のデフォルトではTwitterFacebookへのシェアはうまくいくのに、Instagramのシェアがうまくいきませんでした。
調べてみるとInstagramは独自にアクティビティに追加してやらなければいけないようで、検索して出てきたコードを実際に試してみたのですが、InstagramへシェアすることはできてもTwitterFacebookがアクティビティに表示されなくなってしまいました。
その解決策として、以下のコードにすることで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"

結果

f:id:marikooota:20170410192930j:plain

AWSでWordPressのWebサイトを起動させる手順

1インスタンスの作成」を押下

f:id:marikooota:20170228195440p:plain

2 AWS Market Placeを選択し、検索欄にWordPressと入力

f:id:marikooota:20170228195807p:plain

3 WordPress powered by Bitnami を選択

f:id:marikooota:20170228200156p:plain

4 無料利用枠を選択し、「確認と作成」を押下

f:id:marikooota:20170228200415p:plain

5作成」を押下

f:id:marikooota:20170228200716p:plain

6 既存のキーペアを選択するか、新しいキーペアを作成する。

f:id:marikooota:20170228201351p:plain

選択したプライベートキーファイルへのアクセス権があり〜・・・にチェックをいれ、「インスタンスの作成」を押下

7 作成されるのを待つ。

f:id:marikooota:20170228220828p:plain

作成ログの表示をクリックして、「作成が完了しました」と表示されていたらOK!! 「次のインスタンスの作成が開始されました」の横のインスタンスIDをクリックする。

8 WordPress Webサイトをテストします。以下のページのパブリック IPにアクセスします。

f:id:marikooota:20170228221711p:plain

9 WordPressのサイトが開きます

f:id:marikooota:20170228213541p:plain

10 管理者画面にいきたいので、IPアドレスの後に「/admin」をつけます

f:id:marikooota:20170228213806p:plain

11 ログイン画面

f:id:marikooota:20170228213944p:plain

ユーザー名とパスワードが必要です。確認しましょう。

12 パスワードを確認するには、先ほどのAWSの画面で作成したインスタンス上で右クリックします。 インスタンスの設定 ->システムログの取得を選択してください。

f:id:marikooota:20170228221748p:plain

13 システムログが表示されるので、スクロールしていくと下の方にpasswordが書いてあります。

f:id:marikooota:20170228221233p:plain

14 では、WordPressのログイン画面に戻ってログインしてみます。

Username: user
Password: 先ほどシステムログで確認したパスワード

※ 初期状態のUsernameはuserです

f:id:marikooota:20170228214913p:plain

14 管理者画面が表示されます

f:id:marikooota:20170228215258p:plain

以上で完了です。

今回は試験的に作成しただけなのでインスタンス削除してます..