Thursday, December 5, 2013

ローカライズでいろいろつまづいた

自分のローカライズの方法がわるかったのか、英語環境と日本語環境でのframeが同じなのに表示される場所が違うってことが起きた。

それを解決するためにはユーザーの言語環境を見ないといけないんだけど、それをifで分けたら解決できた。

    frame = self.view.frame;
    
    float version = [[[UIDevice currentDevice] systemVersion] floatValue];
    CGRect statusFrame = [[UIApplication sharedApplication]statusBarFrame];
    CGRect navFrame = self.navigationController.navigationBar.frame;
    
    // NSLocalizedStringのEnglishとJapaneseどちらが適用されるかを調べる
    NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
    NSArray *languages = [ud objectForKey:@"AppleLanguages"];

    int enIndex = [languages indexOfObject:@"en"];
    int jaIndex = [languages indexOfObject:@"ja"];
    
    float offset = (enIndex < jaIndex ? navFrame.size.height + (version < 7.0 ? 0 : statusFrame.size.height) : 0);
    
    frame = CGRectMake(0, 0, frame.size.width, frame.size.height - offset);
こんな感じにわけたら、うまく解決できた。

多分もっといい方法があると思うんだけどね。


あと、これでわかったことなんだけど、NSLocalizedStringはlanguagesで一番最初の言語に対応する文字を返す。

Tuesday, December 3, 2013

JASidePanelで左メニューが表示される際のデリゲートを作成する

左のメニューが表示されるさいに、その中のtableviewを更新したいなーと思っていたので、ごちゃごちゃやってたら出来ました。
参考にしたのは以下の二つのリンク
逆引きObjective-C for iPhoneアプリ - デリゲートを自作クラスに実装する
[iOS] Protocol – Delegateパターン | Objective-C イベント伝達 その1 « きんくまデザイン

以下、その手順です。

1. delegateプロパティをJASidePanelController.hに追加する

以下のコードをJASidePanelController.hに加えます。

@property (nonatomic, assign) id delegate;

2. プロトコルをJASidePanelController.hファイルの中で定義します

プロトコルをJASidePanelController.hファイルの中にこう定義します。
(プロトコルがなんなのかは現段階で理解できていないが、気にしない♪)

// ここまで省略

@protocol JASidePanelControlDelegate; // ここを追加しました

typedef enum _JASidePanelStyle {
    JASidePanelSingleActive = 0,
    JASidePanelMultipleActive
} JASidePanelStyle;

typedef enum _JASidePanelState {
    JASidePanelCenterVisible = 1,
    JASidePanelLeftVisible,
    JASidePanelRightVisible
} JASidePanelState;

@interface JASidePanelController : UIViewController

#pragma mark - Usage
/*
ここからずっと下まで省略
*/

// Containers for the panels.
@property (nonatomic, strong, readonly) UIView *leftPanelContainer;
@property (nonatomic, strong, readonly) UIView *rightPanelContainer;
@property (nonatomic, strong, readonly) UIView *centerPanelContainer;

@property (nonatomic, assign) id delegate; // これを追加しました

@end


/************ ここから下のコードも追加しました ************/
@protocol JASidePanelControlDelegate 

@optional
- (void) JASidePanelControl:(JASidePanelController *)controller leftMenuVisible:(UIViewController *)viewController;

@end

3. 左メニューが表示されるところでデリゲートを呼び出す。

僕はここにこう書きました。

- (void)toggleLeftPanel:(__unused id)sender {
    NSLog(@"%@", _visiblePanel);
    
    if (self.state == JASidePanelLeftVisible) {
        [self _showCenterPanel:YES bounce:NO];
    } else if (self.state == JASidePanelCenterVisible) {
        [self.delegate JASidePanelControl:self leftMenuVisible:_leftPanel]; // ここを追加
        [self _showLeftPanel:YES bounce:NO];
    }
}

4. 最後にJASidePanelControllerのサブクラスでデリゲートを設定しデリゲートメソッドを埋める

// ~省略~
- (void)viewDidLoad
{
    [super viewDidLoad];
 // Do any additional setup after loading the view.
    
    self.delegate = self;
}

// ~省略~
- (void) JASidePanelControl:(JASidePanelController *)controller leftMenuVisible:(UIViewController *)viewController {
    leftMenuViewController *leftMenu = (leftMenuViewController *)viewController;
    [leftMenu.table reloadData];
}

多分、これでこれから表示されるViewControllerのメソッドを実行できるはず。

それよりも、はてなブログってすごいな。
あっちにコードがものすごくきれいに表示される。これからあそこをメインにしようっと。

でけたーーー!!驚きです。

Monday, December 2, 2013

NavigationBarを編集する

iPhoneアプリだと上によくナビゲーションバーがありますよね。
その編集方法です。

タイトルをつける

こちらに書いてあるコードの

self.navigationItem.title = @"タイトル";

と書くと自分の望むように変えられます。

ボタンを作成する。


    UIBarButtonItem *setting = [[UIBarButtonItem alloc]initWithImage:[UIImage imageNamed:@"settings_32.png"]
                                                               style:UIBarButtonItemStylePlain
                                                              target:self
                                                              action:@selector(settingButtonTapped:)];
    self.navigationItem.rightBarButtonItem = setting;

これは画像を使ってボタンを作成する際のサンプルコード。
UIBarButtonItemを使うんだねと覚えておけば、次調べるときも時間をかけなくてすむ。

iPhoneのOSのバージョンをプログラムから取得する

こちらのスタックフローを参考にした。
http://stackoverflow.com/questions/7848766/how-can-we-programmatically-detect-which-ios-version-is-device-running-on

NSStringで

[[UIDevice currentDevice] systemVersion]
で取得できる。

iOS6で追加された「引き下げて更新」を使ってみた

iOS6で「引き下げて更新」がdefaultで用意されました。

なので、その使い方をこちらのリンクを参考に書きます。


// UITableViewが画面にあるので、そこに対応させるようにしてみました。
- (void)viewDidAppear:(BOOL)animated {
    // UITableViewControllerを作成
    UITableViewController *tableVC = [[UITableViewController alloc]init];
    tableVC.tableView = self.table;
    
    // UIRefreshControlを作成
    UIRefreshControl *refresh = [[UIRefreshControl alloc]init];
    
    [refresh addTarget:self action:@selector(refresh:) forControlEvents:UIControlEventValueChanged];
    
    // 作成したrefreshをtableVCにくっつける。
    tableVC.refreshControl = refresh;
}

// refreshするプログラム。
- (void)refresh:(UIRefreshControl *)refresh {
    UIAlertView *alert = [[UIAlertView alloc]initWithTitle:@"sample"
                                                   message:@"sample"
                                                  delegate:self
                                         cancelButtonTitle:@"OK"
                                         otherButtonTitles:nil, nil];
    [alert show];
    [refresh endRefreshing];
}


使ってみて、意外とカスタマイズ性があまりなかったので、「引き下げて更新」を使っているアプリはほかの方法を使っているのでしょう。

defaultではださいので、cocoacontrolで見つけたこちらのライブラリーも試してみます。
これも比較的すぐ出来ました。
こちらのGithubページの通りやればすぐ出来ると思うのでわざわざ記事にはしません。

JASidePanelsの使いかた

しゃれおつなアプリを使うとあるボタンを押すと左からメニューがでてくることありますよね。

そんなものを実現してくれるのがこのgotosleep / JASidePanelsです。
ほかにも同じようなことを実現してくれるライブラリはあるのでCocoa Controlで探してみるといいでしょう。

とりあえず、今回はJASidePanelsを使うということで。

1. QuartzCore.frameworkを加える。
2. 
JASidePanelController.h
JASidePanelController.m
UIViewContorller+JASidePanel.h
UIViewContorller+JASidePanel.m
をプロジェクトに加える。
3. 
JASidePanelControllerを継承する適当なクラスをつくる。
ここではmySidePanelViewControllerと名付ける。
4.
そして作成したmySidePanelContorllerクラスのmySidePanelViewContorller.mに以下のコードを加える。

-(void)awakeFromNib{
    [self setLeftPanel:[self.storyboard instantiateViewControllerWithIdentifier:@"leftMenu"]];
    [self setCenterPanel:[self.storyboard instantiateViewControllerWithIdentifier:@"center"]];
    
    [self setLeftFixedWidth:200];
}

storyboard上で「leftMenu」と名付けられたViewControllerが左に表示される画面。
storyboard上で「center」と名付けられたViewControllerが中央に表示される画面。

※ここでstoryboardでcenterと名付ける画面をNavigation Controllerにすると、ライブラリ側で左上にメニューの画像をおいてくれます!

ただ、左のメニューから真ん中のViewControllerに値を渡す方法がないので僕は渡したい値をNSUserDefaultを使用してます。

これだけで出来ます。

Sunday, November 24, 2013

画像をアプリ内に保存 / 取り出し

画像を保存する

画像をurlにわざわざ取りにいくのは時間もかかるしめんどくさいので、アプリ内に保存してしまいましょう。
LINEとかはそうしてますよね。オフラインでも友達の写真は表示されるのでわかります。

以下のコードでOK→(参照もと: http://fitss.jp/blog/2013/05/xcodeImagePicker.shtml)


            UIImage *image = self.image.image;
            NSData *imageData = UIImagePNGRepresentation(image);
            NSString *path = [NSString stringWithFormat:@"%@/image.png", [NSHomeDirectory() stringByAppendingPathComponent:@"Documents"]];
            if ([imageData writeToFile:path atomically:YES]) {
               NSLog(@"save successs");
            } else {
                UIAlertView *alert = [[UIAlertView alloc]initWithTitle:@"エラー"
                                                               message:@"画像が保存できませんでした。あとでもう一度お試しください"
                                                              delegate:self
                                                     cancelButtonTitle:@"確認"
                                                     otherButtonTitles:nil, nil];
                [alert show];
                return;

            }



そしてこちらが保存した写真を取り出す方法

画像を取り出す

NSData *imageData = [NSData dataWithContentsOfFile:[imageLinks objectAtIndex:i]];
UIImage *image = [[UIImage alloc]initWithData: imageData];

乱数を作成する

小数 (0 ~ 1.0)

double drand48()
これだけ!!

整数 (0 ~ x)

xが100の場合
int someinteger = arc4random() % 100
このとき生み出される数の範囲は0 <= x <= 99
99までしか生み出されない。
こんな感じ。

整数で望む範囲の数を取得するにはちょっと注意が必要かも。

「One Habit Log」がリリースされました

僕が初めて作ったアプリ「One Habit Log」がリリースされました。
One Habit Log on the App Store on iTunes

アプリを作ろうと思ったことは何度もあるんですけど、ユーザーのデータが必要でデータベースが必要だったり、スピードが出なかったりで個人では出来なかったんですね。(←単純に僕の実力が足りない)

でも、今回はネットワークへ接続することはしないですし、ユーザー一人で完結するサービスなのでそこら辺の心配はなかったので楽だったです。

今回の一番の収穫はMagical Recordを使えるようになったこと。

次はこれをGoogle ChromeみたいなUIで複数記録できるようにしたいんだよね。


方法は暇なとき探しているけど、なかなかViewを取得して表示てことができない。
と思ってたら意外と簡単にUIViewを画像で保存することは出来るらしい。
そうだ、プログラミングしよう UIViewを画像で保存する。

アップデートできそう?

Sunday, November 10, 2013

やっとアプリの提出がおわったーー!!

ValidationでXcodeがクラッシュしていたのですがなんとか、解決できましたー!

Provisioning Profileのファイルが登録されていなかったからでした。
Provisioning Profileを制作するとこんな画面になりました。


これで僕が作成したアプリが「Waiting For Review」になりました!

これで審査が通れば、晴れてApp Storeに出ます!

Saturday, November 9, 2013

archiveしたアプリをValidateできない

Validateを押して自分のアカウント情報を入れると、ユーザーのauthentication fileが見つかりませんて出てきて、そこでiTunes Connectからダウンロードスルを選択するとxcodeが落ちる。

不可解

2013/11/10 追記
この問題ですが解決しました!
やっとアプリの提出がおわったーー!!

これで審査が通ればApp Storeに現れます!

Saturday, November 2, 2013

NSStringの空白かチェック

UITextFieldとかUITextViewに入力された文字が空白かどうかを判定するメソッド。
ここから丸パクリ。iphone - NSString is empty - Stack Overflow


- (BOOL) isEmpty:(NSString *)string {
    if([string length] == 0) { //string is empty or nil
        return YES;
    }
    
    if(![[string stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]] length]) {
        //string is all whitespace
        return YES;
    }
    
    return NO;
}

UITextViewなどに囲い線をつける

何度もやっていたのに久しぶりに触りだすと忘れていたのでメモ。
UITextViewでユーザーに入力してほしいから領域を示すために画像を周りを線で囲いたいことがある。


//1. まずQuartzCore.frameworkを使用する

// 2. つぎにFirstViewContorller.mとか使用したいmファイルでQuartzCoreをimport

// 3. あとは使用したいところに以下のように指定する
    self.text.layer.borderColor = [UIColor grayColor].CGColor;
    self.text.layer.borderWidth = 1.0;
    self.text.layer.cornerRadius = 4.0;

Friday, November 1, 2013

UITableViewのメソッドとか

UITableViewControllerを使わないでUITableViewを使いたいてのはよくあると思うけど、よくテーブルビューのためのメソッド一覧を忘れてしまうのでその一覧。
僕が比較的よく使うやつ。
//table delegate
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    return 1;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return 10;
}

-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
    return 100;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    UITableViewCell *cell = [[UITableViewCell alloc]init];
    
    static NSString *CellIdentifier = @"OneQuestion";
    cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];

    return cell;
}

-(void)tableView:(UITableView *)tableView didHighlightRowAtIndexPath:(NSIndexPath *)indexPath{

}

-(void)scrollViewDidScroll:(UIScrollView *)scrollView {

}

Monday, October 7, 2013

ローカライズの方法とアプリの申請

これもまた参考にしたリンクのメモ。
ローカライズの方法。

こちらが大変参考になる。
アプリの名前を言語によって変える
Xcodeでアプリ名を多言語に対応させる方法(ローカライズ)
アプリの中の写真や文章を他言語対応する方法
iPhoneアプリ開発者が知っておきたい「アプリを多言語化(ローカライズ)」する方法

次は申請!!
これらのリンクを参考に進めていきました。
iPhoneアプリ申請やAppleの審査に関するメモ
【まとめ】iPhoneアプリ Xcode4での iTunesConnectへ申請する方法まとめ

またこちらのリンクもリリースする前の確認事項のリンクがあり非常にありがたかったです。
Mac初心者のiPhoneアプリ開発記9 アプリ申請までの道


僕はitunesconnectを使用して申請しました。
デベロッパープログラムに登録しているアカウントでログインして、手順に従って入力事項を入力すれば以外とすぐ終わりました。

紹介画像などを様々な大きさで用意するのに時間がかかりましたが、それ以外は特に詰まったこともなかったです。

チュートリアルの作り方

初めてアプリを使用するユーザーにチュートリアルを見せますよね。
そのチュートリアルの作成です。

参考にしたのはこちら→UIPageControlの使い方
以下のサンプルコードはこちらのサイトのほぼ丸パクリです。

サンプルのチュートリアルです。

//
//  ViewController.h
//  SampleApp
// いらないコメント省略

#import 

@interface ViewController : UIViewController 

@end

// ViewController.m
#import "ViewController.h"

@interface ViewController () {
    UIPageControl *pager;
    UIScrollView *scroll;
}

@end

@implementation ViewController

- (void)viewDidLoad
{
    [super viewDidLoad];
}

- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

- (void)viewDidAppear:(BOOL)animated {
    int page = 3;
    
    // scrollを作成
    scroll = [[UIScrollView alloc]initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height - 100)];
    scroll.contentSize = CGSizeMake(self.view.frame.size.width * page, self.view.frame.size.height - 150);
    scroll.backgroundColor = [UIColor clearColor];
    
    // ページング指定
    scroll.pagingEnabled = YES;
    
    // スクロールバーを表示しない
    scroll.showsHorizontalScrollIndicator = NO;
    scroll.showsVerticalScrollIndicator = NO;
    // ステータスバータップで上に戻らない。
    scroll.scrollsToTop = NO;
    // delegate
    scroll.delegate = self;
    
    // scroll内で表示するViewを作る。
    for (int a = 0; a < page; a++) {
        UIView *view = [[UIView alloc]initWithFrame:CGRectMake(self.view.frame.size.width * a + 20, 20, self.view.frame.size.width - 40, self.view.frame.size.height - 170)];
        switch (a) {
            case 0:
                view.backgroundColor = [UIColor redColor];
                break;
            case 1:
                view.backgroundColor = [UIColor blueColor];
                break;
            case 2:
                view.backgroundColor = [UIColor greenColor];
                break;
            default:
                break;
        }
        [scroll addSubview:view];
    }
    
    
    // pagerを作る
    pager = [[UIPageControl alloc] initWithFrame:CGRectMake((self.view.frame.size.width / 2) - 20, self.view.frame.size.height - 40, 40, 20)];;
    pager.numberOfPages = page;
    pager.currentPage = 0;
    pager.pageIndicatorTintColor = [UIColor colorWithRed:0.84f green:0.85f blue:0.86f alpha:1.0f];
    pager.currentPageIndicatorTintColor = [UIColor blackColor];
    
    [pager addTarget:self action:@selector(changePageControl:) forControlEvents:UIControlEventValueChanged];
    
    [self.view addSubview:pager];
    [self.view addSubview:scroll];
}

// scrollしたときのdelegate
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
    // UIScrollViewのページ切替時イベント:UIPageControlの現在ページを切り替える処理
    pager.currentPage = scroll.contentOffset.x / self.view.frame.size.width;
}

// pagerのバリューに伴ってscrollも移動させる
- (void)changePageControl:(id)sender {
    
    // ページコントロールが変更された場合、それに合わせてページングスクロールビューを該当ページまでスクロールさせる
    CGRect Aframe = scroll.frame;
    Aframe.origin.x = self.view.frame.size.width * pager.currentPage;
    Aframe.origin.y = 0;
    // 可視領域まで移動
    [scroll scrollRectToVisible:Aframe animated:YES];
}


@end


次はこれを踏み台にして、Safariみたいな複数のページを自分で切り替えられるようなものを実装します。

アプリのアイコンを作成するのに参考にした画像など

まずはデフォルトアイコンやスプラッシュ画面のサイズ、名称を参考にさせていただいたリンク
iOSアイコンファイル早見表(2013/9)
すんばらしく見やすい。これ見て一発でわかった。
まじ感謝


Xcode5からはアイコンやスプラッシュ画面専用のImages.xcasesetsが出てきたからかなり管理しやすくなった。
アプリをリリースするの初めてだから、Xcode4でどうやって管理していたかは知らないけど。。。

アイコンやスプラッシュ画面などの画像はこちらのwebサービスを使いました。
簡単に文字や、背景色、文字色を指定した好きな大きさの画像が作れる大変優れたサービスです。
dummyimage.com

ここで無地の画像を作成して、Mac純正の画像にリサイズ、Skitchという無料のアプリで文字を入れたりしました。

今回作ったアプリはこんな感じのアプリです。
タイトルは「One Habit Log」。その名の通り一つの習慣しか記録することが出来ません。

これが最初アプリを起動した画面
右上のタイトルをクリックすると意気込みやタイトルをつけることが出来ます。
日付を指定すると下から、ボタンが出てきて記録することが出来ます。
記録した後の画面。

記録が目的なので今日の日付以降は記録することが出来ません。

次の記事はおそらくチュートリアルの作り方です。




Sunday, October 6, 2013

Magical Recordの使い方

やっとMagical Recordが使えるようになったのでメモしておく。
Personというエンティティ名をサンプルとして使用する。
こんな感じのデータ構造になっております。

Person
--name
--age

Insert

[MagicalRecord setupCoreDataStack];

Person *person = [Person MR_createEntity];
person.name = @"Sato Taro";
person.age = [NSNumber numberWithInt: 19];
これだけで新しいPersonを作成することが出来る。

Select

 
[MagicalRecord setupCoreDataStack];
NSArray *persons = [Person MR_findAllWithPredicate:[NSPredicate predicateWithFormat:@"(name == '%@')", @"Sato Taro"]];
これが単純なセレクト
ANDでつなぐ場合
[MagicalRecord setupCoreDataStack];
Person *person = [Person MR_findFirstWithPredicate:[NSPredicate predicateWithFormat:@"(name == 'Sato Taro') AND (age == 19)"]];
SORTしたいとき
[MagicalRecord setupCoreDataStack];
NSArray *persons = [Persn MR_findAllSortedBy:@"age" ascending:YES];
ほかにもっと複雑なセレクトを書けたいときは順次加えていく。

Update

[MagicalRecord setupCoreDataStack];
Person *person = [Person MR_findFirstWithPredicate: [NSPredicatepredicateWithFormat:@"name = 'Sato Taro'"]];
person.name = @"Sato Kogoro";
これだけ!

Delete

[MagicalRecord setupCoreDataStack];
Person *person = [Person MR_findFirstWithPredicate: [NSPredicatepredicateWithFormat:@"name = 'Sato Taro'"]];
[person MR_deleteEntity];

今のところこんな感じです。新しいことがわかったら随時加えていきます。

UIAlertViewにテキストのinputをさせる

参考→【iOS5】UIAlertViewにsetAlertViewStyleでテキストフィールドを追加する!!

ここを参考に僕も実装しました
- (void) settingButtonTapped {
    UIAlertView *setTitle = [[UIAlertView alloc] initWithTitle:@"Set Title"
                                                       message:nil
                                                      delegate:self
                                             cancelButtonTitle:@"Cancel"
                                             otherButtonTitles:@"Save", nil];
    [setTitle setAlertViewStyle:UIAlertViewStylePlainTextInput];
    [[setTitle textFieldAtIndex:0] setText:habit.title];
    [setTitle show];
}

// Setボタンを有効にするかを決める
- (BOOL)alertViewShouldEnableFirstOtherButton:(UIAlertView *)alertView {
    NSString *inputText = [[alertView textFieldAtIndex:0] text];
    
    if( [inputText length] >= 1 ) {
        return YES;
    } else {
        return NO;
    }
}

// アラートのsetTitleのsaveボタンが押された
-(void)alertView:(UIAlertView*)alertView clickedButtonAtIndex:(NSInteger)buttonIndex {
    NSString *input = [[alertView textFieldAtIndex:0] text];

    if (buttonIndex == 1) {
        NSLoG(@"%@", input);
    }
}

Saturday, October 5, 2013

UIButtonをプログラムから作成する

このようなフラットデザインのボタンを作れるライブラリを使いながらボタンを作成したのでめもめも
QBFlatDesignについてはこちらのgithubプロジェクトを見ていただければわかるので、ここでは触れません。

では、ボタンをプログラムから作る方法と、クリックイベントの取得の方法をばここに。
    QBFlatButton *bottomButton = [[QBFlatButton alloc]initWithFrame: CGRectMake(20, frame.size.height - 80, frame.size.width - 40, 60)];
    // デフォルトのボタンを作るときは以下のコード
     UIButton *bottomButton = [UIButton buttonWithType:UIButtonTypeRoundedRect]; // この場合あとでフレームを指定しないと行けない
    // font
    bottomButton.titleLabel.font = [UIFont boldSystemFontOfSize: 20];
    [bottomButton setTitle:@"Mark" forState:UIControlStateNormal];
    [bottomButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
    // color
    bottomButton.faceColor = [UIColor colorWithRed:0.23 green:0.83 blue:.51 alpha:1];
    bottomButton.sideColor = [UIColor colorWithRed:.16 green:.76 blue:.44 alpha:1];
    // event
    [bottomButton addTarget:self action:@selector(bottomButtonTapped) forControlEvents:UIControlEventTouchUpInside];

    [self.view addSubview:bottomButton];