2017年12月4日月曜日

2017年のしめくくり

pyspa Advent Calendar 2017 12/4です。

どうもお久しぶりです。オンライン上では一切活動しておりませんが、おかげさまで大変元気にやってます。というわけで2017年のまとめを書いておこうと思います。

そもそもなんで最近ブログ書いてないの

書くことがないからです。というのも私は最近iOS/Androidの開発ではなくジャバでサーバサイドアプリケーションを書くみたいな仕事をしていて、別にSpring Bootの話なんかここに書いてもあまり面白くもなければ、すでにありとあらゆる先人が地雷を踏み尽くしていて調べればだいたいなんでもわからないことは分かる状態になっているので、わざわざ私が書く必要もないと思われるためです。

なんでiOS/Androidやってないの

端的に言えば愛想が尽きました。iPhone Xは買っていません。SwiftとKotlinは大好きで今でも仕事で使ったり他人のコードをレビューしたりする毎日ですが、もはやiOSもAndroidもどちらも執着するには値しません。

なんか技術的に新しいネタないの

正直今ネイティブアプリに大きな流れはないので(ARとクライアントサイド機械学習程度しか新規性のあるネタがなく、どちらも適用分野が限られ、ほとんどのアプリケーションでは縁がない)、後数年の間はモバイルネイティブアプリ一択だった流れが半分ぐらいウェブに返ってくると思っていて、ゲームなどではすでにその兆候もありますし、React NativeとかモバイルフロントエンドJavaScriptとかを見ておけばいいんじゃないのかなぁと思っています。個人的にはあんまりどちらも好きじゃないんですが、悪いものではないと思います。
また、Rxはもはや使えて当然と思っていますが、しかしながらRxを限界まで酷使するようなアプリはごく少数の人間しかメンテできなくなり、そのうちメンテがおぼつかなくなり、Rxのシグナルやオブザーバを使うのではなく昔ながらのdelegateやcallbackで手っ取り早くコードを修正するような事態が訪れ、ゆっくりと破綻していくような事態が周囲で散見されています。正直人間のほうが技術側に追いついていないというか。使えるやつは使えるんだけどそれじゃメンテ回らないよというか。

なんか注目のビジネスネタないの

フィンテックはガチだと思います。儲かるので。ただしビットコイン、というかブロックチェーン技術を神聖視するのは正直イケてないと思います。
動画配信系もガチだと思います。これもカネの匂いがします。ただしこっちはそもそもプレイヤーになれる存在が非常に限られており、ドワンゴすらそろそろもうついていけないかなぁという状態ですので、皆様がんばってくださいという感じです。
AIが〜とか機械学習が〜とかはやらなくてはならないのは間違いありませんが、ぶっちゃけ現状ではまだそんな言うほど大したことないです。どっかのタイミングで私みたいな何もわからないド素人でも簡単に放置してデータ食わせるだけで勝手に最良の状態に学習する事ができるような仕組みが誕生すると思っていて、そうなったら本当に爆発的に世界が変わる気がしています。それまでは自動運転みたいなカネが突っ込まれまくる分野でのみ世界が変わるほどの進展が見られるんじゃないかと勝手に思っています。
スマートスピーカーは正直使ってみて要らないと思います。不便とは思いませんが、現状無くてもほとんど全く困らないので要らないです。無いと困るようになったら爆発的に売れるのではないでしょうか。

今年のゲームはどうだったの

ゼルダがGotY 2017なのは間違いありませんが、その他も非常に豊作な一年だったと思います。個人的な推しはtheHunter: Call of the Wildです。

家どうなったの

友人が家を探しているというので色々物件を紹介していたら、まさかの我がマンションに引っ越してきました。おかげさまで大変楽しい毎日です。近所付き合いってのも悪いもんじゃないと思います。
あとはベッドを買い替えました。こちらもiPhone Xを買うより100倍ぐらい幸福になりました。良いお金の使い方をしたと思います。

婚活どうなったの

退会しました。1年間、のべ数百人単位の女性を紹介してもらって、数十人単位と付き合って、数人とじっくり時間を過ごしてみた得た結論は、正直私は女性と付き合っても煩わしいばかりで楽しくないし、相手も私といても満足できないし、結婚してもいいことはないというものでしたので、そのようにしようと思います。1年前はちょっと焦りとかもありましたが、今では大変気分も落ち着いて平穏が得られました。自らの人生について考える良い機会だったと思います。

今どうなの

おかげさまで非常に幸福な人生を送っていると思います \(^o^)/
来年もいい年になりますように!

2017年6月19日月曜日

UIWebView, WKWebView 等において Drag and Drop を禁止する方法


iOS 11よりDrag and Drop APIがUIKitに追加され、UITextView / UITableView / UICollectionViewに簡単にDrag and Dropを実現するためのdelegateが用意されたり、それ以外のUIViewにもDrag and Dropをハンドリングするための仕組みが用意されました。このDrag and Dropは基本的にはアプリをまたいでデータを受け渡しすることを前提として作られていますが、一応は自分のアプリ内でデータを移動したりコピーしたりするためにも使えるようになっているので、積極的に活用していきたいところです。

ところでこのDrag and Drop、基本的にはアプリ側が対応しない限り自動的には対応してくれないのですが、例外があります。それがUIWebView / WKWebView / SFSafariViewController要するにWebViewのたぐいの皆様です。これらについては最初からDragも可能ですしDropも可能なように作られているようです。便利ですね!

ですがここをご覧になっている皆様の中には、大人の事情によりどうしてもそのような便利な機能をユーザーさんにわざと提供したくないと言うケースがある方もいらっしゃるかと思います。そこでUIWebView / WKWebViewのデフォルトのDrag and Dropを無効化したり、頑張って自分のコードでハンドリングしたりすることができるようにする方法をご紹介します。なおSFSafariViewControllerに対しては手も足も出ないのでご了承ください。
if #available(iOS 11.0, *) { 
    webView.scrollView.subviews.first?.interactions = []
}
たったのこれだけです。簡単ですね!

なんのこっちゃなので詳しく説明します。

まずはじめにiOS 11よりUIView.interactionsプロパティが追加になっています。このinteractionsは[UIInteraction]型となっていて、公式のドキュメントによると「ジェスチャベースの挙動をビューに定義する」ことができるらしいです。まさにドラッグ・アンド・ドロップ的なやつですね。
で、Appleによって最初からドラッグ・アンド・ドロップ用のUIInteractionすなわちUIDragInteractionとUIDropInteractionが用意されています。これらのInteractionは内部的にdelegateを持っていて、これらのInteractionがインタラクションを検知したらdelegateにメッセージングを行い、そいつらが実際のドラッグ・アンド・ドロップ処理を行うというような仕組みになっているようです。
調べてみたところUITableViewのような最初からDrag and Drop用のdelegateを追加されているViewについても、このinteractionsに何らかのUIDragInteraction / UIDropInteractionが追加されていて、それが一旦イベントを拾ってから我々が定義したUITableViewDragDelegate / UITableViewDropDelegateに問い合わせを行うという仕組みで動作しているということがわかりました。

ここまで分かれば後は簡単で、UIWebView / WKWebViewも同様にinteractionsで一旦イベントを受け取った後に、内部的に処理を行っていると考えられるのでUIWebViewの中身をどったんばったん探してみたところwebview.scrollView.subviews.firstの中にinteractionsが刺さっているのを発見したと言う次第です。

先程の無効化する例では空配列を渡すことで完全にinteractionsを無効化していますが、ここで自分が作ったカスタムのUIDragInteraction / UIDropInteractionを渡してやればうまい具合に自分でWebViewのDrag and Drop挙動をカスタマイズすることができるかもしれません。また両方を消すのでなくて片方だけを消せばDragによってデータを持ち出すのは禁止するがDropによってデータを持ち込むのは許可するみたいなこともできます。

2017年5月24日水曜日

Xcode 8.3 & Swift 3.1 環境で LLDB を使う Tips

以前も似たような記事書いた気がするんですが、定期的にSwiftのコード上でLLDBを使ってて困ることがあるので定期的に書くことにします。ほとんど https://stackoverflow.com/questions/29441418/lldb-swift-casting-raw-address-into-usable-type からの転載です。

LLDBの言語を指定して実行する

標準ではbreakpointを挟んでbreakした行がSwiftであればSwift、Objective-CであればObjective-CでLLDBの言語が選択されるようなのですが、これでは毎回毎回breakした地点に応じてデバッグの仕方が変わって大変で仕方がないので、Swiftに統一してしまうのが良いと思っています。
expr -l swift -- {Swift Command}
e -l swift -- {Swift Command}
とすると常にSwiftでLLDBに対してコマンドを送る事が可能になります。逆にObjective-C側に寄せたい場合は
expr -l objc++ -O -- {Objective-C Command}
e -l objc++ -O -- {Objective-C Command}
とすれば良いです。-Oオプションがないと結果がいい感じにObjective-Cのオブジェクトとして扱われないので注意。

importを忘れずに

以前も書いた気がしますがLLDBでbreakした環境はそのままだとFoundation.frameworkすらまともに使えない(シンボルを認識しない)状態なので、importを忘れずにしましょう。
e -l swift -- import UIKit
e -l swift -- import MyApp
e -l swift -- import MyAppLib
という具合でしょうか。他必要なものがあればその都度。注意点として自分のアプリ自体に含まれるシンボル、例えばAppDelegateだとかViewControllerのたぐいだとかもimportしないと触れないので忘れないように。

unsafeBitCast(_:to:)を使ってポインタからオブジェクトを取得する

これも以前書いた気がしますが、Swift 3でまたシグネチャが変わったらしいので念のため。
e -l swift -- let $obj = unsafeBitCast(0x00000000, to: MyClass.self)
e -l swift -- print($obj)
とかやるといい具合になります。

エイリアスを作ろう

毎回e -l swift -- とかタイプするの面倒ですし、poがe -O -- のエイリアスなのと同様に、eswiftとかpswiftとかそういうエイリアスを作ってしまえばいいと思います。~/.lldbinitの中に、
command alias eswift e -l swift -- 
とか書いておけば良い気がします。

2017年3月7日火曜日

NSAttributedString に lineSpace を付与したとき、1行であるにも関わらず lineSpace が下端に付与されて困っている人はこれを読んだら直ります



突然ですが皆さん、NSAttributedStringには2017/03/07付のiOS 10.3現在でも致命的にバグっている箇所があります。

症状:

  • NSAttributedStringにNSParagraphStyleAttributeNameを利用してNSParagraphStyle経由でlineSpaceが付与されている。
  • NSAttributedStringに複数の「区間」が存在する。例えばNSAttributedString全体が単一のAttributeによって構成されているときはこの問題は発生しません。2つ以上の異なるAttributeの区間が必要です。
  • NSAttributedStringを描画するときに、横幅及び文字列の長さの都合で、1行で描画される。複数行になるときにはこの問題は発生しません。
  • NSAttributedStringにNSBackgroundColorAttributeNameが付与されていない。または、NSBackgroundColorAttributeNameとNSKerningAttributeNameの両方が付与されている。

上記の条件を全て満たすとき、
本来、1行の文字列はレンダリングしたときに下端にlineSpaceが付与されてはいけませんが、この条件が満たされていると下端にlineSpaceが付与されてしまいます。先頭の画像の左上のケースがこの問題に相当します。

具体的な問題としては、
  • 問題が発生するNSAttributedStringを使用したUILabel, UITextField, UITextViewの描画内容が思いっきり上にずれます(下に無駄なスペースが発生するので)
  • NSAttributedStringの地点で壊れているので、Core Textを利用したり、boundingRect(with size: CGSize, options: NSStringDrawingOptions = [], context: NSStringDrawingContext?) -> CGRectを使っても一切回避できません

解決策: 

この問題に対する正しいワークアラウンドの方法は一つしかありません。

NSAttributedStringの全Attribute区間にNSBackgroundColorAttributeNameを付与し、かつどの区間にもNSKerningAttributeNameを付与しない。

背景色が不要の場合はUIColor.clearでも付与してごまかしてください。
以上です。よろしくお願いします。

参考: