woshidan's blog

あいとゆうきとITと、とっておきの話。

Google App Scriptでセルを扱う

はてなが俺たちのはてなじゃない...。

諸々の集計業務を担当することになって、コピペを効率化しようと思ってGoogle App Scriptを調べたのでまとめます。

内容

  • 特定のセルの値を取得
  • 特定のセルに値を貼り付け
  • 他のファイルのソースコードを参照するときの手順
  • 確認用ダイアローグを出す
  • シートを開いたときに行う処理
  • 参考

特定のセルの値を取得

// 単一の値を取得
SpreadsheetApp.getActive().getSheetByName("SHEET_NAME").getRange("A1").getValue();

// 現在アクティブなシートを取得する場合
SpreadsheetApp.getActiveSpreadsheet()).getRange("A1").getValue();

// 特定の範囲の中の値を取得する場合
// 2次元配列が帰ってきます
SpreadsheetApp.getActive().getSheetByName("SHEET_NAME").getRange("A1:B6").getValues();

特定のセルに値を貼り付け

// 単一のセルに貼り付けというのはなく、範囲から範囲への貼り付けとなる
var rangeToCopy = sheet.getRange('A:A');
var targetToCopy = sheet.getRange('B1'); // 貼り付け範囲か、一番左上のセルを指定するようにしておく
rangeToCopy.copyTo(targetToCopy);

// 形式のみ, 値のみのオプションについて
rangeToCopy.copyTo(targetToCopy, {formatOnly:true}); // 形式のみ
rangeToCopy.copyTo(targetToCopy, {contentsOnly:true}); // 値のみ

// 2次元配列を特定の範囲へ貼り付け
var copyValues = [[1, 2], [3, 4]];
SpreadsheetApp.getActive().getSheetByName("SHEET_NAME").getRange("A1:B2").setValues(copyValues);

他のファイルのソースコードを参照するときの手順

  1. 新しいプロジェクトを作成して分割したいソースコードを貼る
  2. 1のプロジェクトに対して、 ファイル > 版で管理 から、他のプロジェクトに公開するバージョンのコメントを入れて保存
  3. 2のプロジェクトのソースコードを参照したいプロジェクトを開いて、リソース > ライブラリ を選択
  4. 3で開いたメニューの下の方に、2のプロジェクトのURLの一部 https://script.google.com/macros/d/SomeLooooongRandomString/edit?ui...SomeLooooongRandomStringの部分を入れる
  5. 入力した文字列が正しければ、2のプロジェクト名が3のメニューに現れるので、2のプロジェクトを利用する側で使うバージョンと、2のプロジェクトの関数などの前につく識別子を入力して、「保存」を押す
  6. 2のプロジェクトを利用する側で、5で決めた識別子を頭につけて2のプロジェクト内の関数を利用する

確認用ダイアローグを出す

// メッセージだけでる
Browser.msgBox("メッセージが出ます");

// OK/キャンセルのボタンがついたダイアローグが出る
Browser.msgBox("これからシートを編集しますか?", Browser.Buttons.OK_CANCEL);

// 選択されたボタンを取得
var selections = Browser.msgBox("これからシートを編集しますか?", 
                        Browser.Buttons.OK_CANCEL);
// キャンセルボタンをクリックした場合は"cancel"
Browser.msgBox(selections);

シートを開いたときに行う処理

// シートを開いたときにダイアローグを出す
function onOpen(){
  Browser.msgBox("これからシートを編集しますか?", Browser.Buttons.OK_CANCEL);
}

// シートを開いたときに上部にメニューを追加する
function onOpen() {
  var sheet = SpreadsheetApp.getActiveSpreadsheet();
  var entries = [
    {
      name : "シートの上部にに追加するメニュー内の項目の名前",
      functionName : "その項目を選択したとき起動する関数"
    }
  ];
  sheet.addMenu("シートの上部に追加するメニューの名前", entries);
}

参考

結局なのですが、普通にGoogle Spread Sheetの関数を使ってCSVインポートからのコピペ前の整形作業用の単純なシートを何枚か作ったらそれだけで大幅に時間が短縮されてしまい、Google App Scriptの動作が遅かったため、2日程度で0からほとんど動くところまで書いたのに導入までできなかったのが残念です...。

暇なときにもう少し手直ししてリベンジしたい...。

Spinnerのレイアウトをコードで変更する

スタイルを利用した指定がうまく動かなかったり見つけるまでにちょっと時間がかかったりしたので、まあ適当にメモ。

  1. android:id="@android:id/text1"@android:id/text1がidに指定されたTextViewだけのレイアウトリソースを用意
  2. Spinnerの要素をセットする ArrayAdapter の引数に渡す
  3. ドロップダウン用のレイアウトは、ArrayAdapter.setDropDownViewResourceメソッドの引数に渡す

Adapterに手動で要素を追加しているのは、利用する時にスピナーの要素が動的に変わっていたからです。

また、Spinnerの要素の先頭に画像などをセットしたい場合は、BaseAdapterを継承してgetViewメソッドをオーバーライドして用います。

setContentView(R.layout.activity_main);

ArrayAdapter<String> adapter = new ArrayAdapter(this, R.layout.item_in_list);
adapter.add("One");
adapter.add("Two");
adapter.add("Three");
adapter.setDropDownViewResource(R.layout.item_in_drop_down_list);
AppCompatSpinner spinner = (AppCompatSpinner) findViewById(R.id.imageFolderNameSpinner);
spinner.setAdapter(adapter);

spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
    @Override
    public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
    }

    @Override
    public void onNothingSelected(AdapterView<?> parent) {

    }
});
<!-- res/layout/activity_main.xml -->
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true"
    tools:context="com.example.woshidan.myapplication.MainActivity">

    <android.support.design.widget.AppBarLayout
        android:layout_height="wrap_content"
        android:layout_width="match_parent"
        android:theme="@style/AppTheme.AppBarOverlay">

        <android.support.v7.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            android:background="?attr/colorPrimary"
            app:popupTheme="@style/AppTheme.PopupOverlay">
            <android.support.v7.widget.AppCompatSpinner
                android:id="@+id/imageFolderNameSpinner"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                />
        </android.support.v7.widget.Toolbar>

    </android.support.design.widget.AppBarLayout>

    <include layout="@layout/content_main"/>

    <android.support.design.widget.FloatingActionButton
        android:id="@+id/fab"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom|end"
        android:layout_margin="@dimen/fab_margin"
        android:src="@android:drawable/ic_dialog_email" />

</android.support.design.widget.CoordinatorLayout>
<!-- item_in_list.xml -->
<?xml version="1.0" encoding="utf-8"?>
<TextView android:id="@android:id/text1"
    android:textSize="18dp"
    android:padding="8dp"
    android:textColor="#0f0"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:ellipsize="marquee"
    android:background="#fff"
    android:textAlignment="inherit"
    xmlns:android="http://schemas.android.com/apk/res/android" />
<!-- item_in_drop_down_list.xml -->
<?xml version="1.0" encoding="utf-8"?>
<TextView android:id="@android:id/text1"
    android:textSize="18dp"
    android:padding="8dp"
    android:textColor="#f00"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:ellipsize="marquee"
    android:textAlignment="inherit"
    xmlns:android="http://schemas.android.com/apk/res/android" />

f:id:woshidan:20160809000455g:plain

スタイルで指定できるはずなんだけどなぁ。

SSL/TSLについて少し勉強しました

内容

SSL/TSLで使うプロトコルの設定について

SSL/TSLの利用はApacheやnginxなどのWebサーバのソフトの設定を通して有効にできます。 また、これらのソフトが動くサーバのOSのバージョンによっては一部のプロトコルが利用できない場合があります。

SSL/TSLの暗号化のアルゴリズムの指定は暗号スイートの単位で行います。

暗号スイートというのは、通信の暗号化に使うアルゴリズムはこれ、鍵交換に使うアルゴリズムはこれ、といった風に通信の各部の暗号化プロトコルの組み合わせのことです。

// Apacheの場合
SSLCipherSuite DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES128-SHA256:DHE-RSA-CAMELLIA128-SHA:DHE-RSA-AES128-SHA:AES128-GCM-SHA256:AES128-SHA256:CAMELLIA128-SHA:AES128-SHA:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-SHA256:DHE-RSA-CAMELLIA256-SHA:DHE-RSA-AES256-SHA:AES256-GCM-SHA384:AES256-SHA256:CAMELLIA256-SHA:AES256-SHA
// nginxの場合
ssl_ciphers AES128-SHA:AES256-SHA:RC4-SHA:DES-CBC3-SHA:RC4-MD5;

openssl ciphersで、そのコンピュータに入っているopensslが利用出来る暗号スイートの一覧が見れます。

d.hatena.ne.jp

$ openssl ciphers -v
DHE-RSA-AES256-SHA      SSLv3 Kx=DH       Au=RSA  Enc=AES(256)  Mac=SHA1
DHE-DSS-AES256-SHA      SSLv3 Kx=DH       Au=DSS  Enc=AES(256)  Mac=SHA1
AES256-SHA              SSLv3 Kx=RSA      Au=RSA  Enc=AES(256)  Mac=SHA1
EDH-RSA-DES-CBC3-SHA    SSLv3 Kx=DH       Au=RSA  Enc=3DES(168) Mac=SHA1
EDH-DSS-DES-CBC3-SHA    SSLv3 Kx=DH       Au=DSS  Enc=3DES(168) Mac=SHA1
DES-CBC3-SHA            SSLv3 Kx=RSA      Au=RSA  Enc=3DES(168) Mac=SHA1
DES-CBC3-MD5            SSLv2 Kx=RSA      Au=RSA  Enc=3DES(168) Mac=MD5 
DHE-RSA-AES128-SHA      SSLv3 Kx=DH       Au=RSA  Enc=AES(128)  Mac=SHA1
DHE-DSS-AES128-SHA      SSLv3 Kx=DH       Au=DSS  Enc=AES(128)  Mac=SHA1
AES128-SHA              SSLv3 Kx=RSA      Au=RSA  Enc=AES(128)  Mac=SHA1
DHE-RSA-SEED-SHA        SSLv3 Kx=DH       Au=RSA  Enc=SEED(128) Mac=SHA1
DHE-DSS-SEED-SHA        SSLv3 Kx=DH       Au=DSS  Enc=SEED(128) Mac=SHA1
SEED-SHA                SSLv3 Kx=RSA      Au=RSA  Enc=SEED(128) Mac=SHA1
RC2-CBC-MD5             SSLv2 Kx=RSA      Au=RSA  Enc=RC2(128)  Mac=MD5 
RC4-SHA                 SSLv3 Kx=RSA      Au=RSA  Enc=RC4(128)  Mac=SHA1
RC4-MD5                 SSLv3 Kx=RSA      Au=RSA  Enc=RC4(128)  Mac=MD5 
RC4-MD5                 SSLv2 Kx=RSA      Au=RSA  Enc=RC4(128)  Mac=MD5 
EDH-RSA-DES-CBC-SHA     SSLv3 Kx=DH       Au=RSA  Enc=DES(56)   Mac=SHA1
EDH-DSS-DES-CBC-SHA     SSLv3 Kx=DH       Au=DSS  Enc=DES(56)   Mac=SHA1
DES-CBC-SHA             SSLv3 Kx=RSA      Au=RSA  Enc=DES(56)   Mac=SHA1
DES-CBC-MD5             SSLv2 Kx=RSA      Au=RSA  Enc=DES(56)   Mac=MD5 
EXP-EDH-RSA-DES-CBC-SHA SSLv3 Kx=DH(512)  Au=RSA  Enc=DES(40)   Mac=SHA1 export
EXP-EDH-DSS-DES-CBC-SHA SSLv3 Kx=DH(512)  Au=DSS  Enc=DES(40)   Mac=SHA1 export
EXP-DES-CBC-SHA         SSLv3 Kx=RSA(512) Au=RSA  Enc=DES(40)   Mac=SHA1 export
EXP-RC2-CBC-MD5         SSLv3 Kx=RSA(512) Au=RSA  Enc=RC2(40)   Mac=MD5  export
EXP-RC2-CBC-MD5         SSLv2 Kx=RSA(512) Au=RSA  Enc=RC2(40)   Mac=MD5  export
EXP-RC4-MD5             SSLv3 Kx=RSA(512) Au=RSA  Enc=RC4(40)   Mac=MD5  export
EXP-RC4-MD5             SSLv2 Kx=RSA(512) Au=RSA  Enc=RC4(40)   Mac=MD5  export

上記の一覧の一行分の内容の意味は、

暗号スイートの名前    プロトコルのバージョン    鍵交換の方法(KeyeXchange)    サーバ認証の方法(Authentication)    暗号化の方法(Encryption)    HMAC(Message Authentication Code)のハッシュアルゴリズム

となります。

理解してるつもりの SSL/TLS でも、もっと理解したら面白かった話 · けんごのお屋敷

SSL証明書のインストールの大まかな流れ

SSL/TSLを有効にするには、サーバの身元を保証するSSL証明書をサーバにインストールする必要があります。

https://www.cybertrust.ne.jp/sureserver/support/tec_download.html#01

上記の資料を参考におおまかな手順をまとめると、下記のようになります。

  • openssl genrsaコマンドで秘密鍵ファイルを作成 cf. openssl genrsa -aes256 -out server.key 2048
  • openssl reqコマンドで秘密鍵ファイルからCSR(公開鍵)ファイルを作成 cf. openssl req -new -key server.key -out server.csr
    • SSL証明書の詳細情報を見たら出てくる情報を入力していきます
    • 証明書を入れるサーバーのFQDNも入れます
    • ここのコマンドで証明書の作成に使うハッシュ関数SHA1かSHA2か指定します
  • 作成した公開鍵を証明書を発行する会社に送ります
  • 証明書がサーバ証明書の会社から発行されます
  • サーバー証明書と中間証明書を連結させてpemファイルを作ってサーバーにアップロードします
    • cf. cat サーバー証明書 中間証明書 > 連結ファイル.pem
  • サーバーにアップロードした連結ファイルの場所をssl_certificate(nginxの場合)のディレクティブにフルパスで指定します
  • 秘密鍵ファイルもサーバーにアップロードして、その場所をssl_certificate_key(nginxの場合)のディレクティブにフルパスで指定します
  • webサーバ(nginxなど)の再起動をして、SSLで通信できているか確認します

プロトコルのバージョンと利用出来る公開鍵証明書のハッシュ関数について

接続したいサービスごとにこのハッシュ関数で作った証明書でないとだめですよ、とか、このバージョンのプロトコルを使ってはダメですよ、といった設定がされていることがあります。

また、OSやブラウザの設定により、公開証明書のハッシュ関数が古いと正式な証明書でも不正な証明書と表示されたり、URLバーに警告が表示されることがあります。

postd.cc

今なら少し自信を持ってオレオレ証明書が作れそうな気がしてきました...

UI用のクラスのプロトコルとWebViewなどについて

内容

上の本を参考にiOSのUIViewと戯れています。

とりあえず、4章までやったので、ここまでで一区切り~~。

  • UI用のクラスを使うとき実装するプロトコルについて
  • WebViewの使い方について
  • ATSの例外のためのInfo.plistの設定
  • PlayGround
  • TableView
    • リサイズマスク
    • 行のIndicatorの種類
  • NavigationController
  • UIPickerViewを使う時に実装するプロトコルについて

UI用のクラスを使うとき実装するプロトコルについて

TableViewやWebView, PickerViewなどのクラスをViewControllerのViewに置く場合、対応するプロトコルを実装する必要があり、クラス名DataSource, クラス名Delegateといった名前をしています。

データの表示の中身やViewから受け取れるイベントのハンドリングをそれを置くViewのViewControllerに委譲する、みたいなのりみたいです。

DataSource

Viewのクラスで扱う行数や行ごとのViewを返すメソッドなどがあるみたいです。 AndroidでいうとAdapterにあたる感じのプロトコルです。

Delegate

Viewをタップした時などのイベントをハンドリングするメソッドを書くプロところです。 AndroidでいうとSwipeRefreshLayoutのコールバックのリスナーみたいな位置付けみたいです。

WebViewの使い方について

読み込み中のIndicatorを出したい場合は

UIApplication.sharedApplication().networkActivityIndicatorVisible = true

のようにする。

ATSの例外のためのInfo.plistの設定

結局 PropertyList からの編集がうまくいかなくて、ソースコードの編集をしました。

<plist version="1.0">
<dict>
  <key>NSAppTransportSecurity</key>
  <dict>
    <key>NSExceptionDomains</key>
    <dict>
            <key>localhost</key><!-- HTTPでの通信を例外的に許可したい「ドメイン名」を入れる -->
            <!-- ダメな例: http://localhost:3000 -->
            <!-- ダメな例: localhost:3000 -->
            <!-- ダメな例: 192.168.56.1 -->
            <!-- ダメな例: 192.168.56.1:3000 -->
            <dict>
                <key>NSTemporaryExceptionAllowsInsecureHTTPLoads</key>
                <true/>
            </dict>
    </dict>
  </dict>

PlayGround

アプリ全体のビルドをせずにViewや関数だけを試したりできるSwiftの実行環境です。

ViewをPlayGroundで表示して試したい時は、

import UIKit
import XCPlayground

class CustomView: UIView {
  // Viewの実装
}

val testView = CustomView()
XCPShowView("Some ID", testView)

のように、XCPShowViewメソッドを使います... と書いてあったのですが、 Swift 2.1の頃に XCPlaygroundPage を使うように変わり、XCPShowViewメソッドはdeprectedになっていました。

参考: http://qiita.com/koishi/items/e78f8b852d5f7ae8d125

実際使っている様子は下記です。

f:id:woshidan:20160802233426p:plain

TableView

リサイズマスク

これは実はInterfaceBuilderでやるのでは、と思ったのですが、一応設定をメモします。

        let tableView = UITableView()
        tableView.frame = frame
        tableView.autoresizingMask = [
            UIViewAutoresizing.FlexibleWidth,
            UIViewAutoresizing.FlexibleHeight,
            UIViewAutoresizing.FlexibleBottomMargin,
            UIViewAutoresizing.FlexibleLeftMargin,
            UIViewAutoresizing.FlexibleRightMargin,
            UIViewAutoresizing.FlexibleTopMargin
        ]

行のIndicatorの種類

// cellはUITableViewCellのインスタンス
cell.accessoryType = UITableViewCellAccessoryType.None // 何もつけない
cell.accessoryType = UITableViewCellAccessoryType.DisclosureIndicator // そのセルの右側に「 > 」みたいなのをつける
cell.accessoryType = UITableViewCellAccessoryType.DetailDisclosureButton // そのセルの右側に「(i) > 」みたいなのをつける
cell.accessoryType = UITableViewCellAccessoryType.Checkmark // そのセルの右側にチェックマークをつける. Noneと切り替え?して使う?

NavigationController

  • NavigationControllerを使いたいViewControllerはStoryBoardでNavigationControllerを追加
  • NavigationControllerの戻るボタンに入る文字はひとつ前のスタックのViewControllerのタイトル

UIPickerViewを使う時に実装するプロトコルについて

UIPickerViewは要素が輪のようになっていてクルクル回して選択するUIです。

UIPickerView - UIKit | Apple Developer Documentation

  • UIPickerViewDelegateについて
    • func pickerView(pickerView: UIPickerView, rowHeightForComponent component: Int) -> CGFloat で1行の高さ
    • func pickerView(pickerView: UIPickerView, viewForRow row: Int, forComponent component: Int, reusingView view: UIView?) -> UIView で1行分のViewを作成
    • func pickerView(pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) でピッカーの要素が
  • UIPickerViewDataSourceについて
    • func pickerView(pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int で、行数を返すメソッドを返す
    • func numberOfComponentsInPickerView(pickerView: UIPickerView) -> Int で、コンポーネント数を返すが基本は1でよさそうです

XCodeのデバッガについて少しだけメモ

内容

上の本を参考にiOSのUIViewと戯れています。

今日はスライダーなどをいじってみたのですが、そこでは特に書くことがなかったので、デバッガについて。

デバッガ

エディタの左側の灰色の部分をタップしてブレークポイントを設定。

デバッグしているときのメニューについて。

f:id:woshidan:20160729002703p:plain

左から

  • デバッグ関係のメニューを閉じたり開いたりする
  • ブレークポイントで処理を止めるかどうか決める
  • 次のブレークポイントorクラッシュ箇所まで進むか、今の処理を一時停止するか
  • 処理を一行分進む
  • 変数の中身などのステップも1つとして1ステップ次の処理に進む
  • よくわからない...

デバッグ用のメニューを開きながらステップ実行していくと変数の中身などが確認出来ます。

エラーの中身について

変数の中身を確認するタブの横にクラッシュなどが発生したときにメッセージがでるのですが、メソッド名などが確認出来るだけで行数がちょっとよくわからない現状です。

メソッド名がわかればブレークポイントを仕込めばいいわけですが。

2016-07-29 00:27:57.697 SliderEx[22451:903197] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Application tried to present modal view controller on itself. Presenting controller is <SliderEx.ViewController: 0x7ff723dad930>.'
*** First throw call stack:
(
  0   CoreFoundation                      0x000000010362dd85 __exceptionPreprocess + 165
  1   libobjc.A.dylib                     0x00000001053d1deb objc_exception_throw + 48
  2   UIKit                               0x0000000104003ab9 presentationControllerClassForPresentationStyle + 0
  3   UIKit                               0x000000010400685c -[UIViewController _performCoordinatedPresentOrDismiss:animated:] + 489
  4   UIKit                               0x000000010400636b -[UIViewController presentViewController:animated:completion:] + 179
  5   SliderEx                            0x000000010344572b _TFC8SliderEx14ViewController9showAlertfTGSqSS_4textGSqSS__T_ + 955
  6   SliderEx                            0x0000000103444fa8 _TFC8SliderEx14ViewController7onClickfCSo8UIButtonT_ + 1352
  7   SliderEx                            0x000000010344500a _TToFC8SliderEx14ViewController7onClickfCSo8UIButtonT_ + 58
  8   UIKit                               0x0000000103e57a8d -[UIApplication sendAction:to:from:forEvent:] + 92
  9   UIKit                               0x0000000103fcae67 -[UIControl sendAction:to:forEvent:] + 67
  10  UIKit                               0x0000000103fcb143 -[UIControl _sendActionsForEvents:withEvent:] + 327
  11  UIKit                               0x0000000103fca263 -[UIControl touchesEnded:withEvent:] + 601
  12  UIKit                               0x0000000103eca99f -[UIWindow _sendTouchesForEvent:] + 835
  13  UIKit                               0x0000000103ecb6d4 -[UIWindow sendEvent:] + 865
  14  UIKit                               0x0000000103e76dc6 -[UIApplication sendEvent:] + 263
  15  UIKit                               0x0000000103e50553 _UIApplicationHandleEventQueue + 6660
  16  CoreFoundation                      0x0000000103553301 __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 17
  17  CoreFoundation                      0x000000010354922c __CFRunLoopDoSources0 + 556
  18  CoreFoundation                      0x00000001035486e3 __CFRunLoopRun + 867
  19  CoreFoundation                      0x00000001035480f8 CFRunLoopRunSpecific + 488
  20  GraphicsServices                    0x0000000107cc3ad2 GSEventRunModal + 161
  21  UIKit                               0x0000000103e55f09 UIApplicationMain + 171
  22  SliderEx                            0x0000000103447092 main + 114
  23  libdyld.dylib                       0x0000000105e9592d start + 1
  24  ???                    

UIButton, UIAlertController, UITextView, UITextFieldあたりに触りました

内容

上の本を参考にiOSのUIViewと戯れています。

  • 最初からSwiftでコードを書きたい時はSwiftでプロジェクトを作成する
  • Text用, 画像用のViewがあった話
  • Swiftのクラス定数っぽいものの名前
  • ボタンの種類
    • type: UIButtonType.System
    • type: UIButtonType.InfoDark
    • type: UIButtonType.InfoLight
    • type: UIButtonType.ContactAdd
    • type: UIButtonType.DetailClosure
  • ボタンを押したときのイベントの設定
  • アラートの種類
  • アラートのボタンの挙動の設定
  • AlertActionのハンドラーの引数としてのクロージャの利用例
  • UITextFieldについて
  • UITextViewでも文字編集可能にできる

最初からSwiftでコードを書きたい時はSwiftでプロジェクトを作成する

これまでObjective-CのプロジェクトにSwiftのクラスをちょいちょい書いて試してたんですが、プロジェクト作成時にObjective-CかSwiftかを選べるのでSwiftで書きたい時は、この時点で Swiftを選んだ方が楽です。

関連して少し調べてみたのですが、, Objective-CからSwift, SwiftからObjective-Cのクラスの参照については下記ブログ記事が参考になりました。

http://techblog.yahoo.co.jp/ios/swift-from-objc/

上記記事より手順だけ簡単にまとめると、

  • SwiftからObjective-Cを参照する場合はBridging Headerを用意して、そこに必要なクラスをimportする
  • Objective-CからSwiftを呼ぶ場合はビルド時に自動生成される.hファイルをimportする

となります。

Text用, 画像用のViewがあった話

String.drawAtPointメソッドや、UIImage.drawAtPointメソッドを見て、ひええ、となっていたのですが、LabelクラスやUIImageViewクラスがちゃんとありました。

少し UIImageViewをカスタマイズしたい時などに UIImage.drawAtPointメソッドなどを使うのかな、と思いました。

Swiftのクラス定数っぽいものの名前

タイププロパティといいます。

ボタンの種類

UIButtonはコンストラクタで指定するtypeで見た目が変化します。

type: UIButtonType.System

f:id:woshidan:20160728003518p:plain

type: UIButtonType.InfoDark

f:id:woshidan:20160728003515p:plain

type: UIButtonType.InfoLight

f:id:woshidan:20160728003517p:plain

type: UIButtonType.ContactAdd

f:id:woshidan:20160728003512p:plain

type: UIButtonType.DetailClosure

f:id:woshidan:20160728003513p:plain

何かミスったかもしれない、という感じなのですが、現場に入っていろいろ見てみようと思います。

Customが画像ボタン作るときに使うみたいでした。

let button = UIButton(type: UIButtonType.Cutom)
button.setImage(image, forState: UIControlState.Normal)

また、ボタンに表示される画像やテクストはそれが表示されるときのボタンの状態と一緒に指定します。

ボタンを押したときのイベントの設定

func onClick(sender: UIButton) {
  if sender.id == BTN_TARGET {
    // do something
  }
}

func makeButton() -> UIButton {
  let button = UIButton(type: UIButtonType.System)
  button.tag = BTN_TARGET // 適当なInt
  button.addTarget(self, action: "onClick:", forControlEvents: UIControlEvents.TouchUpInside)
  return button
}

button.addTarget(self, action: "onClick:", forControlEvents: UIControlEvents.TouchUpInside) のactionのコロンを忘れてあたふたしたのはいい思い出です。

UIControlEvents.TouchUpInsideはインタフェースビルダーで設定した覚えがあるのですが、どうなのでしょう。

アラートの種類

普通のアラート(コンストラクタで指定するpreferredStyleは UIAlertControllerStyle.Alert)とアクションシート(コンストラクタで指定するpreferredStyleは UIAlertControllerStyle.ActionSheet)があります。

アクションシートは下図のようなボタンが縦に並んだシートです。

f:id:woshidan:20160728004654p:plain

アラートのボタンの挙動の設定

alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.Default, handler: nil))
// 表示
self.presentViewController(alert, animated: true, completion: nil)

AlertActionのハンドラーの引数としてのクロージャの利用例

慣れるために眺めます。

alert.addAction(UIAlertAction(title: "Yes",
            style: UIAlertActionStyle.Default, handler: {(alert) in
                self.showAlert(nil, text: "Yesをクリック")
            }))

{ (クロージャのブロックに渡される変数名) in 計算 } のような感じです。

UITextFieldについて

HTMLでいうフォーム。ViewControllerにUITextFieldDelegateを実装させることで、フォーム内に入力された文字が変化したときの挙動を設定できます。また、UIKeyboardTypeでキーボードの種類を、returnKeyTypeでreturnキーにあるボタンのスタイルを決定できます。

UITextViewでも文字編集可能にできる

UITextViewでもその内部の文字の編集が可能です。UITextView#becomeFirstResponder()メソッドで編集を開始させ、UITextView#resignFirstResponder()で編集を終了させます。

UITextViewで入力された文字が変化したときの挙動を設定したい場合は、 UITextViewDelegateを実装します。

今日はここまでです。

タッチベントの処理とタイマーの処理について

内容

上の本を参考にiOSのUIViewと戯れています。

  • タッチベントの処理
    • UIResponder
    • 画面の再描画
    • タッチ位置の取得
    • ルートビューコントローラの取得
  • タイマーの処理
    • UIImageとUIImageViewの違い
    • タイマーの設定

タッチベントの処理

UIResponder

https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIResponder_Class/

UIViewクラスとUIApplicationクラスのスーパークラスで、タッチ操作に関するイベントを取得、処理できます。

タッチイベントの操作を行いたいときは、このクラスのメソッドをオーバーライドします。

タッチ開始時

func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?)

タッチ移動時

func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?)

タッチ終了時

func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?)

タッチキャンセル時

func touchesCanceld(touches: Set<UITouch>, withEvent event: UIEvent?)

画面の再描画

func setNeedsDisplay()

サンプルでは、タッチイベントを取得 => タッチイベントをプロパティに => 再描画 としていましたが、これ子Viewの一部を再描画などでなくて大丈夫なのかな(いちいち全体再描画するのはちと怖いな)、みたいなことを思ってしまった...。

タッチ位置の取得

UITouch#locationInView(view: UIView?) -> CGPoint

ルートビューコントローラの取得

ルートビューコントローラから3Dタッチが利用可能かの設定が取得できます。

ルートビューコントローラは下記の手順で取得します。

  • UIApplicationクラスのsharedApplicationでUIApplicationオブジェクトを取得
  • UIApplicationクラスのkeyWindowプロパティ、またはwindowsプロパティからウィンドウを取得
  • windowクラスのrootViewControllerプロパティでルートビューコントローラを取得

ルートビューコントローラから3Dタッチが利用可能かなどの設定は、UIViewControllerクラスのtraitCollectionプロパティを通して取得できる、UITraitCollectionクラスのオブジェクトから取得できます。このUITraitCollectionクラスのオブジェクトには端末の特徴的な情報(縦方向, 横方向のサイズ, 画面のスケール, UIの種類...)が入っています。

タイマーの処理

UIImageとUIImageViewの違い

補完でUIImageViewがでてきて、あれ、UIImageを違うの?と思ったので確認。

UIImage

https://developer.apple.com/reference/uikit/uiimage

画像データをiOSのプラットフォームで取り扱うためのクラス。Immutableなクラスなので利用する時はいつも既存のデータからこのオブジェクトを作成するっぽい。

Drawable的な。

UIImageView

https://developer.apple.com/reference/uikit/uiimageview

一連のアニメする画像あるいは静止画像を表示するためのView.アニメの開始 & 停止のメソッドも生えてるみたいです。

ImageView的な。

タイマーの設定

var _timer = NSTimer.scheduledTimerWithTimeInterval(
                定時処理を行う時間の間隔,
                target: self,
                selector: "onTick:", // 時間になった時実行するメソッド名 
                // deprectedみたいな警告出てたので実際使う時は調べ直す
                userInfo: nil,
                repeats: true) // 繰り返し処理を行うかどうか
// タイマーの停止
_timer.invalidate()
_timer = nil