tomoyaonishiのブログ

iOSのことを中心に・・・その他もあるよ!

ライセンス情報を自動で設定に吐き出してくれるLicensePlistがすごい

f:id:tomoyaonishi:20180919133846p:plain:h500

LicensePlist

LicensePlistというものを見つけました。CocoaPods、Carthage両対応でSettings.bundleを吐き出してくれて設定画面の自分のアプリ欄にライセンス表記をまとめて自動で表示できるという素晴らしいツールです。

この手のツールはどれもいまいちでCocoaPodsにしか対応していなかったり、Carthageにしか対応していなかったりして面倒でした。

これはありがたい!!

以下の記事をみながらやればすんありイケルと思います。

HomebrewでMacコマンドラインツールとして実行もできますが、CocoaPodsをすでに使っている人はCocoaPodsに追加する方法が良いと思います。環境移行しやすいですしね。

LicensePlist というiOSアプリ利用ライブラリのライセンス一覧を生成するツールを作りました

自分はCococaPodsでやったので、少し補足いれつつ書いておきます。

CocoaPodsにLicensePlist自体を追加

pod 'LicensePlist'
$ pod install

Run Scriptにスクリプトを追加

if [ $CONFIGURATION = "Debug" ]; then
    ${PODS_ROOT}/LicensePlist/license-plist --output-path $PRODUCT_NAME/Settings.bundle
fi

Settings.bundleを追加

iOSの設定アプリの自分のアプリの領域に情報を追加するにはSettings.bundleというものが必要です。これを追加します。

  • New File...からSettings.bundleを選択して追加する

f:id:tomoyaonishi:20180919132740p:plain

  • 余計なものを消して、Root.plistを書き換えてライセンス欄を追加する。

f:id:tomoyaonishi:20180919133003p:plain

Root.plist

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
    <dict>
        <key>PreferenceSpecifiers</key>
        <array>
            <dict>
                <key>Title</key>
                <string>Licenses</string>
                <key>Type</key>
                <string>PSChildPaneSpecifier</string>
                <key>File</key>
                <string>com.mono0926.LicensePlist</string>
            </dict>
        </array>
        <key>StringsTable</key>
        <string>Root</string>
    </dict>
</plist>

ビルド

ビルドするとSettings.bundle内に以下のファイルが追加されるはずです。追加されない場合は「Add files to Settings.bundle...」で追加してください。

f:id:tomoyaonishi:20180919133617p:plain

設定アプリで確認

f:id:tomoyaonishi:20180919133120p:plain:h500

たったこれだけで面倒な作業から解放されます。作者さんありがとうございます 😍

AtomからVisual Studio Codeに乗り換えた

Atomが起動しなかったり、終了しなかったりプロセスが不安定になることが多くイライラしたのでVisualStudioCodeに乗り換えました。😃

パフォーマンステストなどは以下の記事で丁寧にやってあるので安心して大丈夫かと思います。VisualStudioCodeすごい。

ryuta46.com

プラグイン周りも相当ニッチなものでない限りは無いものは無い感じだったので(少なくとも自分の用途では足りている)ので困ることはなさそうです。

自分のカスタマイズポイントとしてはコードフォーマットのショートカットをXcodeの「コントロール + I」にするだけなので特に不満もなくすんなり移行できました。

Microsoftに買収されたりGitHub自体はそのままでもエディタだったりサブで展開しているツール群ははそのうち統廃合されそうですね。

TwitterKit for iOSでログイン情報が保存されない

TwitterKit for iOSでログイン機能が正常に動作しないバグに直面しました。かなりの時間悩んでいたのですが、原因はLinked Frameworks and Binariesに追加するとき、TwitterKit.frameworkTwitterCore.frameworkを追加していたことでした。

ドキュメント見落としてました。というか気づかないよこれ・・・。

同じ罠にハマっている人がいるような気がするので投稿しました。

https://github.com/twitter/twitter-kit-ios/wiki/Installation

Note: You might run into issues if TwitterCore.framework is also placed here

Note: Again, you might run into issues if TwitterCore.framework is also placed here

AutoLayoutでViewの横幅に追従して表示するが画面からはみ出さないようにする

AutoLayoutであるViewの横にボタンをおいて、ボタンをViewの横幅に追従させるような画面があると思います。

たとえば、画像のようにタイトルの横にお気に入りボタンをおいて、タイトルの長さによってお気に入りボタンも右にずれていくような場面です。

f:id:tomoyaonishi:20180827200049p:plain

このとき、タイトルの長さが長すぎると、ボタンが画面からはみ出してしまいます。

UILabelの横幅の最大値を決める方法でもいいかもしれませんが、シンプルにButtonの右端の制約を右端からみて、graterThanOrEqualにします。そして最低限確保したい右側のマージンとしてconstant: 8などとすると、タイトルが長くなりすぎたとき、ボタンが画面からはみ出さずに、右からのマージンを8あけて表示してくれます。

f:id:tomoyaonishi:20180827200613p:plain

結構、シンプルなのですが、最近まで気づかなかったのでメモでした。

一応ソースコードです。Storyboardでやってます。

github.com

UILayoutGuideを使って意味のないUIViewを使わないようにする

UILayoutGuideとはAutoLayoutのための矩形領域表現クラスです。簡単に言うとUIViewからレンダリングとタッチイベント機能を取り去ったものです。

iOS11でSafeAreaが登場したのでview.safeAreaLayoutGuideが有名でしょうか、これがUILayoutGuideです。

実はiOS9から登場していて、view.layoutMarginsGuide, view.readableContentGuideもUILayoutGuideです。

で本題です。

UILayoutGuideはシステムが作るものと思う人がいるかも知れませんが、自分でも作成できます。

何ができるかというと、冒頭で記述したようにAutoLayoutのための矩形領域をUIViewなしで表現できます。たとえば、2つのViewをまとめて空のUIViewに乗せて、空のUIViewをセンタリングするみたいなことはよくあると思います。また、3つのViewを均等に表示するために空のUIViewをスペーサーとして使うこともあるでしょう。

UILayoutGuideを使えば空のViewは不要になります。

たとえば、均等に表示する場合、今までだと

[view1] - [dummyView1] - [view2] - [dummyView2] - [view3]

として、dummyView1,2の横幅を制御して均等に表示していました。

これをUILayoutGuideを使うと

[view1] - [UILayoutGuide] - [view2] - [UILayoutGuide] - [view3]

となります。

        let space1 = UILayoutGuide()
        view.addLayoutGuide(space1)
        
        let space2 = UILayoutGuide()
        view.addLayoutGuide(space2)
        
        space1.widthAnchor.constraint(equalTo: space2.widthAnchor).isActive = true
        space2.widthAnchor.constraint(equalToConstant: 50).isActive = true
        
        label2.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
        label2.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
        
        label1.centerYAnchor.constraint(equalTo: label2.centerYAnchor).isActive = true
        label1.rightAnchor.constraint(equalTo: space1.leftAnchor).isActive = true
        space1.rightAnchor.constraint(equalTo: label2.leftAnchor).isActive = true
        label2.rightAnchor.constraint(equalTo: space2.leftAnchor).isActive = true
        space2.rightAnchor.constraint(equalTo: label3.leftAnchor).isActive = true
        label3.centerYAnchor.constraint(equalTo: label2.centerYAnchor).isActive = true

こんな感じです。

メリットとしてUILayoutGuideはUIViewではないので、View階層を汚しません。空のUIViewを使いだすとUIの構造が分かりづらくなったりするのでなるべくこちらのほうがいいと思います。

とはいえ、StackViewでええやんっていうところもあるし、使い所次第ですね。

UILayoutGuideが生まれた背景は、AutoLayoutのために空のUIViewを使うことは、

  1. 自身の生成コストと運用コスト
  2. View階層に追加されるので、View階層に対するタスク時のオーバーヘッド
  3. 空の透明なViewは本来の意味で使われる他のViewへのメッセージングを阻害してしまう。(タッチイベントを意図せず奪ってしまうとか?)このような不具合のデバッグは非常に難しい。

の3つが挙げられていました。

UILayoutGuide - UIKit | Apple Developer Documentation

サンプルコードはこちらです。

github.com

AppStoreでリリース中のアプリのソースコードを公開してみた。

僕が開発したiOSアプリ「百選めぐり」のソースコードを公開しました。

試験的にnoteで有料販売にしたいと思います。ソースコード売買のプラットフォームもあるのですが、利用側がわざわざアカウント作るモチベがいまいちイメージできなかったのでnoteにしてみました。

アプリはこちらです。

百選めぐり

百選めぐり

思いつきで一気に作ったので通信などロジック周りの作りは微妙かもしれません。

詳細はこちら。

note.mu

もし売れたらレポートしたいと思います。

HighSierraにアップデートするとパーミッションがおかしくなったのでHomebrewを再インストールする

High SierraにアップデートしたらMacパーミッションの周りが変わったみたいでhomebrewが使えなくなりました。

手動でパーミッション変えたり、解決策はあるようでしたが、めんどくさいのでHomebrew自体を再インストールすることにしました。

まずはアンインストール

$ ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/uninstall)"

そのあとインストール

/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"

これで再インストール完了です。

sudoとかつけまくってよくわからなくなってる人とかもこれで。

たまにはお掃除しましょう!

UICollectionViewFlowLayoutAutomaticSizeを使用したCollectionViewの使い方

UICollectionViewFlowLayoutAutomaticSizeを使用したCollectionViewFlowLayoutでUICollectionViewを作ったことがなかったので勉強がてらまとめました。

だいたいありそうなUI構成は問題なくできそうです。UICollectionViewFlowLayoutAutomaticSizeを使いながらも手動でセルサイズを変える方法もみつけました。

contentView自体の大きさを決めてしまうとViewDebuggerでみたときにAmbigiousLayoutになっていたり、ハマりどころ多かったですが、UITableViewのやりかたと同じ要領でやればいいだけでした。。

layout.invalidateLayout()を、viewWillLayoutSubviewsで呼びたかったのですが、Cellの中のviewが更新されるタイミングなのかスクロール中にviewWillLayoutSubviewsが何度も呼ばれてしまって、無限ループになってしまう問題に直面しました。viewWillLayoutSubviewsでinvalidateしないほうがいいのか、Cellの構成が間違っているのか。。

これはこういうもんだった。willDisplayCellあたりでセルのレイアウトが走るからでした。

あと、よくない制約の貼り方でセルの大きさを決められないとき、以下のワーニングが出ます。ログの通り、セルが表示されなかったり、無限ループになったりして面倒でした。 1つずつセルをみていって、解消するしかないです。

CollectionViewMaster[39946:2565272] The behavior of the UICollectionViewFlowLayout is not defined because:
2018-06-24 14:11:24.247596+0900 CollectionViewMaster[39946:2565272] the item width must be less than the width of the UICollectionView minus the section insets left and right values, minus the content insets left and right values.
2018-06-24 14:11:24.247730+0900 CollectionViewMaster[39946:2565272] Please check the values returned by the delegate.
2018-06-24 14:11:24.247976+0900 CollectionViewMaster[39946:2565272] The relevant UICollectionViewFlowLayout instance is <UICollectionViewFlowLayout: 0x7fe0f0c17e50>, and it is attached to <UICollectionView: 0x7fe0f102ca00; frame = (0 0; 414 736); clipsToBounds = YES; gestureRecognizers = <NSArray: 0x604000245700>; layer = <CALayer: 0x60400023dfe0>; contentOffset: {0, -64}; contentSize: {414, 8990}; adjustedContentInset: {64, 0, 0, 0}> collection view layout: <UICollectionViewFlowLayout: 0x7fe0f0c17e50>.
2018-06-24 14:11:24.248337+0900 CollectionViewMaster[39946:2565272] Make a symbolic breakpoint at UICollectionViewFlowLayoutBreakForInvalidSizes to catch this in the debugger.

ソースコードはこちら。

github.com

JavaScript, TypeScriptでシングルトン

iOSなどではよく(?最近はアンチパターンですかね)で使われるシングルトンをブラウザ環境上のJavaScriptでどう書くかわからなかったのでメモです。

コードはTypeScriptですが、こんな感じです。

定義側

interface StoreInterface {
    x: number;
    methodHoge: () => any;
}

class Store implements StoreInterface {
    x = 0;

    constructor() {
    }

    methodHoge() {
        this.x++;
    }
}

export default new Store();

利用側

import Store from "./Store";

render() {
      Store.methodHoge();

      // ...
}

export default のところで new をつけてインスタンス化しているところがポイントでしょうか。

UITableViewについて改めて考える

UITableViewについて改めて考えました。

iOSアプリとは切っても切れないUITableViewですが、リスト=UITableViewという考え方はどうかなとおもったのでメモがてら書きます。

結論から言うと再利用しないのであれば(もしくは再利用がごく少量)UIScrollViewのほうがいいじゃんということなのですが。

UITableViewはiPhoneOS2.0から利用可能です。当時はメモリが少なく目に見えない部分の無駄なViewを保持しておくということがあり得ない状況でした。そこでUITableViewのCellを再利用しメモリ使用量を減らすというアプローチが取られたわけです。

現在の主要なiPhoneは十分にメモリがあるので、そこまでセンシティブにならなくてもいいかなとおもっています。

リストだけれども、再利用しないViewで構成されている場合はScrollViewにaddしてそのViewの参照をプロパティにでも持っておくほうがスクロールのたびにViewを作り直すコスト(パフォーマンスとコード)がなくていいなと個人的に思います。

TwitterのようなツイートViewが大量にリスト形式で表示されるようなThe UITableViewだねってところはよいのですが、ユーザ情報入力画面のような各行(氏名、性別、プロフィール画像、コメントなど)が1度きりの登場でかつ10行程度のものであればUIScollViewにaddしてプロパティにViewの参照をもっておくほうがそれぞれのViewに入力された情報も簡単に取得できるので便利です。TableViewだとcellForRowで再描画するために、Viewの値をインデックス付きで保持するために、各Viewの値をdelegateなりで受け取って云々と面倒です。

めんどくさくなってきたのでソースコードへどうぞ。

github.com

ソースコードのVisualFormatの中身の順序を変えると表示順が変更されます。縦方向だけVisualFormat使うといいよ。

AutoLayoutでスワイプできるUITableViewCellのサンプルを更新しました。

セルをスワイプできるUITableViewのサンプルプロジェクトをずっと前に書いていましたがSwift1.2くらいだったので、Swift4に更新しました。

AutoLayoutを使っているのでConstraintを直接いじる形でもよかったのですが、一時的なViewの移動に最適なUIView.transformをいじる形にしてみました。

github.com

iPhoneX && UITabBarController && UIToolbar && SafeArea && Blur

iPhoneXでUITabBarControllerのタブをhiddenにしてもSafeAreaが伸びてくれずに、タブ上部に表示しているUIToolbarのblurがHomeIndicatorまで伸びてくれない場合に以下の対応で対症療法できることが判明しました。

UITabbarの上にUIToolbarの構成で、UIToolbarはSafeArea.bottomに対して貼り付けている状態です。

f:id:tomoyaonishi:20171020195548p:plain:w300

UITabbarをhiddenにするとHomeIndicatorまでBlurが伸びません。

f:id:tomoyaonishi:20171020195821p:plain:w300

UITabBarがない場合のSafeArea.bottomの位置までUIToolbarを下げてあげる(-49.0pt)となんとBlurがHomeIndicatorまで伸びてくれます。たぶんiOSのバグですが、その場しのぎの対応であればこれがいいかもしれません。

class FirstViewController: UIViewController {
    @IBOutlet weak var bottomMargin: NSLayoutConstraint!
    
    @IBAction func hideBar(_ sender: Any) {
        guard let tab = tabBarController?.tabBar else { return }
        if tab.isHidden {
            tabBarController?.tabBar.isHidden = false
            bottomMargin.constant = 0.0
        } else {
            tabBarController?.tabBar.isHidden = true
            bottomMargin.constant = -49.0
        }
    }

}

f:id:tomoyaonishi:20171020200716p:plain:w300

UIBarPositioningなど考えうるものすべていじりましたがこれでしか直せませんでした。

なんかバックグラウンドいって戻ってくるとUITabBarがhiddenでもSafeAreaの位置が正しくなってるような気がするんですが、よくわかりません。。

FoursquareのAPIについて

FoursquareAPIが無料枠アカウントでは、商用目的では使えないという旨の記載があった;;

なんとかならないかと思い、直接Foursquareさんに問い合わせたところ、無料枠アカウントでも運用やソフトウェアの資金調達のための広告等はOKとの返事をもらいました。

これなら、もし運用費稼ぎたくなっても安心して使えますね!

引用

you can use the API's up to the current maximum rate limit for free and would not require a commercial license if you're just using advertising to fund your app/software.

Homebrewを再インストール

El CapitanにアップデートしたらMacパーミッションの周りが変わったみたいでhomebrewが使えなくなりました。

手動でパーミッション変えたり、解決策はあるようでしたが、めんどくさいのでHomebrew自体を再インストールすることにしました。

まずはアンインストール

$ ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/uninstall)"

そのあとインストール

/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"

これで再インストール完了です。

CentOS7.1にてApacheとPythonでCGI

CentOS7.1にApacheを入れた状態でPythonCGIとして動かしたかったのでその時の手順をメモしておきます。 

IDCFクラウドの一番貧弱なサーバを借りてやっております。

Apacheファイアウォールなどの設定はできていて、index.htmlは見れる状態からスタートです。

apacheの設定ファイルを編集します。

$ sudo vim /etc/httpd/conf/httpd.conf

長々と設定が書かれていますが、以下の箇所を見つけます。

<Directory "/var/www/cgi-bin">
    AllowOverride None
    Options None
    Require all granted
</Directory>

次のように修正します。オプションにCGIで実行可能、実行するファイルの拡張子は.py .cgiとします。(.cgiは今回は使いません)

<Directory "/var/www/cgi-bin">
    AllowOverride None
    Options ExecCGI
    Require all granted
    AddHandler cgi-script .cgi .py
</Directory>

設定ファイルの編集はこれで終わりです。CGIスクリプトを作成します。

CGIスクリプトの置き場所に移動します。

$ cd /var/www/cgi-bin/

実行ファイルを作成します。

sudo vim test.py

---

#!/bin/python3.5

print("Content-Type: text/plain")
print("")
print("Hello World")

!bin/python3.5のところはpython3.5を入れているのでこうなっています。デフォルトのpython2.7でよければ#!bash/pythonでいけるとおもいます。 CGIスクリプトの出力方法については別途検索してください。ヘッダーとボディの間に1行改行のためのprintが入っています。

権限を付与します。

sudo chmod 755  test.py

ブラウザから

http://IPアドレス/cgi-bin/test.py

にアクセスすれば「Hello World」と出ます。