2017年8月9日水曜日

変なメッセージ/dyld: Library not loaded:

昨日、Macの不調からかキーチェンアクセス中のiOS Develop証明書が消えてしまったらしく、頑張って再作成した。

その後、ちゃんと動作することを確認したプロジェクトが、今日になって以下のメッセージを出して落ちるようになった。

dyld: Library not loaded: @rpath/libswiftAVFoundation.dylib

ほんとはもっと長く続いてるんだけど、要するにライブラリがロードできないらしい。
ネットで調べてプロジェクトの設定をいじってみたけど直らない。一つだけじゃなく、他のいくつかのプロジェクトが同じ状態。

結局、プロジェクトをCleanしたらどうかという情報があったのでしてみたら、あっさり直ってくれた。

昨日証明書が消えた状態で起動して状態を見ていたプロジェクトだったから、そのせいだろうか。証明書再発行後にはちゃんと動くことを確認したんだけどな。
解せねえ…。

2017年8月8日火曜日

Revoke Certificate/証明書が無効に!?

晩飯食って戻ってきたらMacが再起動してて、OSのインストーラが立ち上がっていた。
なんのはずみか知らないが、前にも2度ほどあり、その都度もう一度再起動したら正常に起動してくれた。
ただし今回はTwitterのアカウントがKeyChainから消えてしまったらしく、再度入力する羽目に。ちょっと怖い。

そのままXcodeで作りかけのアプリをビルドしようとしたら、以下のメッセージが出た。

証明書を取り消すアカウントにはすでにこのマシンの署名証明書がありますが、キーチェーンには存在しません。 新しい証明書を作成するには、まず既存の証明書を取り消す必要があります。 
Cancelしても改善されないので、試しにRevoke(取り消し)を押してみたら、すぐにApple Developerから「証明書無効になったから再作成せなあかんで」とメールが来た。
どうもさっきのMacの再起動によってTwitterアカウントだけじゃなくてXcodeの情報もKeyChainから消えてしまったっぽい。

GeneralのStatusを見てもこのとおり。
Revokeボタンを押してみたらRepairとかなんとか出て処理してるので、これで直るのかと思ったがまた同じ状況に。

Apple DeveloperのiOS Certificatesでいくつかある証明書を確認。べつに有効期限も切れてないし、Invalidとかなってない。
一応そいつをDownloadして、クリックで勝手にキーチェーンアクセス.appが開くので、これで登録されるのかと思ったが、やっぱり状況変わらず。

ネットで調べると作り直す際はApple Developerの証明書をRevokeしてもいいみたいなので試してみたい。

キーチェーンアクセスの証明書で、有効期限が切れているとして❌印がついてる証明書を削除。
それでもだめなので、iPhone Developerと書かれてる証明書(有効期限内)がいくつもあったのを全部削除。しかし状況変わらず。
XcodeでRevokeし、Repair処理をするとまたその証明書が復活していたが、やはり状況変わらず。

残された手段はApple Developerの証明書をRevokeするのみ。
「証明書を取り消しちゃってええのんかぁ?」と赤いボタンのメッセージが出るけど、俺は男だ勇気を出した。

そしたら「この証明書は、『発行済み』状態ではないため、取り消すことはできません。」だって。ますますわからん。
試しにもう一度Revokeしてみたら、今度は消えちゃったよ。なんだよ、この挙動の不統一は!

今消したのはDevelopmentの証明書なので、そこから作り直せるだろう。
下の方にあるCertificate Signing Requestを押す。

一番上のiOS App Developmentを選べばいいんだと思う。

次の画面にはこんなことが書いてあった、英文で。
その通りにしてみる。
手動で証明書を生成するには、Macから証明書署名要求(CSR)ファイルが必要です。 CSRファイルを作成するには、以下の手順に従ってKeychain Accessを使用してCSRファイルを作成します。
CSRファイルを作成します。
Macの「アプリケーション」フォルダで、「ユーティリティ」フォルダを開き、「キーチェーンアクセス」を起動します。
[キーチェーンアクセス]メニュー> [証明書アシスタント]> [認証局に証明書を要求]を選択します。(そうすると証明書アシスタントが起動する)
[証明書情報]ウィンドウで、次の情報を入力します。
[ユーザ電子メールアドレス]フィールドに、電子メールアドレスを入力します。
[共通名]フィールドに、秘密鍵の名前(例:John Doe Dev Key)を作成します。
[CA電子メールアドレス]フィールドは空のままにしてください。
「要求」グループで、「ディスクに保存」オプションを選択します。
[Keychain Access内で続行]をクリックして、CSRの生成プロセスを完了します。

「続ける」と証明書要求のための「CertificateSigningRequest.certSigningRequest」ファイルの保存先を聞いてくるので、保存する。
それをApple Developerの方からアップロードする。


すぐに証明書ができるので、ダウンロードする。

ダウンロードしたios_development.cerをダブルクリックするとキーチェーンアクセスが起動して証明書に登録される。
しかし状況は同じまま!
最後の手段でMacを再起動したら直った!!

なんでこんなことになったんだろうね? Macが勝手にOSインストーラーが起動しちゃったくらいだから、その時に変なことなったんだろう。
んで、うちのMacとApple Developerをつないでる証明書が無効になっちゃったもんだから、作り直す羽目になったと。

もうひとつAppStoreで販売するためのDistributionの証明書ってのもあるけど、こっちも同じ状態になってるのかね? まあなんとなくやり方わかったんで、今度トラブったら同じように試してみる。

2017年8月2日水曜日

ARkitのクラス

iOS11から搭載されるARkitのクラスの情報の概要。
まあよくわからんのだけれども…。

拡張現実感(Augmented reality:AR)は、デバイスのカメラからのライブビューに2Dまたは3D要素を追加して、それらの要素を現実の世界に生息するように見せるユーザーエクスペリエンスを記述します。
ARKitは、デバイスのモーショントラッキング、カメラシーンキャプチャ、高度なシーン処理、および便利さの表示を組み合わせて、ARエクスペリエンスを構築する作業を簡素化します。

重要

ARKitには、A9以降のプロセッサを搭載したiOSデバイスが必要です。
あなたのアプリをARKitをサポートするデバイスでのみ利用できるようにするには、アプリのInfo.plistのUIRequiredDeviceCapabilitiesセクションにあるarkitキーを使用します。
拡張現実感がアプリの副機能である場合は、isSupported プロパティを使い、現在のデバイスが使用するセッション構成をサポートしているかどうかを判断します。

最初のステップ

ARSession class

拡張現実体験に必要なデバイスカメラとモーション処理を管理する共有オブジェクト。

構成(Configurations)

class ARSessionConfiguration

デバイスの向きのみを追跡する基本構成。
——デバイスの傾き(回転)に関してのみで、位置に関しての情報(デバイスを左に動かしたとかそういうこと)は扱わない。

class ARWorldTrackingSessionConfiguration

デバイスの向きと位置を追跡し、デバイスカメラが認識する実際の表面を検出する構成。
——デバイスの傾き(回転)と位置に関しての情報(デバイスを左に動かしたとかそういうこと)の双方を扱う。

標準ビュー(Standard Views)

Building a Basic AR Experience

ARセッションを設定し、SceneKitまたはSpriteKitを使用してARコンテンツを表示します。

class ARSCNView

3D SceneKitコンテンツを使用して、カメラビューを拡大するARエクスペリエンスを表示するためのビュー。

class ARSKView

2D SpriteKitコンテンツを使用して、カメラビューを拡大するARエクスペリエンスを表示するビュー。

カスタムビュー(Custom View)

Displaying an AR Experience with Metal

カメラ画像のレンダリングと位置追跡情報を使用して、オーバーレイコンテンツを表示することにより、カスタムARビューを構築します。

実世界の物体と位置(Real-World Objects and Positions)

class ARAnchor

ARシーンにオブジェクトを配置するために使用できる、実際の位置と方向。

class ARPlaneAnchor

ARセッションで検出された、実際の平面の位置と向きに関する情報。

class ARHitTestResult

ARセッションのデバイスカメラビュー内のポイントを調べることで見つかった、実際のサーフェスに関する情報。

カメラとシーンの詳細(Camera and Scene Details)

class ARFrame

ARセッションの一部としてキャプチャされた、ビデオ画像および位置追跡情報。

class ARCamera

ARセッションにおけるキャプチャされた、ビデオフレームのカメラ位置および撮像特性に関する情報。

class ARLightEstimate

ARセッション内のキャプチャされた、ビデオフレームに関連する推定シーン照明情報。

2017年7月13日木曜日

CollectionViewのCellの中身を更新

CollectionViewのセルをタッチしたら画像を変更するとか、タッチしたセルだけ選択された印のチェックマークをつけたりする方法。

まず、CollectionViewはTableView同様、最初に読み込まれるときにdelegateメソッドで一気に書かれるのが基本。

セルがタッチされたときに呼ばれる以下のdelegateメソッドに記述することで、特定のセルだけを内容変更することができる。
ただし、この中で直接セルの中の部品をいじってもダメなので、ここでは後述するdelegateメソッドの中で変更するように変更内容を入れる配列(ここではcellIsChecked)などをいじるだけとなる。

//セルが選ばれた時に呼ばれるdelegate
func collectionView(_ collectionView: UICollectionView, shouldSelectItemAt indexPath: IndexPath) -> Bool {
    //選ばれたセルのcheckBtnのチェックを反転
    if cellIsChecked[indexPath.row] {
        cellIsChecked[indexPath.row] = false
    } else {
        cellIsChecked[indexPath.row] = true
    }
    
    //セルの中身を更新したら、Cellを更新しないと反映されない
    collectionView.reloadItems(at: [indexPath])
    
    return true

}

セルの中身の変更はあくまでdelegateメソッド内で

collectionView.reloadItems(at: [indexPath])を実行することで、以下のdelegateメソッドが順番に呼ばれる。
つまり、あくまでも変更は以下のメソッド中で行うということになる。
ただし、最初に読み込まれる時と違い、下のメソッドはreloadItemsで指定したindexの分だけが呼ばれる。

//セクションの数を指定
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int)

//セルの中身を指定
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath)
{
    //選ばれたセルをセル番号で取り出す
    let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath) as! PhotoCollectionViewCell
    var title:String
    if cellIsChecked[indexPath.row] { title = "" } else { title = "" }
    cell.checkBtn.setTitle(title , for: .normal)
        
    return cell

}


collectionView.reloadItems(at: [indexPath])
のかわりに

collectionView.reloadData()
で全体を書き換えることもできるが、処理が重いのでオススメはできない。

2017年7月12日水曜日

Documentsフォルダ内の画像ファイル読み書き

アプリ内にあるDocumentsフォルダに対して、画像ファイルを読み書きする方法。

Swift3ではURL(Swift2.xまではたしかNSURL)型で保存先情報を持つのだが、そのURLをそのまま引数に書いてやってもうまくいかない。
myURLに入っているなら、myURL.pathというプロパティで指定してやる。
引数はString型のようなのでmyURL.descriptionとやってみたがダメだった。

実機で試した場合のpathとdescriptio

ファイルpath
 /var/mobile/Containers/Data/Application/47DCD124-F9A4-4953-9B81-696DA790CCED/Documents/20170711055328142.jpg

ファイルdescription
 file:///var/mobile/Containers/Data/Application/47DCD124-F9A4-4953-9B81-696DA790CCED/Documents/20170711055328142.jpg

頭のfile:///の有無だけのようだ。

書き込み

//Documentsディレクトリのpathを得る(返り値はArrayで、index0がそれ)
let docPath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0]
//StringappendingPathComponentがないのでURLに変換
let fileURL = URL(fileURLWithPath: docPath).appendingPathComponent(fileName)
//JPGに変換
let imageData = UIImageJPEGRepresentation(savePhoto, 1.0)
//画像書き込み(URLのpathを引数に)
//write(to:)はエラーを投げる関数なので、do-catch文が必要
do {
   //do-catchを使ってるので書込みエラーが起きるとcatchに移ってくれる
     try imageData?.write(to: fileURL, options: .atomic)
   //書き込み成功時の処理
   (省略)
   } catch let error {
   //書き込み失敗時の処理
     print("画像保存失敗 \(error)")
   }

読み込み

エラー処理なんかはしてない。

//Documentsディレクトリのpathを得る
let filePath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0]
//その後にファイル名を追加
//(String型のfileWithPathのメソッドがSwiftにないので、一度URL型に変更)
let fileURL = URL(fileURLWithPath: filePath).appendingPathComponent("ファイル名")
//.pathプロパティを引数に画像読み込み
let uiImage = UIImage(contentsOfFile: fileURL.path)

2017年6月7日水曜日

画面キャプチャ方法

//余計なUIをキャプチャしないように隠す
hogeButton.isHidden = true
hogeLabel.isHidden = true
CATransaction.flush() //画面更新
        
//viewと同じサイズのコンテキスト(オフスクリーンバッファ)を作成
let rect = self.view.bounds
UIGraphicsBeginImageContextWithOptions(rect.size, false, 0.0)
//スクリーンをコンテキストに描画 スクリーンキャプチャ
self.view.drawHierarchy(in: view.bounds, afterScreenUpdates: false)

//スクリーンがキャプチャされたコンテキストからUIImageを作成
let capturedImage : UIImage = UIGraphicsGetImageFromCurrentImageContext()!
//コンテキストの扱いを終了
UIGraphicsEndImageContext()
        
//隠したUIを表示させる
hogeButton.isHidden = false
hogeLabel.isHidden = false

アニメの途中が表示されない問題

Context(オフスクリーンバッファみたいなもん)を作ってそこに画像をdrawしたりrenderしたりしてたんだけど、UIViewアニメの途中をキャプろうとしても、なぜかアニメ中のオブジェクトが実際に表示されている位置でキャプチャされてくれない現象が。

詳しいことはわからんのだが、アニメのメソッドで指定してる最後の移動位置でキャプチャされちゃうみたい。
つまり、AからBの位置に5秒間で移動するアニメを実行中、3秒目でキャプチャしてもなぜかキャプチャ画像だけはBの位置に来てる。(画面上ではまだ途中を動いてるというのに)

renderやdrawじゃなくdrawHierarchy

それを回避するため、drawHierarchy()メソッドを使うのがいいようだ。
ただし、afterScreenUpdatesがtrueの場合は同じような問題が発生するので、falseにしなければいけない。

直前のhiddenが効かない新たな問題

でもfalseにすると、写したくないUI部品をキャプチャ動作の直前でhiddenしても、hiddenされなくて写り込んでしまう。
UI部品をhiddenした直後にsetNeedsDisplay()とかやってもダメ。これは画面更新の予約だけで、実際に更新されるのはOS次第なんだそうな。

よくわからんがflush()で解決

そこで調べたところ、部品をhiddenした直後に CATransaction.flush() をしたところうまくいった。
一瞬部品がhiddenされ、スクリーンキャプチャされた後にまた部品が表示される。
CATransactionはCALayerのアニメを使うためのクラスだそうで、flush()はRunLoop終わりで自動的に呼ばれるものを強制的に(?)実行させるものらしい。
flashじゃなくてflushな。顔を赤らめるとか、興奮、感情の芽生え、トイレの貯水槽とかの意味がある。なんだよそれ!?w
正直言ってUIViewのメソッドじゃないのにうまくいくのは納得いかないんだけど、結果オーライでいいや。

2017年5月18日木曜日

画像サイズ変更

画像サイズを縮小したり拡大したりする方法。とりあえず縮小で説明。

要は、UIGraphicsImageContext(オフスクリーンバッファだな)を縮小(拡大)したサイズで作り、そこに元の画像をdrawすればいい。

以下でimageには512*512の画像が入っているとする。それを256*256のContextにdrawすると、Contextの縦横に合わせて画像が縮小されるわけですな。


//オフスクリーンのコンテキスト作成
UIGraphicsBeginImageContextWithOptions(CGSize(width: 256, height: 256), false, 0.0) 
//コンテキストに画像を描画
image.draw(in: CGRect(x: 0, y: 0, width: 256, height: 256))
//コンテキストから画像を得る
myImageView.image = UIGraphicsGetImageFromCurrentImageContext()

UIGraphicsEndImageContext()


drawする際にCGRectの各値を変更すれば、描画位置を変えたり、縦横を縮小(拡大)して描画することができる。

単純な拡大も、例えば100*100の画像を256*256のコンテキストに描画すれば拡大されるっちゅうわけ。