Simple gadget life programming diary

Simple gadget life の中の人によるプログラミングメモ

Swift事始め。サンプルプログラムを作ってGitHubへアップロードしてみた。

これまで、自分のアプリ作成(もう少しかかる。。)やサーバサイドの学習をしていた関係上、Swiftに手を出さないままここまで来てしまいました。
アプリ作成のモチベーションアップのためにもこの辺でSwiftにシフトするのもありかなと考えてちょっとGW中に触ってみました。

作った成果物はこちら

github.com

f:id:jtaka1012:20160509071729p:plain
f:id:jtaka1012:20160509071735p:plain

天気予報のAPIを叩いてパースして表示。せっかくなので過去の情報もDBへ保存という内容はともかく技術的には割りと多く求められるタイプのものだと思います。

使用したライブラリは以下の通り(CocoaPodsにて導入)
- Alamofire (通信ライブラリ)
- Realm (DB)

ホントはもっと使いたかったけど、まずは完成させることが最優先なので、この辺で。
今後はRxSwiftも触ってMVVMモデルでもう少しちゃんと書きなおしてみる想定。

とここまではGitHubのREADMEに書いたことのまとめ。
ここからはSwiftを初めて触った感想を少々

文法的にはわりとすぐ慣れそう

iOSフレームワークという点では今までと同じなので、心配はしていなくて最初の難関(?)としては文法的な部分かのかなと。
いろんな言語を参考にされて作られているっぽいので、「みたことあるなぁ」的な感じにはなります。
個人的にどの言語にちかいのかというと、JSのようなJavaのようなPythonのようなみたいな感じです。
触る前はArray,Dictionaryとかの呼び名はPython的かなとおもってましたが、try-catch(do-catch)はJavaだし、メソッドチェーンみてるとJS思い出すしでなんだか不思議な感じです。
慣れるとObjective-Cに戻りたくなくなってきますね、これ。

ビルド遅い

そんなに遅いのって思ってましたが、作ったサンプルでさえ手持ちのMacBookAir(多分2013製)で1分ほどのビルド時間を要することがあります。ビルド時に型推論とかで時間かかってますかね。クラスのインポート行わないので、このタイミングで何かしらするのかなと。
ある意味CPUやメモリに依存する部分なので、マシンスペックが上がれば解決する問題なような気がしますが、個人用のPCではそんなに変わらないかな。(サーバで出来るようになれば全然話が変わりそうだけど)

厳密な型チェック

最初はよくわかりませんでした。!とか?とか。なんで怒られるのかと。。
要約仕組みを理解してきて書けるようにはなってきて受け入れられるようにはなりました。
基本文法はイマドキなフレンドリーさがある一方で、型チェックにはうるさいですね。「これJavaが嫌いな人とか無理だろうなぁ」と何となく思ってみたり。個人的には問題なしです。

と個人的なファーストインプレッションでした。
小さなサンプルだけど、設計的にいろいろ考えながらやってるので勉強になる。やっぱり手を動かさないとですね。
設計で考えたところはRxSwift対応した時に書いてみようかなと。
もっと触る頻度を増やしていって綺麗に早く書けるようになっていきたいです。

Mac+Pythonでフォルダ内のmp3ファイルをcafファイルに一括変換するスクリプト

お手軽スクリプトです。

フォルダ内の再帰検索やエラーハンドリングには対応していません。
指定したフォルダ内にすべてmp3ファイルが存在する前提です。

pathの部分にmp3ファイルを置いたフォルダの場所を指定してください。

すこしハマった所としてはglobの記述方法でしょうか。
他のサイトのマネでは動かなかった。。

環境はMac(El Capitan)+python 2.7.10 です。

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import subprocess
from os.path import join, relpath, splitext
import glob

path = '/Users/ユーザー名/Desktop/sound/'

# ディレクトリから拡張子なしでファイル名を配列取得
files = [relpath(x, path) for x in glob.glob(join(path, '*'))]

# 配列分処理を繰り返す
for filename in files:
    # ファイル名と拡張子を分ける
    name, ext = splitext(filename)
    # ファイル変換コマンドを引数形式で実行
    cmd = "afconvert -f caff -d ima4 {0}.mp3 {1}.caf".format(path+name, path+name)
    # 実行
    subprocess.call(cmd, shell=True)

よろしければどうぞ。

iOSでセクションタップ方式のアコーディオン型TableViewライブラリを作ってみた。

UIKitのUITableViewだとどこかをクリックすると伸び縮みするようなジャバラの構造にはなっていません。 また、 実現するためにはそこそこのコードを書かなければなりません。
幾つかサンプルのコードがあったけれど、セルの1行目をセクションヘッダーに使っていているものがほとんど。コントローラーが汚れがちで直感的に取り扱えるものでもありません。

そこで、セクションヘッダーをタップすると伸び縮みし、標準のTableViewでコードを書くような間隔で使えるアコーディオン型のTableViewライブラリを作ってみました。(車輪の再発明だと思いますがいい勉強になりました。)

f:id:jtaka1012:20150315102023g:plain

コードはgithubに置いています。
SGLAccordionTableView

できること

・UITableViewと同じメソッド名でセクションの数や列の数、セルの生成、ヘッダーの生成が可能。
delegateとdataSourceを別ファイルに切り出すことも可能。(tableDalegate,tableDataSourceにて指定してください。)
・セクションヘッダーが残らずスクロールさせることが可能。
・ヘッダーがタップされた際にヘッダーのView情報と開閉情報を受け取ることが可能。(アイコンを付け替えたいときなどに使えます。)

できないこと (現在未対応。プルリクエストお待ちしております。。)

・編集モードの使用
・タップをヘッダーの一部分のみに限定する
・コードによる開閉操作

上の動画で使っているコントローラーのコードが以下になります。
スッキリでいつもの通りに使えると思います。
また、_tbl_sampleはstoryBoard上でSGLAccordionTableViewを指定しています。

ポイントはtableDalegate,tableDataSourceの部分。
ここでTableViewで使う情報の取り先を指定しています。名前が違うので注意してください。

#import "ViewController.h"

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    // ** tableDataSource設定後に実施 **/
    // 開閉の初期状態を格納
    NSMutableArray *esArray = [NSMutableArray array];
    
    [esArray insertObject:[NSNumber numberWithBool:YES] atIndex:0];
    [esArray insertObject:[NSNumber numberWithBool:NO] atIndex:1];
    [esArray insertObject:[NSNumber numberWithBool:YES] atIndex:2];
    [esArray insertObject:[NSNumber numberWithBool:NO] atIndex:3];
    [esArray insertObject:[NSNumber numberWithBool:YES] atIndex:4];
    [_tbl_sample setExpandStatus:esArray];

    // セクションヘッダーがスクロールするようにセクションヘッダーの最大値を設定
    _tbl_sample.scrollSectionHeaderHeight = 100;
    
    // delegate設定
    _tbl_sample.tableDelegate = self;
    _tbl_sample.tableDataSource = self;
    
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
    // Return the number of sections.
    return 5;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    return 6;
}

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {

    return 40;
}

-(CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section{
    
    return 50;
}

- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section{

    UILabel *label = [[UILabel alloc]initWithFrame:CGRectMake(0, 0, _tbl_sample.frame.size.width, 100)];
    label.backgroundColor = [UIColor lightGrayColor];

    label.text = [NSString stringWithFormat:@"Section%ld", section];
    
    return label;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *CellIdentifier = @"myCell";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (cell == nil) {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:CellIdentifier];
    }
    
    cell.textLabel.text = [NSString stringWithFormat:@"Row%ld", indexPath.row];
    cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
    
    return cell;
}

-(void)tableViewSection:(NSInteger)section expanded:(BOOL)expanded headerView:(UIView *)view{
    
    NSLog(@"SectionHeader%ld Tapped!",section);
}

- (IBAction)pushedResetButton:(id)sender {
    
    // セクションの開閉状態を取得
    NSMutableArray *array = _tbl_sample.expandStatusArray;
    
    for (NSNumber *n in array) {
        NSLog(@"値は%d",[n boolValue]);
    }
    
}
@end

よろしければどうぞ。

UITableViewの上にヘッダー分の隙間が開くときの対処法

発生するタイミング

StoryboardでViewControllerに対してUITableViewを最初に貼り付けた場合。
ViewControllerのオプションで"Adjust Scroll View Inserts" がオン または "Under Top Bars"のチェックがオフになっている場合。

f:id:jtaka1012:20140522134826j:plain

上記は以前ScrollViewで起こった際のキャプチャ。UITableViewでも同じ事象が発生する。

原因

こちらの記事がくわしい。

やはりお前らのiOS7対応は間違っている(解説編) - Qiita

解決方法

上記の記事のnatsuさんのコメント部分。
他にViewやボタンを貼り付ける場合、TableViewの順番を下げれば事象は発生しなくなる。

忘れた頃にやってしまうのでメモ。

Google-Maps-for-Railsを使ってマップを表示する際、初期表示時の縮尺を変更する方法。

タイトルではrailsのライブラリの名前をあげていますが、javascriptの話です。

Google-Maps-for-Rails
このリンク先のgithubのREADMEに記載されているサンプルの通りにviewに対してコードを記載した場合、マークを1つだけ表示する場合、最大ズームになってしまう。

<div style='width: 600px;'>
  <div id="map" style='width: 600px; height: 400px;'></div>
</div>

<script type="text/javascript">
  handler = Gmaps.build('Google');

  handler.buildMap({
    provider: {
      
    },
    internal: {
      id: 'map'
    }
  },function(){
    markers = handler.addMarkers(<%=raw @hash.to_json %>);
    handler.bounds.extendWith(markers);
    handler.fitMapToBounds();
  });

</script>

オプションはproviderの部分に記載するはずだが、zoomオプションをつけても何ら影響しない。

もう少し調べると、functionの最後に以下の文を加えるとデフォルトの縮尺が変わってくれる。

handler.getMap().setZoom(15);

意外とこの情報が見つからなかったのでメモ。

Twitter認証を要求する際、謎の401エラーとなるときの原因のひとつ

サーバの時刻が実際の時間とズレている。
VirtualBoxで開発している場合、立ち上げっぱなしでクライアントマシンをスリープしていると、エミュレートしているOSもスリープしてしまう。
その場合、エミュレートしているOSの時刻を正しく修正する必要がある。

CentOSは以下で修正可能。

sudo service ntpd stop
sudo ntpdate jp.pool.ntp.org
sudo service ntpd start

たまにハマる。忘れないようにメモ。

railsでgenerateで生成したあとで取り消すコマンド

[hoge@localhost app]$ rails g controller plan
      create  app/controllers/plan_controller.rb
      invoke  erb
      create    app/views/plan
      invoke  test_unit
      create    test/functional/plan_controller_test.rb
      invoke  helper
      create    app/helpers/plan_helper.rb
      invoke    test_unit
      create      test/unit/helpers/plan_helper_test.rb
      invoke  assets
      invoke    coffee
      create      app/assets/javascripts/plan.js.coffee
      invoke    scss
      create      app/assets/stylesheets/plan.css.scss

planのコントローラを生成したが、ホントはplansだった。
その場合、以下のコマンドで取り消しが可能。

[hoge@localhost controllers]$ rails destroy controller plan
      remove  app/controllers/plan_controller.rb
      invoke  erb
      remove    app/views/plan
      invoke  test_unit
      remove    test/functional/plan_controller_test.rb
      invoke  helper
      remove    app/helpers/plan_helper.rb
      invoke    test_unit
      remove      test/unit/helpers/plan_helper_test.rb
      invoke  assets
      invoke    coffee
      remove      app/assets/javascripts/plan.js.coffee
      invoke    scss
      remove      app/assets/stylesheets/plan.css.scss

destroyコマンドで生成を取り消すことが可能。