頭と尻尾はくれてやる!

パソコンおやじのiPhoneアプリ・サイト作成・運営日記


Objective-CのプロジェクトからSwiftのクラスを使う

えらい苦労したよ。
タイトル通り、Objective-CのプロジェクトからSwiftのクラスを使いたかったんだわ。
手順で言うとSwiftのクラスをどこぞからドラッグ&ドロップするなり、New Fileで作成でもいいんだけど、Swiftのファイルを生成するじゃない。
するとXcodeが

Xcodeの質問画面

って聞いてくるけど、Objective-CからSwiftクラスを使うのなら関係ない(はず)。これは逆にSwiftからObjective-Cのクラスを使う時に使うファイル。だからNOを選択しても問題ないと思う。

やり方を調べると
#import “プロジェクト名-Swift.h”
でOK、とかあるんだけど、、、まずこれがはまったのよ。

Xcodeが自動的に作ってくれるよってどこ見ても書いてるけど、ウチのXcodeはぐーたらなのかやる気がないのかそんなファイル作ってくれなかったんだ。
しょうがないから自分でヘッダファイル作成してみたりしたんだけどうまくいかない。

ようやくたどり着いたのが、作ってるけど表示されないってこと。
ニャンですとー?!逆の場合(SwiftからObjCクラスを使う場合)だとプロジェクト名-Bridging-Header.hってファイルがちゃんとXcodeに出てたので勘違いしてたんだわ。

ってことで、
プロジェクト名-Swift.hってファイルはXcodeには表示されません。

ちなみに、ちゃんとファイルができていなければimport文でXcodeがそんなファイルねえよ!って教えてくれるはず。何も言わなければimportはできているんだろう。あと、
#import “プロジェクト名-Swift.h”
のファイル名上でcommand+クリックで中身を確認できる(何書いてるか理解できんけど)。


ところが、まだうまくいかなかったんだ、俺の場合。
Objective-CのSwiftのクラスからオブジェクトを作成する
MySwiftClass *object = [MySwiftClass new];
という部分で、
Use of undeclared identifier ‘MySwiftClass’
というエラーが出るのよ。

MySwiftClassはNSObjectを継承してるし、念のため@objcも書いたりしてるのにやっぱりだめ、、、
Swiftのプロジェクトからはちゃんと使えるクラス、という確認もしてたのに。

ようやく見つけたのがこのページ。
ios - Including Swift files in Objective-C - Stack Overflow

Swiftのクラス宣言の前に’public’を付けよ
という内容。
これでやっと成功。長かったな〜


SwiftのExtensionでプロパティを追加

Objective-CでUIViewオブジェクトのwidthを得るカテゴリを使ってるのよ。実装部分はこんな感じ。
@implementation UIView (AddWidth)
-(float)width
{
    return self.frame.size.width;
}
@end
こうしておけば、widthを得るのに myView.width ですむもんね。
同じようにSwiftでもカテゴリ作ろうかと思ったらどうやらSwiftではExtensionで作成すればいいって聞いたのでそれで作ってみたのよ。
extension UIView {
    func width() -> CGFloat {
        return self.frame.size.width
    }
}
使う時にはこんな感じに、、、
myView.width()
む?最後のカッコが邪魔だ、すっきりしない!

よくよく調べるとこういう書き方もできるそうな。
extension UIView {
    var width: CGFloat {
        return self.frame.size.width
    }
}
最初のと違ってメソッドじゃなくてプロパティを書いてるみたいな感じだな。
これだと使う時には
myView.width
でカッコ付けずに済むのでスッキリ。



Swiftのif letが気持ち悪かったけど

Swiftで最初に if let 〜 って続き方を見た時にすごく違和感があったんだけど、最近ようやくわかってきた(気がする)。

例えば画像ファイルの読み込みを例にするよ。
よくやるのはファイル名からUIImageオブジェクト作ってそこからUIImageViewオブジェクトを作って画像を表示、なんて処理。
下はUIImageオブジェクトを作る部分ね。

(1)
let image = UIImage(named:”hoge.png”)
//処理


(2)
if let image = UIImage(named:”hoge.png”) {
//処理
}

named: ってメソッドは指定する画像ファイルがなかったらnilを返してくるのよ。そりゃそうよね、文字列を間違えることだってあるだろうから、このメソッドがnilを返しうるってのは納得できる(というかリファレンスにもはっきり書いてるし)。

上の(1)の場合だと、imageの型はUIImageではなく、UIImage?になるのよ。
UIImage?だとUIImageクラスのプロパティやメソッドにアクセスできないんだ。やろうとすると書いてる時にXcodeに怒られる(コンパイルエラー)。
エラーメッセージ

ところが(2)のようにif let 〜 みたいに書くと、imageの型はUIImageになってくれるんだわ!
これだとimageに!とか付けなくてもアクセスできる。


この辺りのSwiftの型推論の挙動を知ってからは if let 〜 って書き方がスッキリして見えるから不思議だわ。


もっともこういうとこで引っかかったのは俺がObjective-Cでnilチェックをちゃんとしてこなかったからだな。Appleのサンプルコードなどを見てるとホント実感するよ。悔い改めます。


GarageBandで遊んでみる

アプリの効果音とかを作ることができるのかな?と思ってMacに入ってるGarageBandを使ってみようと思ったのよ。
ところがMacにあるGarageBandを起動しようとするとパスが存在しないとかなんとか。

GarageBand起動時のエラー

んんん?HDD容量がかつかつの時に消しちゃったんだろうか?

まあいいや、再インストールするかと思ってMac AppStoreを見ると、、、

GarageBandアイコン

あら?500円するの?無料なのかと思ってた。
iOS版もあるんだよなと思ってiPadで確認すると、これも500円とのこと。
iPhone 6で見ると無料だったのでダウンロードするとiPadでも無料でダウンロードできた。
新しいMacやiOSデバイスには同梱されてるってパターンなのかな。

そんなわけで少し触ってみたんだけど、音楽や楽器の知識がゼロの俺にはなかなか難解だよ。まずはiOS版GarageBandのいいチュートリアルサイトを探さねば。


法線と光源の内積でテクスチャの明るさを決める

頭と尻尾はくれてやる! / Metalでテクスチャを貼る
↑この続きなんだけど、なんだか描画されたオブジェクトが暗いのよ。
シェーダ内でテクスチャに対して面の法線と光源の内積をかける、というよくある処理してるんだけど、正面から光をあてたりしても妙に暗い。
法線うんぬんの処理をせずにテクスチャ画像そのままで表示させるとちゃんとテクスチャ画像を拾ってるのは確認できるので、どうも法線関連部分がおかしいみたい。

参考にした”MetalBasic3D"ってAppleのサンプルコードでは次のようなコードで内積を求めている。
{
    float3 normal = normal_array[vid].position;//(1)
    float4 eye_normal = normalize(constants.normalMatrix * float4(normal, 0.0));//(2)
    float3 light_position = float3(1.0 , 1.0 , 1.0);//(3)
    float n_dot_l = dot(eye_normal.rgb, normalize(light_position));//(4)
}

(1)法線はfloat3でもらう。
(2)法線に一つの要素を追加しのと4x4のマトリックスとかけてる。
(3)float3の光源。
(4)(2)の結果の三要素(.rgb)と光源で内積を計算してる。

別にOpenGL ESで処理してた内容と変わらないよなあと思っていたんだけど、よく見るとこのコードではシェーダに4x4のマトリックスを渡してる。constants.normalMatrixってやつね。

OpenGL ESで使ってるコードを見るとこの部分、3x3のマトリックスを使ってるんだわ。

別に4x4で処理してる内容見ても問題ない(左上の3x3しか使っていない)ように見えるけど、他に思い付かないからとりあえずOpenGL ESと同じように3x3のマトリックスを渡してみると、、、

修正後のロボット

↑あら?意図通りになってる。謎だ、、、と思ったんだけど、今気が付いた。
(2)の部分で4つの要素で正規化(normalize)してるじゃねーかっ!4つめの要素がゼロとは限らないし。
{
    float4 raw_eye_normal = constants.normalMatrix * float4(normal, 0.0);
    float3 eye_normal = normalize(row_eye_normal.rgb);
}
確認のため三要素で正規化してやったらちゃんと意図通り表示されたよ!

どっちがいいのか知らないけどシェーダに無駄なデータを渡すのを避けて3x3要素を渡すことにするわ。




Copyright ©頭と尻尾はくれてやる!. Powered by FC2 Blog. Template by eriraha.

FC2Ad