tomoyaonishiのブログ

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

Animate.cssが便利

Animate.cssという軽量CSSアニメーションライブラリがとても便利です。

クラス名に適用したいアニメーションを記述するだけで自動でアニメーションしてくれます。

公式サイトですべてのアニメーションがプレビューができるので導入しやすいと思います。

daneden.github.io

シェルをbashからfishに移行した

bashからfishというものに移行しました。

デフォルト設定で見やすく、履歴やディレクトリ、ファイル等の入力補完もしてくれます。

Web Based configurationというものがとても便利で、ターミナルの表示のカスタマイズをブラウザのGUI上で設定できるのです。

要はbashrc,bash_profile等でごりごり設定を書いていたものが、GUIで見た目をカスタマイズできるということです。

bashrcをマスターする意味を見いだせない自分的には嬉しいポイントです。

f:id:tomoyaonishi:20190512114542p:plain

アプリエンジニアやWebフロントエンジニアなどたまにターミナルを使うくらいの人なら乗り換えたほうが面倒な設定から逃れられると思います!

fishshell.com

UITableViewでAutoLayoutのワーニングが出る問題の対処方法

画像のようなUITableViewCellをAutoLayoutで組んでいるとき、ふと謎のAutoLayoutのワーニングがでることがあります。

f:id:tomoyaonishi:20190509215414p:plain

Unable to simultaneously satisfy constraints.
    Probably at least one of the constraints in the following list is one you don't want. Try this: (1) look at each constraint and try to figure out which you don't expect; (2) find the code that added the unwanted constraint or constraints and fix it. (Note: If you're seeing NSAutoresizingMaskLayoutConstraints that you don't understand, refer to the documentation for the UIView property translatesAutoresizingMaskIntoConstraints) 
(
    "<NSLayoutConstraint:0x17ee8ba0 V:[UILabel:0x17efb790(20)]>",
    "<NSLayoutConstraint:0x17edd410 V:|-(2)-[UILabel:0x17efb790]   (Names: '|':UITableViewCellContentView:0x17eeb260 )>",
    "<NSLayoutConstraint:0x17edf100 V:[UILabel:0x17ee4a70]-(0)-|   (Names: '|':UITableViewCellContentView:0x17eeb260 )>",
    "<NSLayoutConstraint:0x17edec60 V:[UILabel:0x17efb790]-(2)-[UILabel:0x17ee4a70]>",
    "<NSAutoresizingMaskLayoutConstraint:0x17f33410 h=--& v=--& V:[UITableViewCellContentView:0x17eeb260(0)]>"
)
Will attempt to recover by breaking constraint 
<NSLayoutConstraint:0x17ee8ba0 V:[UILabel:0x17efb790(20)]>
Break on objc_exception_throw to catch this in the debugger.
The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in <UIKit/UIView.h> may also be helpful.

このようなときは一番下に貼っている制約をpriorityを999にしてみましょう。大体の場合はこれで解決します。

f:id:tomoyaonishi:20190509215524p:plain

おそらく、初回だけcontentViewが小さくで潰れてしまっているか、罫線との兼ね合いで制約が壊れるんだと思います。 対応いれなくても動作には問題ないですが、気持ち悪いですよね。

また、UIStackViewやUIScrollViewなんかでも同じようなことがおこることがあります。

どんなViewでも動作には問題ないけど、縦方向のAutoLayoutのワーニングがあるときはpriorityを下げる対応をいれてみるといいかもしれません。

create-react-appでsassを使う

create-react-appの環境でsassを使いたかったのですが、導入方法がわからなかったのでメモです。

背景として、webフロント画面を作るのにオーバーキルなライブラリ(MaterialUI、OnsenUI)でハマることも多かった&カスタマイズしづらい&マテリアルデザインが多すぎて飽きたので、完全にcssだけで作られたシンプルなライブラリbulmaでSPAを構築することにしました。

create-react-appのプロジェクト内で以下を実行します。

$ yarn add node-sass

あとは.cssを.scssか.sassにリネームするだけで、js側でcssと同様にimportできますし、ホットリロードもされます。

やっぱ何を調べるにしても公式ドキュメントですね。

facebook.github.io

UIVIewControllerカスタムトランジションを理解する 1

前回以下の記事でカスタムアニメーションとカスタムプレゼンテーションについて説明しました。

UIVIewControllerカスタムトランジションを理解する 1 - tomoyaonishiのブログ

前回の記事で説明しきれなかったインタラクティブトランジションについて説明します。

インタラクティブトランジション

UIViewControllerInteractiveTransitioningというものを使います。

UIViewControllerInteractiveTransitioning は非常にシンプルなprotocolで、今回は public func startInteractiveTransition(_ transitionContext: UIViewControllerContextTransitioning) を使います。

これに関しては、ソースコードをみたほうがわかりやすいとおもいます。

まずはtransitioningDelegateを以下のようにします。この辺わからない人は前回の記事をみてください。

extension InteractiveTransitionViewController: UIViewControllerTransitioningDelegate {
    func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
        return NoAnimatedAnimationController()
    }
    func interactionControllerForDismissal(using animator: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? {
        return pullToDismissTransitionController
    }
}

interactionControllerForDismissalでdismissをインタラクティブにするということになります。presentのときは同じようなメソッドがあるのでそちらを実装します。

では、実際に指に追従するようなViewControllerのdismissの仕方はどのように実装するのでしょうか。

このようになります。

class NoAnimatedAnimationController: NSObject, UIViewControllerAnimatedTransitioning {
    func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval { return 0 }
    func animateTransition(using transitionContext: UIViewControllerContextTransitioning) { }
}

class PullToDismissTransitionController: NSObject, UIViewControllerInteractiveTransitioning {
    var isInteractive: Bool = true
    weak var viewController: UIViewController?
    weak var transitionContext: UIViewControllerContextTransitioning?

    init(viewController: UIViewController) {
        self.viewController = viewController
    }

    func startInteractiveTransition(_ transitionContext: UIViewControllerContextTransitioning) {
        self.transitionContext = transitionContext
    }

    @objc func observePanGesture(panGesture: UIPanGestureRecognizer) {
        guard let viewController = viewController else {
            return
        }
        guard let view = panGesture.view else {
            return
        }

        let translation = panGesture.translation(in: view)
        let velocity = panGesture.velocity(in: view)

        switch panGesture.state {
        case .possible:
            break
        case .began:
            viewController.dismiss(animated: true, completion: nil)
        case .changed:
            view.transform.ty = max(translation.y, 0)
        case .ended, .failed, .cancelled:
            if velocity.y > 0 && translation.y > 80 {
                let initialVelocity = abs(velocity.y)
                let distance = view.bounds.height - translation.y

                UIView.animate(withDuration: 0.45,
                               delay: 0,
                               usingSpringWithDamping: 1.0,
                               initialSpringVelocity: initialVelocity / distance,
                               options: .curveEaseInOut,
                               animations: {
                                view.transform.ty = view.bounds.maxY
                },
                               completion: { _ in
                                self.transitionContext?.completeTransition(true)
                })
            } else {
                guard translation.y > 0 else {
                    self.transitionContext?.completeTransition(false)
                    return
                }

                let initialVelocity = velocity.y > 0 ? 0 : abs(velocity.y)
                let distance = translation.y

                UIView.animate(withDuration: 0.45,
                               delay: 0,
                               usingSpringWithDamping: 1.0,
                               initialSpringVelocity: initialVelocity / distance,
                               options: .curveEaseInOut,
                               animations: {
                                view.transform = .identity
                },
                               completion: { _ in
                                self.transitionContext?.completeTransition(false)
                })
            }
        }
    }
}

self.transitionContext?.completeTransition(true) self.transitionContext?.completeTransition(false) viewController.dismiss(animated: true, completion: nil)

この辺がミソでしょうか。トランジションが完了したのか、キャンセルされてもとに戻るのか、dismiss自体をいつ開始させるかを自分で書く感じです。

ViewController側でPullToDismissControllerのobservePanGesture()にUIPanGestureをセットしてあげます。

final class InteractiveTransitionViewController: UIViewController {
    lazy var pullToDismissTransitionController = PullToDismissTransitionController(viewController: self)

    override func viewDidLoad() {
        super.viewDidLoad()
        let pan = UIPanGestureRecognizer(target: pullToDismissTransitionController,
                                         action: #selector(PullToDismissTransitionController.observePanGesture(panGesture:)))
        view.addGestureRecognizer(pan)
    }
}

これで完成です。

カスタムトランジションで登場するクラスやprotocolはどれも名前がわかりにくいですが、理解すればかなり使いまわしがしやすい設計になっているようです。

また、ViewControllerと密接に関わるため、お互いに参照をもたせてしまうようなやり方が一番いいかもしれません。 他にも実際にリッチに作ろうとすると必要なフラグも多くなり、ただでさえややこしいので変に設計を意識せずに惜しみなく変数にもたせてしまうやり方がいいとおもいます。

インタラクティブトランジションと通常のトランジションを切り替えたいときは、interactionControllerForDismissal()でnilを返せばよいです。

カスタムトランジションはカスタムアニメーション、カスタムプレゼンテーション、インタラクティブトランジションの組み合わせで自由に作ることができます。それぞれを単品で理解した上で、取り掛からないとわけがわからなくなってしまうと思うので、1つずつ丁寧にみてみてください。

また、UIPercentDrivenInteractiveTransitionというクラスがありますが、こちらは進捗率に応じて、AnimationControllerで定義したアニメーションを進めるというものです。使い分けするといいでしょう。

ソースコードはこちら。

github.com

【2019】自由が丘で快適作業【電源、wi-fi、カフェ、PayPay】

自由が丘で快適作業ができるカフェを見つけました。

電源、Wi-Fi、PayPayあります。

 

自由が丘plus南口店というカフェです。

https://g.co/kgs/GEFKc6

 

100席ほどの席があり、テーブル席や2人席、1人用の席も多いです。電源も全ての席に付いています。ハンバーグなどの食事もできて、アルコールからソフトドリンクまでかなり充実しています。

f:id:tomoyaonishi:20190220084804j:image
f:id:tomoyaonishi:20190220084808j:image
f:id:tomoyaonishi:20190220084820j:image

 

---

 

同じお店の名前でもう1店舗ありますが、こちらはこじんまりとした感じで、ケーキやドリンクがいただけます。Wi-Fiはありますが、電源はなさそうでした。子連れを想定しているのかキッズルームがあります。

テーブル席とカウンター席があり、穴場なのか土日でも空いています。

http://jiyugaoka-plus.jp/sp/t200317380/

 

f:id:tomoyaonishi:20190220084816j:image
f:id:tomoyaonishi:20190220084812j:image

 

 

MacBookのストレージの大部分がシステムに占領されている件について

ストレージ250GBのMacBookを使っているのですが、ストレージ容量がなくなってきて、内訳を確認すると100GBくらいシステムに使われていました。調べてみるとXcode関連で80GBくらい空けることができました。その方法を残します。

 

調査

du -sh /* とかでルートディレクトリから容量が大きいところを探します。

 

原因

自分の場合はここがデカかったです。

/Users/xxx/Library/Developer/

 

この中には以下のようなディレクトリがあるのですが、シミュレータ関連が容量を喰ってました。

たしかにXcodeで全デバイス全OSのシミュレータをインストールしてました。。

 

CoreSimulator XCPGDevices XCTestDevices Xcode

 

シミュレータ周りは無理やり消しても大丈夫そうだったのでrmとかで不要なシミュレータは普通に削除しました!

 

 

UIVIewControllerカスタムトランジションを理解する 1

UIViewControllerのカスタムトランジション

UIViewControllerのカスタムトランジションってめっちゃ複雑じゃないないですか?

実際にやってみるとわりと簡単なんですが、クラスがいろいろあったり、デリゲートメソッドも似たようなものが多くて、UIViewControllerAnimatedTransitioningとかUIViewControllerTransitioningDelegateとか、「すみません、もう一度お願いします」みたいなのが多すぎてさらに混乱するんでしょね。

あとはAppleのサンプルコードもネット上のサンプルコードも超絶どシンプルなものがないのも要因かと。

OSSもオーバーキルなものが多いし、ごちゃごちゃやってて結局わかりづらい。

一見ごちゃごちゃしているのですが、汎用性、再利用性も高く設計されていて「めっちゃええやん..」ってなります。

というわけで、カスタムトランジションのシンプルなサンプルを書いたので自由にコピペして使ってください

UIViewでも簡単にできますが、UIViewControllerベースのほうがライフサイクルもしっかりしていて、最近のAppleの主流?(UIAlertControllerとか)にも合っていていいかなと思います。


簡単な解説

大きく3つくらいの利用目的があるかなと思います。

  • present時の遷移アニメーションを変更したい(遷移アニメーションの変更
  • present時のpresentされるVCの表示位置、大きさ、また背景にブラーをつけるなどをしたい(present状態そのものの変更
  • インタラクティブにVCをpresent or dismissさせたい

注意ですが、アニメーションとプレゼンテーションを明確に意識してください。

アニメーションは遷移時に下から出てくるのか、右から出てくるのか、フェードインなのかということです。

プレゼンテーションはどこにどの大きさで表示されるか、背景はどうするかなど、遷移アニメーション後の状態のことを指します。

present時の遷移アニメーションだけを変更したい

modalPresentationStyleを自作するイメージです。 やることは3つです。

1.UIVC.transitioningDelegateをどこかにセットする。

    override func awakeFromNib() {
        super.awakeFromNib()
        transitioningDelegate = transitionController
    }

2.present or dismissあるいは両方のデリゲートメソッドを実装し、アニメーションオブジェクトを返す

final class CustomAnimationTransitions: NSObject, UIViewControllerTransitioningDelegate {

    // これらのメソッドはpresent() or dismiss()の直後に呼ばれる。

    func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
        // nilを返すとでデフォルトのアニメーションできる
        return AnimationController(isPresenting: true)
    }

    func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
        // すべてoptionalのprotocolなのでpresent時だけ変えたいならこのメソッド自体不要
        return AnimationController(isPresenting: false)
    }

3.アニメーションオブジェクトを実装する

    final class AnimationController: NSObject, UIViewControllerAnimatedTransitioning {
        let isPresenting: Bool

        init(isPresenting: Bool) {
            self.isPresenting = isPresenting
        }

        func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
            return 0.5
        }

        func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
            // ここにアニメーションを書く!
            if isPresenting {
                animateForPresentation(context: transitionContext)
            } else {
                animateForDismissal(context: transitionContext)
            }
        }

        func animateForPresentation(context: UIViewControllerContextTransitioning) {
            guard let destination = context.viewController(forKey: .to) else {
                return
            }

            let containerView = context.containerView
            destination.view.transform.ty = containerView.frame.maxY
            containerView.addSubview(destination.view) // 遷移先のViewを準備する

            let timing = UISpringTimingParameters(dampingRatio: 0.5)
            let animator = UIViewPropertyAnimator(duration: transitionDuration(using: context), timingParameters: timing)
            animator.addAnimations {
                destination.view.transform = .identity
            }
            animator.addCompletion { (position) in
                context.completeTransition(position == .end)
            }
            animator.startAnimation()
        }

        func animateForDismissal(context: UIViewControllerContextTransitioning) {
            guard let from = context.viewController(forKey: .from) else { return }
            let containerView = context.containerView

            UIView.animate(withDuration: transitionDuration(using: context),
                           animations: {
                            from.view.transform.ty = -containerView.bounds.height
            },
                           completion: { finished in
                            context.completeTransition(true)
            })
        }
    }

これだけでpresent時のアニメーションを書き換えることができました。

クラスが別れているので別のVCでも使いたいときは簡単に使えますね。

present時のpresentされるVCの表示位置、大きさ、また背景にブラーをつけるなどをしたい

次はカスタムプレゼンテーションです。アニメーションは変更せず、表示位置を変えたり、大きさを変えたりできます。

アニメーションとは無関係です。

アラート、アクションシート、サイドバーなどを自作したいときに使えます。

やることは3つです。

1.UIVC.transitioningDelegateをどこかにセットする。

解説済みなので省略

2.デリゲートメソッドを実装し、UIPresentationControllerオブジェクトを返す

present時の状態を変更するものなので、アニメーションの変更のようにdismiss時のメソッドはありません。

final class CustomPresentationTransitions: NSObject, UIViewControllerTransitioningDelegate {

    func presentationController(forPresented presented: UIViewController, presenting: UIViewController?, source: UIViewController) -> UIPresentationController? {
        return PresentationController(presentedViewController: presented, presenting: presenting)
    }

}

3.UIPresentationControllerオブジェクトを実装する

final class PresentationController: UIPresentationController {

        override var frameOfPresentedViewInContainerView: CGRect {
            let frame = super.frameOfPresentedViewInContainerView
            return frame.inset(by: UIEdgeInsets(top: 100, left: 50, bottom: 100, right: 50))

            // サイドバーのようにしたいとき
            // return CGRect(x: frame.width / 2, y: frame.origin.y, width: frame.width, height: frame.height)
        }

        override func containerViewWillLayoutSubviews() {
            super.containerViewWillLayoutSubviews()
            presentedView?.frame = frameOfPresentedViewInContainerView
        }
    }

これで全画面のpresentではなくて一部に表示するようなことができます。

カスタムトランジションのうち、カスタムアニメーションとカスタムプレゼンテーションを解説しました。

紹介したクラスはほかにも様々なプロパティやメソッドをもっているので調べてみてください。

またアニメーションやプレゼンテーションの仕方によってはViewControllerの参照を持っておいたりする必要が出てくると思います。

カスタムアニメーションとカスタムプレゼンテーションの合わせ技で好きなアニメーションで好きなpresent状態にできます。

またインタラクティブトランジションでドラッグして閉じるとかもできますが、これら2つはまたの機会にまとめます。

ソースコード(著作権ありません、自由にコピペしてください。)

github.com

2019年のXXを予測する

UXが〜をやたら使う人はあまり好きではないのですが、最近CXとかいう言葉もあるようですね。

 

これ以上出てきてほしくないですが、次はEXが出てくると予測します。

 

Employee eXperiment

 

顧客側ではなくて、店舗、店員、従業員側の体験です。

 

当たってたら褒めてください。以上です。

 

ただのUIのことをやたらUXが〜とかいうやつはなんなんだ。。

人工知能(AI)の現状と課題を正しく理解できる本

iOS12でApple Books(iBooks)がアップデートされUI含めてとても便利にわかりやすくなっていたので最近はApple Books推しです。

Apple Booksで購入できる本で流行りの人工知能に関する本を2冊読みました。

一冊目はこちら。

人工知能は人間を超えるか

人工知能は人間を超えるか

  • 松尾豊
  • コンピュータ
  • ¥1,000

AI分野で著名な松尾さんの本です。AIの歴史をベースに世間の誤解、誤解を招くマスコミの表現等、AIとは何かを「正しく」理解できる本でした。マーケット的なAIとテクノロジー的なAIは区別して使いたいなと思いました。

二冊目はこちら。

こちらは松尾さんの本とは少し違って社会への問いかけみたいな本になっています。東大合格を目指しMARCH合格レベルのAIを実際に作った研究者がAIが得意なこと、苦手なこと、限界を解説し、そこからみえた現実社会の課題とAIが普及したあとの世界観が面白かったです。

現時点の日本にAI以下の知能しか持たない人が多くいることがわかってきて、その人達がAIに仕事が奪われたあと、どうなるのか? AIは大学に合格できるレベルの読解力や知識を持つことが可能ということは証明したので、AIに仕事を奪われた人はAIに仕事を奪われない仕事(つまりAI以上の読解力や知識が必要な仕事が)をこなすこができなければならない。みたいなくだりの話が印象的でした。

一冊目で理論と歴史を体系的に、二冊目で社会への影響とか現実社会でのAIの実力と課題とかがインプットできるのでおすすめの二冊です。

自分は本は多くは読まなくて、読むなら理論や概念の理解になる本がほとんどです。コードベースの技術本はほぼ買ったことないですね。後者はWeb上に情報が多く、結局ドキュメントが正解であって、本は古かったり流行とはかけ離れてたりするのが嫌なんですよね。前者は長い時間かけて体系化されたものだったり、断片的でなくて通してインプットすることが大事かなと思っています。

以上!

MaterialUIでTypographyが赤色になるバグ?

MaterialUIを使っていて、Typographyの文字が何故か赤色になるバグ?に遭遇しました。

ローカルビルド中は問題なかったのに、firebaseにhostingすると赤色になってしまっていました。

原因はIconを使っていることでした。よくわかりませんがIconが内部で使っているCSSクラスが悪さしているようでしたのでIconはつかわずに \<i> タグで代用するようにしたら直りました。

よくわかりません。

Onsen UIをReact+TypeScriptで使うときの手順

Onsen UIとは

Onsen UIはcssフレームワークです。HTMLとしても使えますが、Vue.js, React.jsでも使えるラッパーが用意されています。

特徴は、AndroidならMaterialDesign、iOSならiOS Designに自動で見た目を切り替えてくれることです。モバイルWebアプリケーションを作るときにネイティブっぽく見せる、OSデザインをメインに採用したいときなどに重宝します。お気に入りです :)

onsen.io

導入方法

日本語ページが有るのですが、翻訳が古いのか情報が欠け落ちている部分があるので英語サイトがおすすめです。

まずはinstall

yarn add onsenui react-onsenui

importはこんな感じ。

import {BackButton, Card, Icon, Page, Tab, Tabbar, Toolbar, ToolbarButton} from "react-onsenui";

そして、ハマった部分がこれです。これをindex.tsxあたりでimportする。

import 'onsenui/css/onsenui.css';
import 'onsenui/css/onsen-css-components.css';

index.tsx

import * as React from 'react';
import * as ReactDOM from 'react-dom';
import App from './App';
import './index.css';
import 'onsenui/css/onsenui.css';
import 'onsenui/css/onsen-css-components.css';

ReactDOM.render(
  <App />,
  document.getElementById('root') as HTMLElement
);

日本語サイトみてたらcss importのこと載ってなくてはまりました。。

TypeScriptのときは型はこれをインストールすればよいです。

@types/react-onsenui

「React & Redux & TypeScript & fsa」 のシンプルなテンプレートを作った

ReactとReduxの環境をTypeScriptで構築するシンプルなテンプレート的なものを作りました。

create-react-appを使えば簡単にReact & TypeScript環境は作れるのですが、Reduxを入れ込むのが面倒&ディレクトリ作成など新規プロジェクトを作成するごとに機械的にやることなので自分用にサンプルプロジェクトとして作りました。今後はこれを使いまわそうと思います。

TypeScriptの場合、普通にReduxをいれると型周りの恩恵が受けられないことがあるので、typescript-fsaを採用しています。Action定義が見やすくて型安全で結構いいです。

lint周りは基本に忠実ですが、import文やオブジェクトのキーの並び順を「アルファベット順にせよ」というエラーはそこまで恩恵もなく、任意のグルーピングができなくなるのでtsling.jsonでオフにしています。

僕はこのルールのモチベーションが全くわかりません。。ビルドのたびに「アルファベット順じゃないよーww」と言われるのでストレスで開発効率が1/100くらいになります。しかもどこの文の順序がおかしいかは教えてくれません。(こんなことに対してプラグインもいれたくない。)

テンプレートコードはここからどうぞ。

yarn install, yarn startで動くと思います。

github.com

ライセンス情報を自動で設定に吐き出してくれる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自体はそのままでもエディタだったりサブで展開しているツール群ははそのうち統廃合されそうですね。