2012年4月26日木曜日

UILongPressGestureRecognizerの挙動を判別

以前に、GestureRecognizerを使ってタッチ操作を検知というので記事を書いていましたが
そのなかでUILongPressGestureRecognizerは長押しが検知されたときと指が離されたときにイベントが発生すると書きました。

じゃあその長押しが検知されたときと指が離れたときの区別をどうやって判断するかということですがイベント発生時のsenderから状態をとってそこから判断することができます。
- (void)handleLongPressGesture:(UILongPressGestureRecognizer *)sender
{
    switch (sender.state) {
        case UIGestureRecognizerStateBegan:
            // 長押しを検知した場合の処理
            break;
        case UIGestureRecognizerStateEnded:
            // 指が離された場合の処理
            break;
    }
}

これで状態の判別を行うことができます。



UILabelに縁取りしたテキストを描画

昨日はUIImageViewに枠線をつけるやり方を紹介しましたが、今日はUILabelのテキスト部分を縁取りするやり方。
(UILabel自体に枠線をつける方法はUIImageViewと同じです)

まず、UILabelを継承したクラスを作成します。

それから外枠の色とサイズを設定できるようにヘッダーファイルを修正します。
@interface UIOutlineLabel : UILabel

@property (nonatomic, retain) UIColor *OutlineColor;
@property (nonatomic, assign) CGFloat OutlineWidth;

@end

次にソースファイルを修正します。
やり方としてはUILabelのもつ -drawTextInRect:rect をオーバーライドして縁取りします。
- (void)drawTextInRect:(CGRect)rect
{   
    CGSize shadowOffset = self.shadowOffset;
    UIColor *txtColor = self.textColor;
    
    CGContextRef contextRef = UIGraphicsGetCurrentContext();
    CGContextSetLineWidth(contextRef, OutlineWidth);
    CGContextSetLineJoin(contextRef, kCGLineJoinRound);
    
    CGContextSetTextDrawingMode(contextRef, kCGTextStroke);
    self.textColor = OutlineColor;
    [super drawTextInRect:CGRectInset(rect, OutlineWidth, OutlineWidth)];
    
    CGContextSetTextDrawingMode(contextRef, kCGTextFill);
    self.textColor = txtColor;
    [super drawTextInRect:CGRectInset(rect, OutlineWidth, OutlineWidth)];
    
    self.shadowOffset = shadowOffset;
}

あとはこれを使用するViewControllerで実装します。
InterfaceBuildeerを使って画面上にUILabelを貼付けて、そのラベルのIdentity inspectorを開きます。
Custom Classの部分でUIOutlineLabelを選択。

あとはこれを関連づけして色とサイズを指定するだけです。
[aDateLabel setOutlineColor:[UIColor whiteColor]];
[aDateLabel setOutlineWidth:5.f];






2012年4月25日水曜日

UIImageViewに枠線をつける

UIImageViewを使って画像を表示するをするときに簡単に枠線を表示する方法。


まず最初に枠線をつけるために CALayerクラスを使用します。そのためにフレームワーク(QuartzCore.framework)を追加します。

そして下のようにソースファイルを修正していきます。

#import "ViewController.h"
#import "QuartzCore/CALayer.h"

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    UIImage *image = [UIImage imageNamed:@"test.jpg"];
    UIImageView *imageview = [[UIImageView alloc] initWithImage:image];

    CALayer *layer = [imageview layer];
    [layer setMasksToBounds:YES];
    [layer setBorderWidth: 3.f];
    [layer setBorderColor:[[UIColor whiteColor] CGColor]];
}

こんなかんじです。
これはUIImageViewに枠線をつけるというやり方です。
UIImageに枠線をつける場合は画像処理が必要になってくるのかな。。。

簡単ですがメモ程度に



2012年4月24日火曜日

NSMutableArrayのコピー

NSMutableArray をコピーしようとちょっとハマったこと。。。

NSMutableArray *test1 = [NSMutableArray arrayWithObjects:@"test1", @"test2", @"test3", nil];
NSMutableArray *test2 = test1.copy

[test2 removeObject:@"test2"];
for (NSString *p in test1) {
    NSLog(@"%@", p);
}

これだとエラーになるみたいです。

NSMutableArray *test1 = [NSMutableArray arrayWithObjects:@"test1", @"test2", @"test3", nil];
NSMutableArray *test2 = [test1 mutableCopy];

[test2 removeObject:@"test2"];
for (NSString *p in test2) {
    NSLog(@"%@", p);
}

NSMutableArray をコピーする場合は mutableCopy を使うといいみたいです。

以上、備忘録でした。

UIViewのGestureRecognizerを削除する

GestureRecognizerについては以前書きました。
GestureRecognizerを使ってタッチ操作を検知


追加をする方法を簡単に書くと
    // タップ検知
UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc]
 initWithTarget:self action:@selector(handleTapGesture:)];
tapGesture.numberOfTapsRequired = 2;
[self.view addGestureRecognizer:tapGesture];
[tapGesture release];

次はこれを削除する方法
for (UIGestureRecognizer *gesture in [self.view gestureRecognizers]) {
    [self.view removeGestureRecognizer];
}

とまぁこんなかんじです。


UIViewのsubviewを削除する

UIViewにsubviewを追加する場合
// ボタンを追加
UIButton *button = [UIButton buttonWithType:UIButtonTypeInfoLight];
button.frame = CGRectMake(100,100,100,100);
[button addTarget:self action:@selector(doAction:)
     forControlEvents:UIControlEventTouchUpInside];

// 追加したいviewにaddSubview
[self.view addSubview:button];

逆にUIView内にあるsubviewを削除する場合
for (UIView *view in [self.view subviews]) {
    [view removeFromSuperview];
}



2012年4月22日日曜日

objective-cのdelegateを実装する

objective-cでのdelegateとは参照先のオブジェクトから参照元のオブジェクとに対して通知したりメッセージ送信などそれぞれの処理を参照元に委ねるものです。

よく使う機能としてはカメラの撮影やテキストフィールドの入力後処理などがあります。
自分で実装する例は下のようなかんじです。
(PopoverControllerなどでTableViewControllerを開き、そこで選択した情報を参照元のViewに戻すというもの)

ViewController.h
@interface ViewController : UIViewController
<
    SubViewDelegate
>
{
    UIPopoverController *popoverController;
}

ViewController.m
- (void)doAction:(id)sender
{
    // 参照先のオブジェクト生成
    SubViewController *sv = [[SubViewController alloc] init];
    sv.contentSizeForViewPopover = sv.view.frame.size;
    sv.delegate = self;

    popoverController = [[UIPopoverController alloc] initWithContentViewController:sv];
    [sv release];

    [popoverController presentPopoverFromBarButtonItem:sender
                                    permittedArrowDirections:UIPopoverArrowDirectionAny
                                    animated:YES];
}

// 参照先のdelegate実装
- (void)subviewController:(SubViewController *)view didSelectedIndex:(NSInteger)index
{
    [popoverController dismissPopoverAnimated:YES];

    NSLog(@"selected %d", index);
}

SubViewController.h
@class SubViewController;
@protocol SubViewDelegate 

- (void)subviewController:(SubViewController *)view didSelectedIndex:(NSInteger)index;

@end

@interface SubViewController : UITableViewController

@property (assign) id delegate;

SubViewController.m
@implementation SubViewController
@synthesize delegate;

〜省略〜

- (void)tableView:(UITableView *)tableView didSelectedRowAtIndexPath:(NSIndexPath *)indexPath
{
    [delegate subviewController:self didSelectedIndex:[indexPath row]];
}

とまぁこんなかんじです。
実際に作ってみるとそう難しいものでもないので試してみてください。




2012年4月21日土曜日

NSTimerの使い方

NSTimer その名の通りタイマーを管理するオブジェクトです。

ViewController.h
@interface ViewController : UIViewController
{
    NSTimer *aTimer;
}

ViewController.m
// 画面生成時
- (void)viewDidLoad
{
    // タイマーを生成しスタート
    aTimer = [NSTimer
                 scheduledTimerWithTimeInterval:5.f
                 target:self
                 selector:(tick:)
                 userInfo:nil
                 repeat:YES];
}

// 画面破棄時
- (void)viewDidLoad {
    // タイマーを破棄
    [aTimer release];
    aTimer = nil;
}

// タイマーが動作した時の処理
- (void)tick:(NSTimer *)timer
{
    ...
}

// 基本的な使い方は上の通り
// 下はこんな使い方もってかんじ

// 画面表示時
- (void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];

    // タイマーが動いているか判定し、停止している場合は再開する
    if (![aTimer isValid])
        [aTimer fire];
}

// 画面終了時
- (void)viewDidDisappear:(BOOL)animated
{
    [super viewDisappear:animated];

    [aTimer invalidate];
}

こんなかんじですかね。
viewDidDisapper とかでタイマーとめておかないと次のビューに遷移したあともタイマーが動き続ける可能性があります。
また画面に戻ってきたときは今度は viewWillAppear が反応するんでそこでタイマーを再開するというかんじです。
(viewの各メソッドは、ViewController作成時に最初からある各メソッド で簡単に載せてます)



2012年4月20日金曜日

UITextFieldの機能いろいろ

以前に、iOSのソフトウェアキーボードを閉じるでも紹介しましたが他にもいろいろな機能があるのでよく使うものを簡単に紹介します。

テキストフィールド内に説明を表示する
textField.placefolder = @"ここに名前を入力してください";

リターンキーの表示を設定する
textField.returnKeyType = UIReturnKeyDone;
typedef enum {
    UIReturnKeyDefault,  // 改行 or return
    UIReturnKeyGo,         // 開く or Go
    UIReturnKeyGoogle,  // 検索 or Search 
    UIReturnKeyJoin,      // 接続 or Join
    UIReturnKeyNext,     // 次へ or Next
    UIReturnKeyRoute,   // 経路 or Route
    UIReturnKeySearch,  // 検索 or Search
    UIReturnKeySend,     // 送信 or Send
    UIReturnKeyYahoo,   // 検索 or Search
    UIReturnKeyDone,     // 完了 or Done
    UIReturnKeyEmergencyCall,  // 緊急電話 or EmergencyCall
} UIReturnKeyType;

キーボードの配列を設定する
textField.keyboardType = UIKeyboardTypeURL;
typedef enum {
    UIKeyboardTypeDefault,
    UIKeyboardTypeASCIICapable,
    UIKeyboardTypeNumbersAndPunctuation,
    UIKeyboardTypeURL,
    UIKeyboardTypeNumberPad,
    UIKeyboardTypePhonePad,
    UIKeyboardTypeNamePhonePad,
    UIKeyboardTypeEmailAddress,
    UIKeyboardTypeDecimalPad,
    UIKeyboardTypeTwitter,
} UIKeyboardType;

テキストフィールド内にクリアボタンを表示する
textField.clearButtonMode = UITextFieldViewModeWhiteEditing;
typedef enum {
    UITextFieldViewModeNever,
    UITextFieldViewModeAlways,
    UITextFieldViewModeUnlessEditing,
    UITextFieldViewModeWhiteEditing
} UITextFieldViewMode


テキストフィールドにフォーカスを当てる
[textField becomeFirstResponder];

リターンキーが押されたことを検知する
-(BOOL)textFieldShouldReturn:(UITextField*)textField
{
    return YES;
}

テキストフィールドの編集が終了したことを検知する
-(void)textFieldDidEndEditing:(UITextField*)textField
{
    ...
}

クリアボタンが押されたことを検知する
-(BOOL)textFieldShouldClear:(UITextField*)textField
{
    return YES;
}

かなり適当ですけどよく使いそうな機能たちです。リターンキーや配列などは実際に設定してみてどれが一番あってるか試してみるといいと思います。
最後の3つはデリゲートの設定をしておく必要があります。
(ヘッダーファイルに UITextFieldDelegate 、ソースファイルに [textField setDelegate:self] みたいなかんじ)




View関係でちょいちょい使えそうな機能 其の二

前回に引き続き使えそうな機能。
完全に備忘録です。。。

UINavigationControllerで遷移元のView(親View)にデータを渡す
// 画面を離れるとき
- (void)viewWillDisappear:(BOOL)animated {
    NSArray *views = [self.navigationController.viewControllers];
    UIViewController *vc = [views objectAtIndex:views.count - 1];
    vc.hoge = hoge;
}

self.navigationController.viewControllersでナビゲーションしているViewControllerを取得してその何個目にデータを渡すというやり方です。

重複しない番号(連番)の作り方
NSInteger serialNumber = [[NSUserDefaults standardUserDefaults] integerForKey:@"serialNumber"];
serialNumber++;
[[NSUserDefaults standardUserDefaults] setInteger:serialNumber forKey:@"serialNumber"];
[[NSUserDefaults standardUserDefaults] synchronize];

UserDefaultsを使用して連続した番号を保存するやり方。毎回保存することで常に最大値を取得することができます。

UserDefaultsのデータを確認する場合は、iPhoneアプリ開発でアプリごとのデータファイルを確認するにもデータの確認方法は書いてますがここの「Library」というフォルダに保存されるようになっています。

2012年4月18日水曜日

View関係でちょいちょい使えそうな機能

アプリを作成している中で部分で使いそうな機能。

TableViewで指定したセルの位置までスクロール
NSIndexPath *indexPath = [NSIndexPath IndexPathForRow:_objects.count inSection:0];
[tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:indexPath]
     withRowAnimation:UITableViewRowAnimationAutomatic];

// ココ
[tableView scrollToRowAtIndexPath: indexPath atScrollPosition:UITableViewScrollPositionTop animated:YES];

上のコードではTableViewに項目を最下位の行に追加していくなかで、追加した位置までスクロールするというものです。

TableViewの更新
// TableView全体を更新
[tableView reloadData];

// 指定したセクションを更新
[tableView reloadSections:[NSIndexSet indexSetWithIndex:0] withRowAnimation:UITableViewRowAnimationNone];

// 指定した行を更新(複数指定も可能)
[tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationNone];

。。。まぁ何かと使えます。reloadDataは簡単に実装できます。

NSMutableArrayの値を変更
NSMutableArray *objects = [[NSMutableArray alloc] init];
for (int i = 0; i < 3; i++) {
    [objects addObject:[NSString stringWithFormat:@"sample%d", i]];
}
[objects replaceObjectAtIndex:1 withObject:@"test"];

追加・削除は当たり前ですが値の変更もよくあると思います。


。。。備忘録です。

2012年4月17日火曜日

コード上からTableViewにTextFieldを追加する方法

先日、カスタムTableViewCellの作り方を載せましたが
今度はコード上からもう少し簡単にTextFieldを追加する方法を。

カスタムTableViewCellのときと同様にアプリケーションを Master-Detail Application として作成しておきます。
次に、MasterViewController.m の tableView:cellForRowAtIndexPath:を以下のように修正します。
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *CellIdentifier = @"Cell";
    
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (cell == nil) {
        cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
                     reuseIdentifier:CellIdentifier] autorelease];
        
        UITextField *txtField = [[UITextField alloc] init];
        [txtField addTarget:self action:nil forControlEvents:UIControlEventAllEditingEvents];
        txtField.frame = CGRectMake(10.f, 10.f, cell.contentView.bounds.size.width - 20.f, 24.f);
        txtField.returnKeyType = UIReturnKeyDone;
        txtField.delegate = self;

        [cell addSubview:txtField];
        [txtField release];
    }
    
    return cell;
}

TextFieldを追加するコードとしてはこれだけです。
カスタムTableViewCellを作るのに比べるとそう複雑ではないと思います。



VBでプログラムが終了したことを検知する

System.Diagnostics.Processクラスを使ってプログラムを起動した場合、終了イベントを検知することができます。
設定だけなんでメモ程度に。
// ボタンを押してプログラムを起動
private Sub Button1_Click(ByVal sender As Object, ByVal e As EventArgs) Handles Button1.Click
    Dim wProc As Process = Process.Start("Notepad")

    // イベントを発生させるためにTrueにしておく
    wProc.EnableRasingEvents = True

    // イベントハンドラの追加
    AddHandler wProc.Exited, AddressOf Me.Proc_Exited
End Sub

// プログラム終了イベント
Private Sub Proc_Exited(ByVal sender As Object, ByVal e As EventArgs)
    Dim wProc As Process = DirectCast(e, Process)

    Console.WriteLine("Exit Time: {0}, Exit Code: {1}", wProc.ExitTime, wProc.ExitCode)
End Sub

忘れがちなのは EnableRasingEvents を True に設定しておくことぐらいかなと思います。
ExitTime はプログラムが終了した時間、ExitCodeではプログラムが正常終了なのかどうかの判断として使うことができます。正常終了時は通常「0」が入ります。


2012年4月16日月曜日

カスタムTableVIewCellを作ってみる

件名の通りカスタムTableViewCellを作ってみました。
iOSのドキュメントにも記載がありましたがよくわからないまま、とりあえず動きはします。

最初にプロジェクトの作成。
テンプレートは Master-Detail Application を使用します。
とりあえずプロジェクト作成後、一度ビルドと実行をしておきます。

カスタムTableViewCellを作るにはまず空のxibファイルを作成します。
File → New → File... でテンプレート選択画面で User Interface → Empty を選択して保存します。(ファイル名はEditTableViewCellとしました)

できあがったxibファイルに TableViewCell を貼付けます。
合わせて Label と TextField を TableViewCell 内に配置します。

次に、MasterViewController.hに以下を追加
@property (retain, nonatomic) IBOutlet UITableViewCell *editTableViewCell;

次に、EditTableViewCell.xibを開きFile's Ownerを選択します。
Identity inspectorを開きClassで MasterViewController を選択します。
続けれFile's Ownerをcontrolキーを押しながら選択するとOutletsに editTableViewCellが表示されているのでxibファイル内のTableViewCellオブジェクトと関連付けを行います。

MasterViewController.mを開きtableView:cellForRowAtIndexPath:を以下のように修正します。
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *CellIdentifier = @"Cell";
    
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (cell == nil) {
        [[NSBundle mainBundle] loadNibNamed:@"EditTableViewCell" owner:self options:nil];
        cell = editTableViewCell;
        self.editTableViewCell = nil;
    }
    
    return cell;
}

これでカスタムTableViewCellの完成です。
配置してあるLabelやTextFieldに値を入れたりする場合はtagを使用することで操作することができます。
// EditTableViewCellのTextFieldのtagを102とした場合
UITextField *txtField = (UITextField *)[cell viewWithTag:102];





UIViewControllerにUITableViewを表示する

TableViewを使った画面を作ろうと思ってちょっと苦戦したのでメモ程度に。
TableViewを使った画面を作る場合、XCode4.2のテンプレートでは予め Master-Detail Application というのがあります。
これを使わずに画面上にTableViewを実装するやり方。

まずヘッダーファイルに以下を宣言
@interface ViewController : UIViewControler
<
    UITableViewDataSource,
    UITableViewDelegate
>

次にViewController.xibにTableViewを貼付けて関連付けを行います。
まず、アウトレットの設定から。
Assistant editorを開いて貼付けたTableViewをOutletとして登録します。

次に、このTableViewと上で宣言したUITableViewのDataSourceとDelegateに関連付けを行います。
これで画面からの関連付けは完了です。
あとは以下のメソッドをソースファイルに追加することで実装できます。
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
    return 1;
}

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

- (UITableViewCell *)tableView:(UITableView *)tableView
     cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *CellIdentifier = @"Cell";
    
    UITableViewCell *cell
             = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (cell == nil) {
        cell = [[[UITableViewCell alloc]
             initWithStyle:UITableViewCellStyleDefault
             reuseIdentifier:CellIdentifier] autorelease];
    }
    
    return cell;
}

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

}

上のコード自体はMaster-Detail Application などで使用されているものと同じです。
戸惑う箇所は画面とソースの関連付けの部分かなと。。。

とりあえず忘れないように




2012年4月13日金曜日

NavigationControllerにleftBarButtonItemとbackBarButtonItemを表示

NavigationControllerにはbackBarButtonItem・leftBarButtonItem・rightBarButtonItemと左右に項目を配置する位置があります。

右は置いといて左。
NavigationBarに戻るボタンとほかにカスタムボタンを左側に配置したいと思ったんですがドキュメントを見ていると
”左の位置にカスタムボタンやビューを割り当て、それらでデフォルトの「戻る」ボタンを置き換えるには、leftBarButtonItemプロパティにUIBarButtonItemオブジェクトを割り当てます。”
とあります。
置き換えるということは共存はできないのかなと。。。

いろいろと調べてたらUIButtonTypeにAPIに記載されていない値が存在するみたいでそれを使うことでそれっぽいのが実装できました。
- (void)viewDidLoad
{
    [super viewDidLoad];

    UIButton *backButton = [UIButton buttonWithType:101];
    [backButton addTarget:self action:@selector(doBack:)
         forControlEvents:UIControlEventTouchUpInside];
    [backButton setTitle:@"戻る" forState:UIControlStateNormal];

    UIBarButtonItem *backItem = [[UIBarButtonItem alloc]
         initWithCustomView:backButton];

    UIBarButtonItem *folderItem = [[UIBarButtonItem alloc]
         initWithTitle:@"フォルダ" style:UIBarButtonItemStylePlain
         target:self action:@selector(doFolder:)];

    self.navigationItem.leftBarButtonItems = [NSArray arrayWithObjects:backItem, folderItem, nil];

    [folderItem release];
    [backItem release];
    [backButton release];
}

// 戻るボタンの実装
- (void)doBack:(id)sender
{
    [self.navigationController popViewControllerAnimated:YES];
}


戻るボタンの処理を実装する必要はありますが、一応これで上のような画面を作ることができます。



NavigationController の戻るボタンを変更する

NavigationControllerを使用して画面を遷移していくと自動的に戻るボタンが生成されます。
これはデフォルトでは呼び出し元のViewのタイトルが表示されます。
(タイトルが設定されていない場合は"back"と表示されます)
 

タイトルが長い場合などバランスが崩れてしまうのでこれを変更する方法。
呼び出し元のViewController.m
- (void)tapAction:(id)sender
{
    UIBarButtonItem *backItem = [[UIBarButtonItem alloc] init];
    backItem.title = @"戻る";
    self.navigationItem.backButtonItem = backItem;
    [backItem release];

    UIViewController *vc = [[UIViewController alloc] init];
    [self.navigationController pushViewController:vc animated:YES];
    [vc release];
}

注意する点は呼び出し先のViewControllerではなく呼び出し元のViewControllerに記述する必要があります。



2012年4月12日木曜日

UIPopoverControllerを使う際の注意すること

先日、UIPopoverControllerの使い方を書きましたが
そのままではいくつものPopoverControllerが生成されてしまいます。
// Type4ボタンタップ時
- (IBAction) tapAction:(id)sender
{
    UIImagePickerController *ipc = [[[UIImagePickerController alloc] init] autorelease];
    ipc.delegate = self;
    ipc.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
    
    // Popoverのインスタンス生成
    UIPopoverController *popover = [[UIPopoverController alloc]
                                    initWithContentViewController: ipc];

    // Popoverを表示する
    [popover presentPopoverFromBarButtonItem:sender
                permittedArrowDirections:UIPopoverArrowDirectionAny
                animated:YES];
}

実際に上記のコードで起動してみると
左の画面は Type4 ボタンを1回だけクリックした状態、
右の画面は Type4 ボタンを10回くらいクリックした状態です。

右の画面では PopoverController の周りが黒く表示されているのがわかると思います。
これは Type4 ボタンをタップした際にその都度 PopoverController を生成しているからです。

これを回避するために、いくつかコードの修正を行います。

ViewController.h
@interface ViewController : UIViewController
{
    UIPopoverController *popover;
}

ViewController.m
// Type4ボタンタップ時
- (IBAction) tapAction:(id)sender
{
    // Popoverの確認・開かれている場合は一度閉じる
    if (popover) {
        if ([popover isPopoverVisible]) {
            [popover dismissPopoverAnimated:YES];
        }
    }

    UIImagePickerController *ipc = [[[UIImagePickerController alloc] init] autorelease];
    ipc.delegate = self;
    ipc.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
    
    // Popoverのインスタンス生成(ココが違う)
    popover = [[UIPopoverController alloc]
                                    initWithContentViewController: ipc];

    // Popoverを表示する
    [popover presentPopoverFromBarButtonItem:sender
                permittedArrowDirections:UIPopoverArrowDirectionAny
                animated:YES];
}

上記のように popover を予め宣言しておき、それを使い回しできるようにしておきます。
こうすることで Type4 ボタンをいくらタップしても影響がでることはないと思います。
ここでは記載してませんが、dealloc や viewDidunload 等で popover を 破棄した方がいいと思います。
他にも UIPopoverControllerDelegate を実装しておくことで Popover 部分以外をタップしたことによる画面の閉じる処理を通知してくれます。ここで破棄処理をいれておいてもいいかもしれません。
- (BOOL)popoverControllerShouldDismissPopover: popoverController
閉じる前に呼び出される。NOを返すことで画面が閉じないようになる
- (void)popoverControllerDidDismissPopover: popoverController
閉じた後に呼び出される





2012年4月11日水曜日

UIPopoverControllerを使ってみる

先日ActionSheetの使い方を載せましたがちょっと下のiBooksみたいな画面を表示する場合はUIPopoverControllerを使います。

- (IBAction) tapAction:(id)sender
{
    // 表示するViewController
    SelectViewController *svc = [[[SelectViewController alloc] init] autorelease];

    // Popoverの領域の大きさを設定
    svc.contentSizeForViewInPopover = svc.view.frame.size;

    svc.delegate = self;

    // Popoverのインスタンス生成
    UIPopoverController *popover = [[UIPopoverController alloc]
                                    initWithContentViewController: svc];

    // Popoverを表示する
    [popover presentPopoverFromBarButtonItem:sender
                permittedArrowDirections:UIPopoverArrowDirectionAny
                animated:YES];
}

Popoverの表示領域を設定しない場合は、画面いっぱいにViewが表示されます。
ここでは svc.view.frame.size を指定していますがこうしておくとViewのサイズに合わせて表示されます。
独自のViewController(上ではSelectViewController)を使用する場合は予めimportしておく必要があります。

他にも使い方としてはカメラロールの表示も同じやり方で出来ます。

- (IBAction) tapAction:(id)sender
{
    UIImagePickerController *ipc = [[[UIImagePickerController alloc] init] autorelease];
    ipc.delegate = self;
    ipc.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
    
    // Popoverのインスタンス生成
    UIPopoverController *popover = [[UIPopoverController alloc]
                                    initWithContentViewController: ipc];

    // Popoverを表示する
    [popover presentPopoverFromBarButtonItem:sender
                permittedArrowDirections:UIPopoverArrowDirectionAny
                animated:YES];
}

注意することも書いてみたのでそちらもどうぞ
UIPopoverControllerを使う際に注意すること


twitter連携 テスト

テストです
twitter feedを利用してbloggerの更新をtwitterと連動

2012年4月10日火曜日

Toolbarにボタンを追加する

インタフェースビルダーを使用してToolbarにボタンを配置することもできますがコードからボタンを配置する場合
// 巻き戻りボタン生成
UIBarButtonItem *rewindItem
     = [[[UIBarButtonItem alloc]
         initWithBarButtonSystemItem:UIBarButtonSystemItemRewind
         target:self
         action:@selector(doRewind)]
         autorelease];
// 再生ボタン生成
UIBarButtonItem *playItem
     = [[[UIBarButtonItem alloc]
         initWithBarButtonSystemItem:UIBarButtonSystemItemPlay
         target:self
         action:@selector(doPlay)]
         autorelease];
// 早送りボタン生成
UIBarButtonItem *forwardItem
     = [[[UIBarButtonItem alloc]
         initWithBarButtonSystemItem:UIBarButtonSystemItemRewind
         target:self
         action:@selector(doForward)]
         autorelease];
// 可変長スペース生成
UIBarButtonItem *flexibleItem
     = [[[UIBarButtonItem alloc]
         initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace
         target:nil
         action:nil]
         autorelease];
// 固定長スペース生成
UIBarButtonItem *fixedItem
     = [[[UIBarButtonItem alloc]
         initWithBarButtonSystemItem:UIBarButtonSystemItemFixedSpace
         target:nil
         action:nil]
         autorelease];
fixedItem.width = 100;

// ツールバーにボタンを配置
self.toolbarItems = [NSArray arrayWithObjects:flexibleItem, rewindItem, fixedItem, playItem, fixedItem, forwardItem, flexibleItem, nil];

上のようなかんじでUIBarButtonItemインスタンスを生成して、NSArrayに格納しUIToolbarのitemsに渡します。ツールバーの並びはNSArrayに格納した順番になります。
また、ボタンだけでなく可変長・固定長のスペースも同様に配置します。
これを実行すると以下のように表示されます。

また上のサンプルコードでは initWithBarButtonSystemItem を利用してボタンを生成しましたがこれは iOS が持っているUIBarButtonSystemItem を指定するメソッドです。
指定した文字列や画像を指定する場合
-initWithTitle: style: target: action:
文字列のボタンを生成する
-initWithImage: style: target: action:
指定した画像のボタンを生成する



2012年4月9日月曜日

画面をフルスクリーンで表示して各ツールバーを透明にする

カメラロールみたいなイメージで画面はフルスクリーンで表示してステータスバーやナビゲーションバー・ツールバーを透過
// フルスクリーンの設定
self.wantsFullScreenLayout = YES;

// 各バーの表示
[UIApplication sharedApplication].statusBarHidden = NO;
self.navigationController.navigationBarHidden = NO;
self.navigationController.toolbarHidden = NO;

// 各バーのスタイル
[[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleBlackTranslucent];
self.navigationController.navigationBar.barStyle = UIBarStyleBlackTranslucent;
self.navigationController.toolbar.barStyle = UIBarStyleBlackTranslucent;

何らかの操作でこれらを非表示にしたい場合は各バーのHiddenをYESに変更したりalphaを0に設定する
(アニメーションで非表示にする場合、ナビゲーションバー・ツールバーはalphaを変更したほうがスムーズになります)
// アニメーションの設定
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:0.5];
[UIView setAnimationCurve:UIViewAnimationOptionCurveEaseInOut];

// 各バーを非表示
[UIApplication sharedApplication].statusBarHidden = YES;
self.navigationController.navigationBar.alpha = 0;
self.navigationController.toolbar.alpha = 0;

// アニメーションコミット
[UIView commitAnimations];




2012年4月3日火曜日

ActionSheetの使い方

ツールバーとかにボタンを配置してそこから何かを選択させる場合にはActionSheetを使います。


ViewController.h
@interface ViewController : UIViewController
<
    UIActionSheetDelegate
>
{
    UIActionSheet *aActionSheet;
}

ViewController.m
- (IBAction) doClick:(id) sender
{
    if (aActionSheet)
        return;

    aActionSheet = [[UIActionSheet alloc]
                                 initWithTitle:@"Type1"
                                 delegate:self
                                 cancelButtonTitle:@"cancel"
                                 destructiveButtonTitle:@"destructive"
                                 otherButtonTitles:@"button1", @"button2", nil];
    [aActionSheet showInView:self.view];
    [aActionSheet release];
}
- (void)actionSheet:(UIActionSheet *)actionSheet
         clickedButtonAtIndex:(NSInteger)buttonIndex
{
    [aLabelTitle setText:actionSheet.title];
    [aLabelButton
         setText:[NSString stringWithFormat:@"button%d", buttonIndex]];
}
- (void)actionSheet:(UIActionSheet *)actionSheet
         didDismissWithButtonIndex:(NSInteger)buttonIndex
{
    aActionSheet = nil;
}

iPadの場合はアクションシート表示させる際、下のようにした方がいいみたいです。
(これをiPhoneで見ると微妙に動きが違う。。。)
// iPhoneの場合
[aActionSheet showInView: self.view];

// iPadの場合
[aActionSheet showFromBarButtonItem: aButton animated:YES];

アクションシートに destructiveButtonTitle とありますがこれの使い方がよくわからずいろいろと調べてみたんですが、iOSヒューマンインターフェイスガイドラインを見ると
「害を及ぼす可能性のあるアクションを実行するボタンを提供する必要のある場合は、赤野ボタンを使用する」と記載があります。
。。。。。よくわかりませんでした。


機種(iPad/iPhone)の判別

アプリが現在動作している機種(iPad/iPhone)をソースコード内で判別するる場合はUI_USER_INTERFACE_IDIOMを使うと簡単に判別することができます。
swith (UI_USER_INTERFACE_IDIOM()) {
    case UIUserInterfaceIdiomPad:
        NSLog(@"iPad");
        break;
    case UIUserInterfaceIdiomPhone:
        NSLog(@"iPhone");
        break;
    default:
        break;
}

また下のようなやり方もできます。(こっちのほうが一般的?)
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) {
    NSLog(@"iPad");
} else {
    NSLog(@"iPhone");
}

iPad/iPhoneで処理を振り分けるときとかはいいかもしれません




ActionScriptで重なり順を制御

随時オブジェクトを追加していく場合など表示オブジェクト内の子オブジェクトの重なり順を変更したい場合はsetChildIndex()メソッドを使用します。
(スタイルシートで言うところのz-indexみたいなイメージ)
setChildIndex(child:DisplayObject, index:int):void
コンポーネントの順番を変更
getChildIndex(child:DisplayObject):int
コンポーネントの順番を取得
swapChildren(child1:DisplayObject, child2:DisplayObject):void
child1とchild2の順番を入れ替える
swapChildrenAt(index1:int, index2:int):void
表示オブジェクト内の2つのインデックス位置にあるコンポーネントを入れ替える

使い方はこんなかんじ
<mx:Script>
    <![CDATA[
        private function init():void {
            myParent.setChildIndex(myChild1, 1);
            myParent.setChildIndex(myChild2, 0);
        }
    ]]>
</mx:Script>
<mx:Canvas id="myParent" creationComplete="init();">
    <mx:Canvas id="myChild1" backgroundColor="0xff0000" x="50" y="50" width="100" height="100" />
    <mx:Canvas id="myChild2" backgroundColor="0x0000ff" x="100" y="100" width="100" height="100" />
</mx:Canvas>