クロ戌さんちのオフ会に参加してきました
日曜日に、クロ戌さんという霊能者&ヨガの先生のオンラインサロンのオフ会に行ってきました。
クロ戌さんを知ったのはYoutube部活One
最初はわりと軽めノリのオカルト部の心霊スポット巡りとかを見ていたのですが、 基本、疑い深い私がこのひと、すごい本物なのかも、、って思って、オンラインサロンに入ることにしました。 なんか、基本的なお話が日本の神道なのも好感度高い理由。なんとなくだけど。 サロンの内容は簡易霊視相談とか、週間タロット運勢診断とか、ここでしか書けない話とかとかとか、内容濃くてすごく満足です。
んでね、サロンにはお会いしたことがあるお友達、だれもいないのですが、オフ会があるというので参加してきました。 オフ会は午前中は明治神宮に参拝して、代々木公園でヨガをしてきたそうです。
オンラインサロン限定オフ会にて無料の明治神宮参拝のパワースポットツアーと代々木公園パークヨガ😊✨皆んな最高!ちょー楽しい♪夕方からは懇親会☆彡 オンラインサロンの登録はこちらから→ https://t.co/2pXPSG1uTs pic.twitter.com/EC6uRCKTsJ
— 黒∴戌∵仁ॐ クロ戌 (@kuroinutarot) 2019年10月20日
私はちょっと午前中は参加できず、午後3時から下北沢のサロンにもどってくるそうなのでそれに合わせて伺いました。 みんな、飛行機できましたとか、新幹線で広島からきましたとか、車を6時間運転してきましたとか、夜行バスできましたとかとか 日本全国からこの日のために集まってきたのには驚きました。
オフ会後半は17時から体験ヨガで、18時から宴会の予定だったのですが、人数が多くてヨガはなくなり、みんなで丸く座ってクロ戌さんのお話をきいたり(これがすごい儲かったかんじ)してるうちにお食事とお酒をいただくことになりました。
もうね、台所でえらい師匠がお酒作ったりしてるんです。ジンジャーエール2こ、カシスオレンジ4こ、日本酒2こ、ビール9ことか言っちゃって。 それを、こっち側で丸くすわって、雑談しながらいただいて、 次は、お肉炒めたやつとか、チキン揚げたやつとか、チャーハン2種類とか、お豆腐になんか乗っかってるやつとか、 次から次へと出てくるわけです。 こっちで、きゃっきゃ雑談してるのに。背中向けてえらい師匠が、すごくおいしいご飯作ってくれるんです。 いいのか?わたしら、働かなくていいのか?普通逆なんじゃ???って思ったのですが、 霊性が高い方が作ってくれたお酒とかご飯とかいただいてるって、これってはじめてだけどすっごいことなんじゃないか? って思いました。 お酒も、ご飯もたくさんおかわりしてたくさんいただきました。
師匠がやっと座ると、今度は簡易鑑定やら、体の調整やら、除霊やらのはじまり。 痛そうに顔をしかめて肩を揉まれてた人が、背中でお祈りみたいなポーズができるよぐらいに肩がほぐれたり、 お母さんが連れてきた息子さんの仕事に「やめたほうがいい。もっとお母さんにあまえな」とばっさり、 お母さんに心配かけたらいけないと我慢してた息子さんの気持ちをあっさり見抜いたり。すごい。
昨日聞いたことば
金楽欲の先には、幸せはないよ。人の為と書いて偽だからね。愛とか執着になるから注意して。
胸に刻んでおきます。
PICOLで使ったアニメーションたち。iOSで使えるアニメーションあれこれ
PICOLではアニメーションもゲームっぽく、いろいろ使っています。
ほぼ全部!アニメーション書き出してみます。
スプラッシュ
UIImageView#animationImages
— なおち (@naochi___) 2018年7月13日
前の記事でかいたようにUIImageViewのanimationImagesを使用してパタパタ漫画をアニメーションしています。
naochi-interrupt.hatenadiary.com
コードはこんな感じ。
var imageList:[UIImage] = [] for i in (1 ... 35) { // let image = UIImage(named: "splash-\(i)") let imagepath = Bundle.main.path(forResource: "splash\(i)", ofType: "png") let image = UIImage(contentsOfFile: imagepath!) imageList.append(image!) } imageView.animationImages = imageList
Home画面
CAKeyframeAnimation
— なおち (@naochi___) 2018年7月13日上下の雲が、右から左に流れています。 あと、カメラを起動する、バーコード型の丸いボタンがじわーっとアニメーションしています。 丸いボタンは、スプラッシュと同様にImageViewのanimationImagesを使っています。
雲はCAKeyframeAnimationです。
let animation = CAKeyframeAnimation.init(keyPath: "position.x") animation.isRemovedOnCompletion = true animation.repeatCount = .infinity animation.values = [621,-207] animation.duration = 19.0 animation.fillMode = kCAFillModeBoth self.headerImageView1.layer.add(animation, forKey: "position.x")
これが、いただいた画像を使ってアニメーションしてみて、どう調節しても継ぎ目のところでアニメーションがかくっとしてしまう。
もともと業務系アプリ作ってた私は、「いいじゃん。ちょびっとだし」とか思ったのですが、WEB屋さんとかゲーム屋さんはアーティストに近いのでどうしても許せないらしく、 なんとかならないの?と言われ続けてがんばったのですが、なかなかできないでいました。
そしたらある日の会議でイケメンの社長が以前WEB系の技術者だったそうで、アドバイスをいただき、そのとおりに実装したらできました。
Twitterの動画ではわかりにくいのでダウンロードして直接みてみてください。
まず、あがってきたデザインのヘッダを3枚並べて、
真ん中のヘッダを左右反転させてくっつけます。 コードで書いてもいいけど、1回のことなので実際に素材をつくりました。
(わかりやすいように真ん中は背景を黒くしました)
それを画面に使うとXibはこんな感じになります。 素材は(2484 x 150)ですが、Retinaなので画面サイズとしては1242 x 75)
CAAnimationはUIViewと座標系が違うので、この初期値の状態でX座標は真ん中の650という値になります。
そして、端っこまできっかりアニメーションしてしまうとアニメーションの最初の画面と最後の画面に違いが出てしまうためにかくついてしまうので、同じになるように、3つめのヘッダの位置の最初でアニメーションが切り替わるようにしたら!綺麗に動くようになりました。
バーコードカメラのバーコード検出アニメーション
UIView.animation
— なおち (@naochi___) July 13, 2018
動画斜めにしなきゃよかった
UIView.animate(withDuration: 0.2, delay: 0.0, options: .repeat, animations: { () -> Void in borderView.alpha = 0 }, completion: { _ in borderView.alpha = 1 })
トースト出したり、ダイアログを出したりするところでも使っていますが割愛。UIViewのアニメーションです。 iOSでアプリ作った人なら1回は使ったことがあるはず。
囲みを斜めにしてないのはほんとすみません。
査定演出アニメーション
HTML
— なおち (@naochi___) July 16, 2018
査定のカッコイイアニメーションはデザイン会社の ビジネスが進化するモバイル・コンテンツを : : caeruX [カエルエックス] さんが作ってくれました。私はWKWebviewを貼り付けただけです
func loadHtml(){ webView = WKWebView.init(frame: self.animationView.frame) animationView.addSubview(webView) webView.addSubview(self.skipButton) let fileURL = URL(fileURLWithPath: Bundle.main.path(forResource:"assessment_ver2", ofType: "html")!) webView.loadFileURL(fileURL, allowingReadAccessTo: fileURL) }
強いて言えば、私の古い端末(といってもiPhone6Plus)と、iPhoneXとかでは再生速度が違い、アニメーションの終了時間を端末ごとに切り替えて終わりを検出していることぐらいでしょうか。
チケット演出アニメーション
CAKeyframeAnimation + UIViewAnimation + SpliteKit
— なおち (@naochi___) July 16, 2018
スロットがくるくるまわるのはCAKeyframeAnimationです。つなげてアニメーションしているのは上で書いた雲と同じ原理。 サーバーから受け取ったチケットのパーセンテージでアニメーションの終了座標を切り替えています。
コインがバラバラ落ちてくるのはSpliteKitを使いました。
雪をコインの画像に置き換えて、パーセンテージごとに数を増やしただけです。 こんなのが簡単に作れるなんて、すごいいい時代ですね。
残り時間アニメーションのあとのパルスのようなアニメーションはCAKeyframeAnimation 長い画像にマスクをかけてマスクをアニメーションしています。
あ、でも、要所要所でぽよんとか、ぴょんとか入れたのは割愛。
PICOLを丸裸。使ったものとか書いてみる
PICOL第三弾目です
naochi-interrupt.hatenadiary.com
naochi-interrupt.hatenadiary.com
体制
開発に携わる人はざっくり分けて
- マネジメントする人
- サーバー側の人
- UIデザインする人
- クライアントのアプリの人
って感じで私が担当したのは最後のアプリの人のところです。
プロジェクト管理ツール
- Kibela
- 議事録を残す
- Github
- ソース管理とクライアント側のAPIドキュメント
- Slack
- GithubのログとかTrelloでメンションされたときに通知が届くのが便利だった
- Trello
- タスク、バグ管理
技術的なやつ
サーバー側
- Ruby on Rails
- Swagger ( Codegen )
- AWS
クライアント
- swift
- XCTest,UITestとか
- Crashlytics,Fabric
- Alamofire
- RXSwift
- jazzy
なんか、クライアントはswiftって書いてそれで終わりなんだけど、ひねり出してみた。
サーバーの方が数段働き者です。もっとあるし!とかはらさんにいわれますたぶん。AWSのS3にあげるのもAPIがやってくれてるし。クライアント側からはノータッチです。
Alamofireとかとかも色々書いていますが、SwaggerCodegenがAPIにアクセスするときのモデルとか、API接続用のクラスとか自動生成してくれましたので使っています。
APIドキュメントと、クライアント側のモックサーバーも作ってくれまして。これはすごいって思いました。
自分のMacにサーバーが立っているので、データを自由にいじってテストできるし、すごく便利でした。最初、nameがintになってて有無を言わさず全然データがとれないとかそういうのが多発しましたが、そのうち慣れてきたし。
ビルドスキームはこんなかんじ
- ローカルデバッグの時
- ローカルに立てたRubyのサーバーでApiを投げる。あ、なのでローカルにPostgreSQLのDBが立ってます。開発機にデプロイする前の開発中のサーバーのソースも、指定されたブランチをcloneしてrake db:migrateとかして、ローカルでサーバー立てれば開発が進められます。この辺のことは、前の仕事でRuby on RailsでWebサイトを立ち上げていたのでなんとかできた。やっといてよかった。
- みんなでデバッグの時
- FabricでAdhocを配信、接続先は開発用サーバー
- 本番ビルド
- 本番機接続
それにしても、もっとかっこいい言葉で説明できるようになりたいです。
PICOLで発見したこと。アセットカタログは計画的に利用したほうがいい
最近、iPhoneも性能が上がってあんまりメモリのこととか気にしないでガンガン作ることが多いのかも。
naochi-interrupt.hatenadiary.com
前回書いたようにPICOL(ピコル)-バーコードをピコってお金に変える”スピード買取”アプリはゲームっぽい楽しさを意識して作ったので、アニメーションとかとにかく多い。
まずはスプラッシュからアニメーションです。
— なおち (@naochi___) 2018年6月21日
ここだけの話ですが、国民的麦わら海賊団的な雑誌のアプリのスプラッシュがキラッとしてるのは私が作ったものです。
PICOLのスプラッシュはなんかかわいいので、よくあるAppDelegateのdidFinishLaunchingWithOptionsに一回挟んでおわり系ではなく、アプリをバックグラウンドに落として一定時間すぎてアプリをアクティブにしたらもう一度スプラッシュから始まるような作りにしています。
このアニメーションは、CABasicAnimationとか、そっち系のコードでアニメーションするのではなく、パタパタ漫画です。gifアニメーションみたいな、そういうの。
これはUIImageViewに配列でイメージを渡すという。ごくごく簡単なもの。 まあ、誰でもできる。
var imageList:[UIImage] = [] for i in (1 ... 35) { let image = UIImage(named: "splash-\(i)") imageList.append(image!) } imageView.animationImages = imageList
スプラッシュはアセットカタログに入れてあるpng35枚なので、このような書き方です。 ただ、メモリ量が大きいのです。使用メモリが多いと、なにかのきっかけでクラッシュしたりとか、動きがもっさりしたりとか、いやな動きが多くなります。 この感じでスプラッシュを作ってた時のメモリは恥ずかしながらこんなかんじ。 画像を最適化したり、色々やった後でこれ。
このときは、アプリが動いている時ずっとこんな感じのメモリで続いてて、アプリを一旦バックグラウンドに落としてすぐフォアグラウンドに戻すとガクッとメモリが減る。
InstrumentsのLeaksとか見てもメモリリークはしてないし試しにスプラッシュやめて見たらメモリが減ったわけです。
この配列に入ってるイメージが解放されなくて残ってるんだな〜〜〜といろいろやりました。
autoreleasepoolいれたり。
autoreleasepool(invoking: { var imageList:[UIImage] = [] for i in (1 ... 35) { let image = UIImage(named: "splash-\(i)") let image = UIImage(contentsOfFile: imagepath!) imageList.append(image!) } imageView.animationImages = imageList })
アニメーション終わってから涙ぐましい解放したりして。StackOverflowに書いてあったし。
imageView.startAnimating() DispatchQueue.main.asyncAfter(deadline: .now() + 1.5) { UIView.animate(withDuration: 1.0, delay: 0.1, options: .curveEaseInOut, animations: { () -> Void in self.imageView.alpha = 0 }, completion: { _ in self.imageView.image = nil self.imageView.stopAnimating() self.imageView.animationImages = nil self.imageView.layer.sublayers = nil self.imageView.removeFromSuperview() })
全然ダメでした。
そしたら、急に思い出しました。そう言えばアセットカタログがなかった昔は、普通にイメージをフォルダで追加してたなって。 そして、業務用のアプリは こういう書き方あんましちゃいけなかったんだっけってのを。
UIIimage* image = [UIImage imageNamed:@"imagename.png"];
こう書く。そうするとイメージがキャッシュされないんだって。
NSString* path = [[NSBundle mainBundle] pathForResource:@"imagename" ofType:@"png"]; UIImage* image = [[UIImage alloc] initWithContentsOfFile:path];
ってことで、アセットカタログに入れてあったイメージを使わず、昔の通りにファイルで追加して、呼び出して見た。
var imageList:[UIImage] = [] for i in (1 ... 35) { // let image = UIImage(named: "splash-\(i)") let imagepath = Bundle.main.path(forResource: "splash\(i)", ofType: "png") let image = UIImage(contentsOfFile: imagepath!) imageList.append(image!) } imageView.animationImages = imageList
そしたら、驚きの結果が
アセットカタログに入れるのは、何回も使うものだけにした方がいいんですね。つい便利なので文明の力に甘えきってしまっていて反省しました。
私にはすごい発見で、目からウロコがぼろぼろ落ちましたが、別にメモリなんかたいしたことないって? で?って思っちゃダメ。
ていうか、みんなガシガシアセット使ってるよね。
PICOLのこだわり。チュートリアルページ
PICOLというアプリのiOSアプリを担当させていただいて作りました。
PICOLは、バーコードを使って査定する買い取りアプリなのですが、もともとのコンセプトが
「ゲームっぽさ」
を目指しているのもあり、よくある標準UIKitのタブビューやナビゲーションバーを使わないデザインで画面デザインが上がってきました。
PICOLのホーム画面
チュートリアルもHOME画面ににたデザインで仕上がってきたので、作ったチュートリアルはこんなかんじ。
— なおち (@naochi___) 2018年6月18日
どこがこだわりなんだ。普通じゃない?ものすごく普通じゃない?って思ったアナタ。
普通はこっちです。
— なおち (@naochi___) 2018年6月18日
ね。こっちでも全然大丈夫じゃないですか。チュートリアルなんか1回しか見ないものだし、そこに力入れるのも。。って感じもするし。
でも、やだったんです。ページコントロール(画面下のページ番号が点々ででてるやつ)が一緒に横にスクロールしちゃったりするの。
そうなると画面の上下の赤い雲もヘッダとフッタな訳で。これも動いちゃやだなって思いました。ああ、そうなると黒いページ番号も固定したいじゃないかーって、さらに思ってしまいました。
社長がイケメンだしチュートリアルから気持ちいいものを使ってもらいたいですしね。
では、どうやって実装したのか図解します。
まず、チュートリアルは横スワイプでページを切り替えるだけの機能なのでUIPageViewControllerを使用しました。UIScrollViewかどちらかなのかなって思います。
横にスワイプして表示されるチュートリアルのそれぞれのページは、UIPageViewControllerDelegateのメソッドで、今表示するページを指定すて返すような仕組みになっています。表示はこれにお任せです。
func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? { } func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? { }
そして、ヘッダ、フッタと、ページコントロールを持つ、固定したいビューをUIViewとして別のnibに作り初期化したら最初のUIPageViewControllerのviewDidLoadで
self.view.addSubview(tutorialHeaderView)
とやるだけ。 そうすると固定されたヘッダビューの出来上がりとなります。
]
ただし。これだけだと上にヘッダのビューを重ねているので、ページをスワイプ するときのタッチイベントがヘッダビューに取られてしまうので、ヘッダビューのクラスに hittestをつけて、nibからイベントを下のビューに逃したいViewに適当なtagをつけておきます。 そうするとスワイプ のイベントも効くようになります。
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { let view = super.hitTest(point, with: event) //真ん中のviewに触られたら下のviewにタッチイベントを透過したい if view?.tag == 111 { return nil } return view }
サンプルコード、時間できたらあげると思う。