iOSで表示要素を動的に消して親ビューの高さを変える

はじめに

ログイン前後で表示要素の数を動的にし、ビューの高さを変えるという仕様があり、実現方法を調べました。

意外とめんどくさかった。もっと効率の良いやり方もありそうです。

バージョン

XCode Version 7.3.1 (7D1014)

サンプル

ラベル2つとボタン1つを持つフッターがあり、状態によって2つ目のラベルを非表示にして、その分ボタンを上に詰めるという仕様で考えます。

やりかた

制約

添付のような状態にします。

ポイントはボタンのconstraintsにLabel2を使わないこと。

ここでは高さと左右下のマージンだけ設定しています。

f:id:y_sumida:20160615152027p:plain

コード

調べるまで知らなかったのですがconstraintsもIBOutletでViewControllerに接続できます。

フッターの高さとLabel2の上マージンを接続して、viewDidLoadでLabel2を非表示にするため高さを0に、空いた隙間を詰めるためフッターの高さを調整しています。

確認用にボタンタップで表示するようにしています。

もちろん、他のタイミングでもできるのでやりたいことに合わせて読み替えましょう。

ちなみに、Label2自体をIBOutletで接続してhiddentrueで非表示にしても、htmlと違って高さを維持するのでうまくいかないです。

import UIKit

class ViewController: UIViewController {

    @IBOutlet weak var footerHeight: NSLayoutConstraint!
    @IBOutlet weak var label2TopMargin: NSLayoutConstraint!

    override func viewDidLoad() {
        super.viewDidLoad()
        self.label2TopMargin.constant = 0
        self.footerHeight.constant = 120
    }

    @IBAction func tapButton(sender: AnyObject) {
        self.label2TopMargin.constant = 4
        self.footerHeight.constant = 150
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }
}

実行結果

初期ロード時

Label2が非表示でLabel1とボタンの間が詰まっています。

f:id:y_sumida:20160615152125p:plain

ボタンタップ時

フッターの高さが高くなり、Label2を表示しています。

f:id:y_sumida:20160615152135p:plain

PHPのDateTimeクラスを使って日時の差分を取る時に注意すること

はじめに

とても今さら感のあるネタですが自分用にメモ。

プログラムで日付や時間を比較して何かを処理したい場合は多いはず。

PHPだとDateTimeクラスというのがあって、比較演算子==><などが使えます。

大小比較だけでなく、日時としての差分を取るためのDateTime::diff()メソッドも用意されていて、2つのDateTimeクラスの差分を表すDateIntervalクラスを返します。

DateInterval::format()メソッドを使って差分を確認するのですが、ちょっと癖があると思うので注意が必要です。

使い方は、下記の実験を見て貰えばわかるはず。

実験

翌日との比較

コード

<?php
$datetime1 = new DateTime('2015-11-20');
$datetime2 = new DateTime('2015-11-21');
$interval = $datetime1->diff($datetime2);

// %Rは符号を出力するオプション
echo $interval->format('%R%Y') . "\n"; // 年
echo $interval->format('%R%M') . "\n"; // 月
echo $interval->format('%R%D') . "\n"; // 日
echo $interval->format('%R%H') . "\n"; // 時
echo $interval->format('%R%I') . "\n"; // 分

結果

+00 // 年
+00 // 月
+01 // 日
+00 // 時 ?
+00 // 分 ??

翌月の同日との比較

コード

<?php
$datetime1 = new DateTime('2015-11-20 ');
$datetime2 = new DateTime('2015-12-20');
$interval = $datetime1->diff($datetime2);

echo $interval->format('%R%Y') . "\n"; // 年
echo $interval->format('%R%M') . "\n"; // 月
echo $interval->format('%R%D') . "\n"; // 日
echo $interval->format('%R%H') . "\n"; // 時
echo $interval->format('%R%I') . "\n"; // 分

結果

+00 // 年
+01 // 月
+00 // 日 ?
+00 // 時 ??
+00 // 分 ???

翌月の同日との比較その2

コード

<?php
$datetime1 = new DateTime('2015-11-20 ');
$datetime2 = new DateTime('2015-12-20');
$interval = $datetime1->diff($datetime2);

echo $interval->format('%R%Y') . "\n"; // 年
echo $interval->format('%R%M') . "\n"; // 月
echo $interval->format('%R%a') . "\n"; // 日 Dではなくaオプション(総日数)
echo $interval->format('%R%H') . "\n"; // 時
echo $interval->format('%R%I') . "\n"; // 分

結果

+00 // 年
+01 // 月
+30 // 日 やったぜ!
+00 // 時
+00 // 分

解説

要するに、DateTime::diff()で算出するのは、2つの日付の年月日時分秒のそれぞれの差分ということです。

上記の実験では%Hで時間の差を表示すると、どちらも時間が省略されているので0時と解釈されて、0時と0時の比較なので差は0時間です。

今日の0時と翌日の0時で比較した場合、差として24時間を期待したので、ちょっとはまりました。

唯一、日数のみ%aオプションというのが用意されていて総日数が取得できます。

全部にそういうオプション欲しい。

参考

http://php.net/manual/ja/datetime.diff.php

FuelPHPでデフォルトのタイムゾーンと異なるタイムゾーンの日付を取得したい

はじめに

通常、FuelPHPにおけるタイムゾーンは`fuel/app/config/config.php`の`default_timezone`で設定します。

でも、ある日付を別のタイムゾーン、例えばUTC協定世界時)に変換したい時ってありますよね。

WebAPIの日付指定がUTC限定だったり。

そんな時は、Fuelには便利なクラスが用意されてるので、それを使いましょう。

やりかた

Dateクラスを使います。

コード

タイムゾーンが`Asia/Tokyo`の前提です。

`Date::display_timezonel()`でutcを設定した上で、`Date::format()`の第二引数に`true`を指定すると、`Date::display_timezonel()`の日付を取得できます。

簡単ですね。

$date = \Date::create_from_string('2015/11/11/23:00', "%Y/%m/%d%H:%M");
$date->display_timezone('utc');
echo $date->format('%Y/%m/%dT%H:%M', true);
出力
2015/11/11/14:00

Vagrantのベースボックス更新をするスクリプト

はじめに

最近、仕事でVagrantを使って仮想環境を構築して使うことが多いです。

たいていの場合、最初にここからベースボックスを取得して、パッチ更新、各種ミドルウェアのインストールなどをしてメンバーに配布します。

開発を進める中で、ボックスの内容が大きく変わったときや、プロビジョニングに時間がかかるようになってきたら、vagrant packageでボックスを更新して再配布してます。

再配布時にはvagrant destroy -> vagrant box remove -> vagrant box addという手順でやっていたのですが、オプションを眺めていたらvagrant box add --clean --forcevagrant box removevagrant box add同等ということを知りました。

わかったついでに、黒い画面が苦手な人にも配布したりするので、できればコマンド一発にしたいと思ってシェルスクリプトにしてみました。

ファイル名固定だったり、destroy前に確認してなかったり、改善余地はありますが使えるはず。

json使う方法も検討したけど、バージョニングするほどでもないなってことで見送りました。

使い方

配布したVagrantfileとpackage.boxと同じディレクトリに置いて実行。

私のチームではVagrantfileと一緒にコミットしてます。

コード

#!/bin/sh
if [ ! -e ./Vagrantfile -o ! -e ./package.box ]; then
    echo "not found ./Vagrantfile or ./package.box"
    exit
fi

BOX=`sed -n 's/^.*config.vm.box.*=.*"\(.*\)"/\1/p' ./Vagrantfile`

if [ -z ${BOX} ]; then
    echo "not found config.vm.box parameter in ./Vagrantfile"
    exit
fi

echo "##### ${BOX} setup start\n"

echo "##### vagrant destroy\n"
vagrant destroy -f

echo "##### vagrant box add ${BOX}\n"
vagrant box add -c -f ${BOX} ./package.box

echo "##### ${BOX} setup end\n"

PHPのsimplexml_load_file()でXMLを扱うときに注意すること

はじめに

XMLは、JSONと違って同じ名前の要素を複数持つことができるのですが、PHPのsimplexml_load_file関数で扱うときにちょっとめんどくさかったのでメモ。

もっといい解決策があるかもしれません。

実験

入力XML

list1.xml

<?xml version="1.0" encoding="utf-8"?>
<data>
  <book>
      <title>Swiftではじめる iPhoneアプリ開発の教科書</title>
      <caption>Appleのプログラミング言語、「Swift」バージョン2対応の、iPhoneアプリ作成入門書です。</caption>
      <date>2015/10/30</date>
  </book>
</data>

list2.xml

<?xml version="1.0" encoding="utf-8"?>
<data>
  <book>
      <title>詳細! Swift 2 iPhoneアプリ開発 入門ノート</title>
      <caption>待ったなし! Swift 2</caption>
      <date>2015/11/14</date>
  </book>
  <book>
      <title>Swiftではじめる iPhoneアプリ開発の教科書</title>
      <caption>Appleのプログラミング言語、「Swift」バージョン2対応の、iPhoneアプリ作成入門書です。</caption>
      <date>2015/10/30</date>
  </book>
</data>

list1.xmlはbookタグが1個、list2.xmlはbookタグが2個存在します。

コード1

<?php
$xml1 = simplexml_load_file('./list1.xml');
$xml2 = simplexml_load_file('./list2.xml');

$xml_array1 = json_decode(json_encode($xml1), true);
$xml_array2 = json_decode(json_encode($xml2), true);

print_r($xml_array1);
print_r($xml_array2);

SimpleXMLElementオブジェクトを配列に変換する方法は、公式ドキュメントのコメントを参考に。

json_decodeの第2引数をtrueにすることで連想配列にしています。

結果

temp $ php xml2array1.php
Array
(
  [book] => Array //連想配列
      (
          [title] => Swiftではじめる iPhoneアプリ開発の教科書
          [caption] => Appleのプログラミング言語、「Swift」バージョン2対応の、iPhoneアプリ作成入門書です。
          [date] => 2015/10/30
      )

)
Array
(
  [book] => Array //連想配列の配列
      (
          [0] => Array
              (
                  [title] => 詳細! Swift 2 iPhoneアプリ開発 入門ノート
                  [caption] => 待ったなし! Swift 2
                  [date] => 2015/11/14
              )

          [1] => Array
              (
                  [title] => Swiftではじめる iPhoneアプリ開発の教科書
                  [caption] => Appleのプログラミング言語、「Swift」バージョン2対応の、iPhoneアプリ作成入門書です。
                  [date] => 2015/10/30
              )

      )

)
temp $

book要素が1個の場合と2個の場合で結果が異なります。 これでは、viewに表示するときなどに困りますね。

コード2

<?php
$xml1 = simplexml_load_file('./list1.xml');

$xml_array1 = json_decode(json_encode($xml1), true);
print_r($xml_array1);

foreach($xml_array1 as $key => $value) {
  // 連想配列だったら配列に変換
  if (is_array($value) && array_values($value) !== $value) {
  if (array_values($value) !== $value) {
    $xml_array1 = array($key => array($value));
  }
}
print_r($xml_array1);

結果

temp $ php xml2array2.php
Array
(
  [book] => Array
      (
          [title] => Swiftではじめる iPhoneアプリ開発の教科書
          [caption] => Appleのプログラミング言語、「Swift」バージョン2対応の、iPhoneアプリ作成入門書です。
          [date] => 2015/10/30
      )

)
Array
(
  [book] => Array //要素1の連想配列の配列
      (
          [0] => Array
              (
                  [title] => Swiftではじめる iPhoneアプリ開発の教科書
                  [caption] => Appleのプログラミング言語、「Swift」バージョン2対応の、iPhoneアプリ作成入門書です。
                  [date] => 2015/10/30
              )

      )

)
temp $

これで同じ構造になりました。

おまけ

if (array_values($value) !== $value)は何をしているのか。

公式ドキュメントからこの関数の説明を引用します。

array_values() は、配列 array から全ての値を取り出し、数値添字をつけた配列を返します。

つまり、配列の場合はarray_valuesを使っても何も変わりませんが、連想配列であれは値だけになると!==となるので、連想配列かどうかを判定できます。

参考

SimpleXML 関数

array_values

ランダムにタスクを割り当ててSlackに通知するbotを作った

はじめに

先日公開された下記のスライドを社内に紹介したところ、とあるディレクターさんからランダムにタスクを割り当てるbotについて聞かれたので適当につくってみました。

www.slideshare.net

やったこと

とにかく簡単にタスクの登録がしたいということだったので、Googleスプレッドシートを使うことに。

tasksシートとmembarsシートに、それぞれタスクとメンバーを記入、そこからランダムに1件選択してSlackに通知するだけのシンプル仕様。

これをディレクターさんのGoogleスプレッドシートに登録して、任意の時間をスケジューリングしてもらいました。

かなり雑な作りですが、とりあえずは要望を満たせています。

お試し運用してもらって、もっと改善要望出てきたら対応しようと思います。

コード

function doPost() {
    var tasks = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('tasks');
    var taskMax = tasks.getLastRow();
    var task = tasks.getRange(getRandomNumber(2, taskMax), 1).getCell(1, 1).getValue();

    var members = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('members');
    var memberMax = members.getLastRow();
    var member = members.getRange(getRandomNumber(2, memberMax), 1).getCell(1, 1).getValue();

    var payload = {
        text : "<@" + member + "> " + task + "お願いします",
        username : 'タスク割り当てbot',
        icon_emoji:':dart:',
        link_names: 1
    }

    var options = {
        method: 'post',
        payload : JSON.stringify(payload)
    };
    var url = "https://hooks.slack.com/services/xxxxxxxxxx/xxxxxxxxxx/xxxxxxxxxx";
    var response = UrlFetchApp.fetch(url, options);

    if (response.getResponseCode() != 200) {
        Logger.log(response);
    }
}

function getRandomNumber(min, max) {
    return Math.floor((Math.random() * ((max + 1) - min)) + min);
}

2015年の振り返り

雑に2015年の振り返りした。

ここ数年、1年過ぎるのが加速度的に早くなっている気がする。

お仕事全般

去年よりは、コード書く仕事が多かった。

主にFuelPHPと戯れてた。

Qiita:Team

7月くらいからQiita:Teamが導入されたので、積極的に投稿してる。

社内ということもあって、メモ的なものも投稿したりしてハードルはかなり下げてるつもりなんだけど、まめに投稿してくれる人はまだまだ少ないので、しばらくはQiita:Teamおじさんとして活動していこうと思う。

社内勉強会

FuelPHPのコードリーディングを開催した。

業務が多忙になって参加者が減り、最終的に私個人の活動になってしまったのは残念。

社外勉強会など

勉強会はPHPカンファレンスなど気になったものに少しだけ参加した。

去年よりは多かったけど、家のこともあるから2,3ヶ月に一回くらいが限界かなー。

相変わらず自宅では時間が取りにくいので、1時間くらい早めに出社するのを続けてるけど、やりたいことに対して不足気味。

新しい言語はSwiftを11月から始めてみた。来年はなんかアプリ作ろう。

プライベート

会社以外の時間は、ほぼ息子さんと遊んでばっか。

話せる言葉も増えてきたし、アドベントカレンダーでも書いたけど読み聞かせも喜んでくれるので、一緒に遊ぶのが楽しい。

あと石垣島はサイコーだった。

反省など

Qiita:Teamでの情報共有はそこそこ頑張った気がする。

でも仕事が忙しくなってくると、余裕がなくなってダメ。

FuelPHP勉強会とか、もうちょいなんとかなったんじゃないかなー。

来年の目標

もうちょい自分の時間を捻出して、積ん読の消化とコードの読み書きの時間を増やす。

社内勉強会を復活させる。

Qiita:Teamおじさん業を続ける。