iPhoneプログラミング・メモ
by K.I
2014/01/15 〜
Index
- iPhone(iPad)のプログラミングをやってみる。
- Xcodeの使い方が分からず、一度挫折したんだけど、再度挑戦!
- 開発環境は、Mac mini Mid2011版
- Processer: 2.3GHz Intel Core i5
- Memory: 4GB 1333MHz DDR3
- Graphics: Intel HD Graphics 3000 384MB
- Software: OS X 10.9.1
[top]
- 以前、何故挫折したかと言うと、Xcodeがバージョンによって全然使い方が変わっていて、
- それに、以前はマルチウィンドウだったのが、シングルウィンドウになって、
- 狭い画面では、物凄く使いにくくて、イライラして止めてしまった。
- それにしても、何で、開発環境までシングルウィンドウにしたんだろう?
- マルチウィンドウなら、ある程度狭い画面でも、直感的に複数のプログラムを編集できるし、
- 何より、Xcode以外のソフトを同時に使う場合に、Xcodeがデカすぎて邪魔!
- まず、Xcodeの使い方の概要を知っておく必要がある。
- これで、Xcodeの基本的な使い方が良く分かった。
[top]
- 最初に実行されるのは、Supporting Filesに入っている main.m
- UIApplicationMainが、iPhoneのユーザ・インターフェースを呼出すメインメソッド
- 実際に実行されるアプリケーションの動作を、AppDelegateに記述する
- applicationDidFinishLaunchingが起点になるが、このプログラムの場合はdidFinishLaunchingWithOptionsとなっている。
- コントロールの制御は、基本的にViewControllerに記述する
- 最初に実行されるのは、やっぱりmain関数。.mの拡張子はソースファイルということらしい。
- 単に、AppDelegateを実行しているだけ
#import <UIKit/UIKit.h>
#import "AppDelegate.h"
int main(int argc, char * argv[])
{
@autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}
- UIApplicationMainは、イベントループなのかな?
- mainから、AppDelegateが起動される
- AppDelegateは、基本的にはアプリケーションの状態遷移を記述するらしい。
- でも何もしていない。。
- 実際、Windowの表示ぐらしているはずなんだけど。。
- おそらく、ストーリーボードがそれをやっているんだろうけれど。。。
- ViewControllerは、コントロールの制御を記述するらしい。
- でも、ViewControllerは、全く呼び出された形跡がない。
- 多分、ストーリーボードによってWindowにViewが貼り付けられているんだろうと思うが。。。
#import <UIKit/UIKit.h>
@interface ViewController : UIViewController
@end
- ViewController.m
- draggingメソッドのみ記述している
- drag開始は、前回の位置に移動、、
- 移動時は、Imageの位置を追従させ、
- 最後は、位置をprevTranslationに保存する
#import "ViewController.h"
@interface ViewController ()
{
//移動ベクトルを保存
CGPoint prevTranslation;
}
//トンボのプロパティ宣言
@property (weak, nonatomic) IBOutlet UIImageView *tombo;
//パン(ドラッグ)アクションと接続するメソッドの宣言
- (IBAction)dragging:(UIPanGestureRecognizer *)sender;
@end
@implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
}
//ドラッグ中に連続して呼び出されるメソッド
- (IBAction)dragging:(UIPanGestureRecognizer *)sender {
//ドラッグ移動したベクトル
CGPoint translation = [sender translationInView:self.view];
if(sender.state == UIGestureRecognizerStateBegan){
//前回の続きから開始する
[sender setTranslation:prevTranslation inView:self.view];
} else if(sender.state == UIGestureRecognizerStateChanged){
//ドラッグに合わせて移動させる
_tombo.transform = CGAffineTransformMakeTranslation(translation.x, translation.y);
} else if(sender.state == UIGestureRecognizerStateEnded){
//ドラッグ操作終了時の移動ベクトルを保存する
prevTranslation = translation;
}
}
@end
- コントロールの記述自体は、分かりやすい。
- でも、ストーリーボードが間に入っているために、全体的に何やってるのか良くわからん。。。
- ここまでで分からないことが、いろいろある。とりあえずメモしておこう。
- プロトコルって?
- 定義するクラスのメソッドを、予め定義してあるメソッドを使って定義するってことなのかな?
- @propertyって?
- プロパティというのは、インスタンス変数のことかと思ったけど、そうじゃなくて
- インスタンス変数を読み書きするための、アクセッサメソッドの定義をコンパイラへ指示するためのものらしい
- senderって?
- この場合は、draggingメソッドに渡す引数のただの構造体へのポインタみたいなものに見える
- sender.stateってアクセスしてるけど、sender->stateのようにはしないのかな?
- superって
- スーパークラス(UIViewController)の同じ名前のメソッドをそのまま実行するってことかな?
- selfはその名の通り、自分自身ってことだからViewControllerを指すのかな
- _tomboっていきなり出てくるけど、何処で定義しているのか?
- こちらによると、アンダースコアを付けるとインスタンス変数に直接アクセス出来るらしい。
- self.tomboのようなアクセスは、@propertyによるアクセッサメソッドによる参照になるらしい。
- ドラッグによるアクションは、どうやって接続しているのか?
- Pan Gesture Recognizerをどうやって接続しているのかわからん。。。
[top]
- まず、サンプルを眺めてみたものの、全体的な構成が全く分からない。
- これは、ストーリーボード(Interface Builerというべきか)が間に入っているからじゃないか?
- Interface Builderを使わないでやってみよう。
- でも、ストーリーボードが無い頃の記述なので、 こちらも参考にさせてもらった。
- Xcode5から、ストーリーボードを作らない指定が出来なくなっている様なので、これもまた違いがあるんだけど。。1
- プロジェクトを新規作成
- File→New→Project...で、Single View Applicationを作成
- ちなみにClass Prefixを、BlufishからBFとしたので、生成されるファイル名の頭にBFと付いている。
- Storyboardを削除
- 左側のプロジェクトナビゲータから、Storyboardのファイルを右クリックでDeleteする
- info.plistのStoryboardの参照を削除
- さらに、Supporting Filesに入っているxxx-info.plistファイルを開いて、Main storyboard file base nameの項目を削除
- AppDelegate.mのapplication:didFinishLaunchingWithOptionsメソッドで
- AppDelegateは、main.mから呼び出されている。
- これは、変更なし
#import <UIKit/UIKit.h>
#import "BFAppDelegate.h"
int main(int argc, char * argv[]) {
@autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([BFAppDelegate class]));
}
}
- これで、コンパイルすれば空っぽの黒いウィンドウが表示される
- 空っぽのウィンドウが表示されてもプログラムとは言えないので、Windowにラベルを置いて表示してみる。
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; // Windowの大きさをスクリーンサイズに
CGRect rect = [self.window frame]; // Windowの大きさを求めて
UILabel* label = [[UILabel alloc] initWithFrame:rect]; // Labelの大きさをそれに合わせる
label.text = @"Hello World!"; // ラベルを"Hello World!"にする
self.window.backgroundColor = [UIColor whiteColor]; // Windowの背景色を白に
[self.window addSubview:label]; // Windowにラベルを追加
[self.window makeKeyAndVisible]; // Windowを表示する
return YES;
}
- これで、InterfaceBuilderを使わないHello Worldが出来ました!
- 初め、ラベルが表示されないので、何か間違ってるのかと思って悩んだんだけど、
- Windowの背景色は、黒がデフォルトらしい。文字も黒なので見えなかっただけ
- 今度は、InterfaceBuilderと同じ様に、Viewにラベルを表示してみよう。
- AppDelegate.hで、ViewControllerクラスをインポート
- viewControllerのインスタンス保持するためのプロパティを追加
#import <UIKit/UIKit.h>
#import "BFViewController.h" // ViewControllerクラスをインポート
@interface BFAppDelegate : UIResponder <UIApplicationDelegate>
@property (strong, nonatomic) UIWindow *window;
@property (strong,nonatomic) BFViewController *viewController; // viewControllerのインスタンスを保持するためのプロパティ追加
@end
- AppDelegate.mの、application:didFinishLaunchingWithOptionsメソッドで
#import <UIKit/UIKit.h>
@interface BFViewController : UIViewController
@end
- ViewController.mの、viewDidLoadメソッドで、Windowと同じ様にViewにLabelを表示する
#import "BFViewController.h"
@implementation BFViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
CGRect rect = [self.view frame]; // viewの大きさを求めて
UILabel* label = [[UILabel alloc] initWithFrame:rect]; // Labelの大きさをそれに合わせる
label.text = @"Hello View World!"; // ラベルを"Hello View World!"にする
self.view.backgroundColor = [UIColor whiteColor]; // viewの背景色を白に
[self.view addSubview:label]; // viewにラベルを追加
}
@end
- これで、Window上のViewに表示することが出来た。
- Windowに直接描画する方が簡単だけど、Viewならば複数の画面を簡単に切替えられるので、プログラムの自由度が大きくなるってことらしい。
- それでは、ボタンのようなActionに対する処理はどうしたら良いんだろう。
- Inaterface Builderを使わない方が、プログラムの構造が全然分かりやすいような。。。
1何で、こう開発環境の仕様をコロコロと変えるんだろう?Appleはユーザの利便性を全然考えていないのかなぁ。
[top]
- さくらインターネットのスタンダードプランで、サーバを借りているので、データはそちらに置いておきたい。
- MacMiniから、ssh接続でマウントするメモ →参考
$ svn co http://macfuse.googlecode.com/svn/trunk/filesystems/sshfs/binary sshfs-binaries
$ cd sshfs-binaries
$ sudo mv sshfs-static-leopard /bin/sshfs
- デスクトップ上に、OSXFULE Volume 0が出来る
- あとは、普通のディスクのようにアクセス可能(ちょっと遅いけど、凄く便利)
- 手動でまうんとするのは面倒なので、
- OSX標準の、autometerでマウントコマンドのスクリプトを作る
- autometerを起動して、ファイル→新規で、Applicationを選択
sshfs: cannot find sshnodelay.so
- 調べてみると、このエラーは新しいOpenSSHでは出ないらしいが、
- これが直接の原因じゃなくて、Login時にパスワードが必要なのが問題らしい。
- 鍵認証で、SSH Loginするように設定(後述)してから、再度実行すると、ちゃんとマウント出来た。
- autometorのスクリプトを、アプリケーションフォルダに保存しておく
- 自動実行の設定
- システム環境設定→ユーザとグループ→ログイン項目タブを選択
- +を押して、保存しておいた autometorのスクリプトを追加する
- 追加したスクリプトが、ログイン時に実行されるようにチェックを入れる
- これで、Login時に自動的にマウントされるようになった。
$ ssh-keygen
$ scp .ssh/id_rsa.pub username@sitename:id_rsa.pub
- サーバにLoginして、送信したファイルを、authorized_keys2に設定
$ ssh username@sitename
$ cat id_rsa.pub >> ~/.ssh/authorized_key2
$ chmod 600 ~/.ssh/authorized_key2
$ exit
- あとは、再度サーバにLoginすると、最初にOSXのキーチェインで、パスワードを聞いてくるので、
- 公開鍵作成時のパスワードを登録しておけば、次回からはパスワードなしでLogin出来るようになる。
[top]
- アプリケーションのアイコン設定は、まず復数のアイコンファイルを作成する必要がある。
- Xcode5では、プロジェクトファイルのImage.xcassetsをクリックすると、AppIconとLaunchImageが設定出来る。
- iPhoneの場合、最低限、以下の画像サイズのファイルを用意すれば良いようだ
→iPhone 29pt 58x58
→iPhone 40pt 80x80
→iPhone 60pt 120x120
→ 2x 640x960(縮小表示)
→R4 640x1136(縮小表示)
- 一般的なアプリの動作じゃないんだけど、、
- 動作させ続ける必要がないプログラムも簡単に終了出来ないのが気持ち悪かった。
- xxxx-info.plistで、右クリックしてAdd Row、
- Application does not run in backgroundというKeyを追加、ValueをYESにする
- JSON形式の設定ファイルを用意、例えばファイル名config.jsonを以下の様に記述
{
"block" : {
"width" : 34.0,
"height" : 16.0,
"rows" : 5,
}
}
- NSDictionary型のconfigに読み込む
static NSDictionary *config = nil;
NSString *path = [[NSBundle mainBundle] pathForResource:@"config" ofType:@"json"];
NSData *data = [NSData dataWithContentsOfFile:path];
if (!config) {
config = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingAllowFragments error:nil];
}
int rows = [config[@"block"][@"rows"] intValue];
CGFloat width = [config[@"block"][@"width"] floatValue];
CGFloat height = [config[@"block"][@"height"] floatValue];
[top]
[top]
[プログラムの部屋に戻る]
⇒ Disqusの広告がうるさすぎるので基本は非表示にしました