iOS/iPhone/iPad/watchOS/tvOS/MacOSX/Android プログラミング, Objective-C, Cocoa, Swiftなど
『LiVE for WebLiFE*』という、jQueryベースのウェブ製作ソフトを利用する機会が出来たので、それの導入作業について書いてみた。
これは、手に取った人しか体験できないのだが、驚いたのは凝ったパッケージ。App Storeに代表されるように、これからはアプリケーションはダウンロードでという流れになってゆくと思うが、だからこそ、パッケージを手にする楽しみを与えてくれるのは、単純に嬉しい。
インストール自体はとても簡単だ。CD-ROMにある説明文を読んで、インストーラを起動し、ユーザー登録をすれば完了する。
使い方だが、アプリケーションを開くと、動画での説明がある。ただ、あえて、いきなり新規サイトを選択してみた。
とりあえず、名刺サイトの雛形を選択して、ftpでアクセスできるWebサーバに公開してみた。
何となく感じはつかんだので、説明動画を見て、複雑なものに挑戦してみたいと思う。
ついに、iPad(iPad mini)を手に入れた。なので、急にiPadについて興味が湧いてきた。そこの、この地図アプリをiPadに対応させる事にする。
まずは、既存のStoryboard書類名をiPhoneだと分かるようにする。
Xcodeで「MainStoryboard.storyboard」を選択して、「MainStoryboard_iPhon.storyboard」に変更する。
TARGETSでアプリケーションを選択して、DevicesをUniversalに変更し、iPhone/iPod touch用Storyboard名を先ほど名前を変更したファイル名に変更する。
次に、iPad用StoryboardをiPhone用と同じ内容で作成し、iPhone用と同様に設定する。
同じ内容というのは、コントロールの配置もそうだし、アウトレットやアクション、Segueということだ。
その際、重要な事だが、まだ、Bing Maps Control for iOSは、iOS6のAutolayoutに対応していないようで、これをOffにしておかないと、クラッシュする。
設定画面のPageCurlにも対応。
ソードに手を加えなくても、StoryboardだけでiPad対応できるというところがnib/storyboardの素晴らしいところ!?
今回は自分自身の為の備忘録となってしまい申し訳ない。Blocksを使った並列処理だ。
まず、管理するキューを用意する。
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
キューの本数を一本にして、直列に実行したい場合は、プロパティmaxConcurrentOperationCountを1に設定すればいいと思う。
queue.maxConcurrentOperationCount = 1;
オペレーションを生成し、ブロックを設定する。
NSBlockOperation *operation = [[NSBlockOperation alloc] init]; [operation addExecutionBlock:^ { /* 処理1 */ }];
オペレーションをキューに追加する。
[queue addOperation:operation];
iPad版を作成するとビルド時に警告が表示されていた。PageCurlがNGということだ。
画面遷移としてPageCurlが指定できないと考えて、対応を先送りにしていたが、腹を括って対応してみるとなんて事はない。ツールバーのボタンのアイコンとしてPageCurlが指定できないだけだ。iPadでは設定のアイコンにした。
今回は、自分の為の備忘録となってしまいました。
ざまざまな事情から、Store申請関連の情報は少ない。
そんな状況のなかで役立った事を記述する。
証明書関連について、少しややこしく感じてしまうと思うが、公式な情報はここにまとめられている。
Technical Note TN2250iOS Code Signing Troubleshooting
Store申請関連の日本語の書籍を見つけた。
iOSプログラミング ビルド&リリース
ただし、電子書籍のみなので、自分はTIPSを本に書き込むのが癖なので、それが出来ないのが残念だ。
生成したバイナリが意図した構成になっていないことで苦労した事がある。その場合は、AdHoc用のアーカイブを保存し、サフィックスを.zipに変更して解凍し、codesignコマンドで内容を確認した。
% codesign -d オプション - Payload/アプリ名.app
自分自身への冬休みの宿題として、Core Animationを課題としてあげる事にした。
宿題に取り組む前は、上記教科書の章立てについて疑問があったが、よくよく、読んでみると、関係性に気がついたので、それを説明してみることにした。
OS X限定となってしまうが、AppKitには、NSAnimatablePropertyContainerプトロコルを利用した、UIのアニメーション機能がある。このプロトコルの定義は以下のとおり。
使用方法は、以下のとおり。
マウス・クリックされると画像の位置が変化するプログラムの例だ。
@interface BaseView ()
@property (strong, nonatomic) NSImageView *mover;
@property (assign, nonatomic) NSRect leftFramePosiotion;
@property (assign, nonatomic) NSRect rightFramePosiotion;
@property (assign, nonatomic) BOOL isRight;
- (void)initializeFramePositions;
- (void)addImageToSubview;
- (void)move;
@end
@implementation BaseView
@synthesize mover = _mover;
@synthesize leftFramePosiotion = _leftFramePosiotion;
@synthesize rightFramePosiotion = _rightFramePosiotion;
@synthesize isRight = _isRight;
- (id)initWithFrame:(NSRect)frame
{
self = [super initWithFrame:frame];
if (self) {
[self initializeFramePositions];
[self addImageToSubview];
[self addSubview:self.mover];
}
return self;
}
- (id)initWithCoder:(NSCoder *)aDecoder
{
self = [super initWithCoder:aDecoder];
if (self) {
[self initializeFramePositions];
[self addImageToSubview];
[self addSubview:self.mover];
}
return self;
}
- (void)drawRect:(NSRect)dirtyRect
{
}
- (BOOL)acceptsFirstResponder
{
return YES;
}
- (void)mouseDown:(NSEvent*)theEvent
{
[self move];
}
- (void)initializeFramePositions
{
CGFloat frameX = NSWidth(self.frame);
CGFloat frameY = NSHeight(self.frame);
self.leftFramePosiotion = NSMakeRect(0.0, 0.0, frameX / 4.0, frameY / 4.0);
self.rightFramePosiotion = NSMakeRect(7.0 * frameX / 8.0, 7.0 * frameY / 16.0, frameX / 8.0, frameY / 8.0);
self.mover = [[NSImageView alloc] initWithFrame:self.leftFramePosiotion];
self.isRight = NO;
}
- (void)addImageToSubview
{
[self.mover setImageScaling:NSScaleToFit];
[self.mover setImage:[NSImage imageNamed:@"snapshot.jpg"]];
}
- (void)move
{
if (self.isRight) {
[self.mover setFrame:self.leftFramePosiotion];
}
else {
[self.mover setFrame:self.rightFramePosiotion];
}
self.isRight = !self.isRight;
}
@end
setFrame:メソッドで画像の位置が変わるのだが、これにAnimatorプロキシを加えると、アニメーションするようになる。変更するのは、moveメソッドだ。
- (void)move
{
if (self.isRight) {
[[self.mover animator] setFrame:self.leftFramePosiotion];
}
else {
[[self.mover animator] setFrame:self.rightFramePosiotion];
}
self.isRight = !self.isRight;
}
何が発生したのか説明すると、setFrame:でプロパティに対して変更が発生すると、それを契機にアニメーションしだす。プロパティ名をキーにしてanimationForKey:で利用するアニメーションを取得する。キーに対応した値は、valueForKey:で取得する。キーに対応するアニメーションが見つからない場合で、defaultAnimationForKey:でデフォルトのアニメーションを取得する。それでも見つからない場合はアニメーションしない。
次回からは、このとおりに動作していることを、要素毎に試行錯誤して確認してみようと思っている。
NSAnimatablePropertyContainerプトロコルの続きだ。
分かりやすくする為に、単純な浮動小数点の値をアニメーションの対象にしたいと思う。alphaValueだ。
moveメソッドを以下に変更する。
- (void)move
{
if (self.isRight) {
[[self.mover animator] setAlphaValue:1.0];
}
else {
[[self.mover animator] setAlphaValue:0.0];
}
self.isRight = !self.isRight;
}
マウスダウンすると、徐々に透明になり、その次は徐々に表示されると思う。
このアニメーションを違いが分かるように、キー・フレームのアニメーションに置換してみよう。
まずは、QuartzCore/QuartzCore.hをimportし、QuartzCore.frameworkをLinkに加える。
キー・フレームのアニメーションは、以下のとおり。
- (CAKeyFrameAnimation *)opacityAnimation
{
CAKeyframeAnimation *animation = [CAKeyframeAnimation animation];
animation.values = [NSArray arrayWithObjects:
[NSNumber numberWithFloat:0.0],
[NSNumber numberWithFloat:0.75],
[NSNumber numberWithFloat:0.0], nil];
animation.keyTimes = [NSArray arrayWithObjects:
[NSNumber numberWithFloat:0.25],
[NSNumber numberWithFloat:0.50],
[NSNumber numberWithFloat:0.75], nil];
return animation;
}
これを対象のNSImageViewに設定する。
self.mover.animations = [NSDictionary dictionaryWithObjectsAndKeys:[self opacityAnimation], @"alphaValue", nil];
プロパティalphaValueを設定すると、これがキーになっているopacityAnimationが取得され、適用される。opacityAnimationは値を0.0、0.75、0.0と変化させるアニメーションなので、点滅するようになったと思う。
この状態で、moveメソッドを前回の内容に戻すとどうなるだろうか?
- (void)move
{
if (self.isRight) {
[[self.mover animator] setFrame:self.leftFramePosiotion];
}
else {
[[self.mover animator] setFrame:self.rightFramePosiotion];
}
self.isRight = !self.isRight;
}
animationsの内容をopacityAnimationのみにしたので、frameで検索されるアニメーションが見つからないと思ったのだが、default animationに存在するようで、やっぱりアニメーションした。
インタフェースから実装は予想できるが、やはり、確認したいので、アニメーションさせているNSImageViewのサブクラスを作成して、プロキシのメソッドが、どのように呼ばれているのか探っていみることにした。
以下が調査用のサブクラス。
#import <Cocoa/Cocoa.h>
@interface MyImageView : NSImageView
@end
#import "MyImageView.h"
@interface MyImageView ()
@end
@implementation MyImageView
- (id)initWithFrame:(NSRect)frame
{
self = [super initWithFrame:frame];
if (self) {
}
return self;
}
+ (id)defaultAnimationForKey:(NSString *)key
{
DBGMSG(@"%s, key:%@", __func__, key);
id result = [super defaultAnimationForKey:key];
DBGMSG(@"%s, animation:%@", __func__, result);
return result;
}
- (id)animationForKey:(NSString *)key
{
DBGMSG(@"%s, key:%@", __func__, key);
id result = [super animationForKey:key];
DBGMSG(@"%s, animation:%@", __func__, result);
return result;
}
@end
そして、NSImageViewでインスタンスを生成していたところをMyImageViewに変更する。
self.mover = [[MyImageView alloc] initWithFrame:self.leftFramePosiotion];
以下がデバッグ出力。
2012-12-31 15:12:00.639 CocoaAnimation[2189:403] -[MyImageView animationForKey:], key:frameSize
2012-12-31 15:12:00.642 CocoaAnimation[2189:403] +[MyImageView defaultAnimationForKey:], key:frameSize
2012-12-31 15:12:00.644 CocoaAnimation[2189:403] +[MyImageView defaultAnimationForKey:], animation:
2012-12-31 15:12:00.645 CocoaAnimation[2189:403] -[MyImageView animationForKey:], animation:
2012-12-31 15:12:00.647 CocoaAnimation[2189:403] -[MyImageView animationForKey:], key:frameOrigin
2012-12-31 15:12:00.648 CocoaAnimation[2189:403] +[MyImageView defaultAnimationForKey:], key:frameOrigin
2012-12-31 15:12:00.649 CocoaAnimation[2189:403] +[MyImageView defaultAnimationForKey:], animation:
2012-12-31 15:12:00.650 CocoaAnimation[2189:403] -[MyImageView animationForKey:], animation:
-animationForKey:で検索したが見つからず、+defaultAnimationForKey:で検索して見つかったのが分かると思う。
これを独自に追加したアニメーションの場合だとどうなるだろうか?BaseViewの-moveで、フレームを設定しているのを透明度に変更。
2012-12-31 15:20:36.110 CocoaAnimation[2215:403] -[MyImageView animationForKey:], key:alphaValue
2012-12-31 15:20:36.114 CocoaAnimation[2215:403] -[MyImageView animationForKey:], animation:
独自に追加したアニメーションが見つかっている。