UILabel

文字を折り返す

複数行に渡って表示する場合は、UILabelのnumberOfLinesプロパティを変更する。無制限の場合はゼロを設定する

textLabel.numberOfLines = 0;

折り返すときのモードも設定できる

textLabel.lineBreakMode = UILineBreakModeCharacterWrap;

UITableViewの使い方

テーブルデータの再読み込み

データの更新などを行った後、スレッド内で単に

tableView.reloadData()
[(UITableView)tableView reloadData]

とやると落ちてしまう。そこで以下のようなコードを記述することによって、データの再読み込み処理をメインスレッドに戻して処理させるようにする。

dispatch_async(dispatch_get_main_queue(), { () -> Void in
    self.tableView.reloadData()
})
[(UITableView)tableView performSelectorOnMainThread:@selector(reloadData) withObject:nil waitUntilDone:NO];

また、下記のようにUserDefaultのデータをCellにデータを反映させる場合に、

@IBOutlet weak var label: UILabel!

override func viewWillAppear(animated: Bool) {
        let defaults = NSUserDefaults.standardUserDefaults()
        label.text = defaults.valueForKey("label")! as? String
}

viewWillAppear()でreloadData()しただけでは、うまくテーブルが更新されないので、

override func viewWillAppear(animated: Bool) {
        let defaults = NSUserDefaults.standardUserDefaults()
        label.text = defaults.valueForKey("label")! as? String
        tableView.reloadData()
}

viewDidAppear()でもreloadData()する必要がある。

    override func viewDidAppear(animated: Bool) {
        tableView.reloadData()
    }

セルのハイライトを解除する

UITableViewControllerを使用しない場合、View遷移時のセルハイライトの解除を自分で記述する必要がある

tableView.deselectRowAtIndexPath(tableView.indexPathForSelectedRow, animated: true)
[(UITableView)tableView deselectRowAtIndexPath:[(UITableView)tableView indexPathForSelectedRow] animated:YES];

セル選択時にハイライト表示しない

セルを選択した場合もハイライト表示としない

    func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as UITableViewCell
        cell.selectionStyle = UITableViewCellSelectionStyle.None
        return cell
    }

    func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
        tableView.deselectRowAtIndexPath(indexPath, animated: true)
    }
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
	// セル選択時のスタイルを無表示スタイルに設定する
	cell.selectionStyle = UITableViewCellSelectionStyleNone;
}

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
	// セル選択時も非選択状態とする
	[tableView deselectRowAtIndexPath:indexPath animated:YES];
}

セル選択不可

UITableView内全てのセルを選択不可にする

tableView.allowsSelection = false
[(UITableView)tableview setAllowsSelection:NO];

ヘッダビューを追加する

UITableView上部にヘッダビューを追加する

tableView.tableHeaderView = view
(UITableView)tableView.tableHeaderView = (UIView)view;

追加するViewは、ViewcontrollerのサブViewにしない。
文字数に合わせたセルの高さを設定する

セルの高さを設定する

  • テーブル内のUILabelの文字数に合わせてセルの高さを指定する
    - (CGFloat)tableView:(UITableView*)tableView heightForRowAtIndexPath:(NSIndexPath*)indexPath  
    {
    	// セルの取得
    	UITableViewCell *cell = [self tableView:_tableView cellForRowAtIndexPath:indexPath];  
    	// 最大サイズ
    	CGSize bounds = CGSizeMake(_tableView.frame.size.width, _tableView.frame.size.height);
    	// ラベルサイズ
    	CGSize size = [cell.textLabel.text sizeWithFont: cell.textLabel.font
    	constrainedToSize: bounds
    	lineBreakMode: UILineBreakModeCharacterWrap];
    	return size.height;
    }
  • セルの高さを自動設定する
    // とりあえず高さを指定
    tableView.estimatedRowHeight = 100
    // 高さを自動指定
    tableView.rowHeight = UITableViewAutomaticDimension

プロトタイプセルに含まれる子ビューを指定する

Storyboard上で設定したPrototypeCellに含まれるUIImageViewやUILabelをコード上から指定する場合、それぞれのViewにラベルを付与しておくことで、ラベル番号を指定してViewを取得することが可能である。

Prototype Cells

プロトタイプセル上にUIImageViewを作成し、Tag=1を指定している。

Prototype Cells Property

上記の例で、UIImageViewにSwift上からアクセスする場合は、

    func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as UITableViewCell
        let imageView: UIImageView = cell.viewWithTag(1) as! UIImageView
        return cell
    }

とすれば良い。

Objective-Cのデザインパターン

クラスクラスタ

あるスーパークラスから派生したサブクラスが多数存在する場合、それぞれのサブクラスをダイレクトに利用しようとするとクラスの数が膨大になり利用しにくい。そこでサブクラスを非公開とし、サブクラスへのアクセスは公開されたスーパークラスへのアクセスによって実現する。これがクラスクラスタである。サブクラスはスーパークラスより生成されるが非公開であるため、外からはスーパークラスのインスタンスとして見える。

クラスクラスタに属するサブクラス

クラスクラスタは簡明さと拡張性のトレードオフである。拡張を必要とする場合はサブクラスを別途作成する必要がある。サブクラスを定義する際は以下のように作成する。

  • イニシャライザを全てオーバライドする
  • プリミティブメソッド(最低限実装しなければならないメソッド)をオーバライドする

他の派生メソッドはオーバライドしなくても使用することができるが、挙動を変更する場合はオーバライドして実装する。

ファクトリメソッド

ファクトリーメソッドとはクラスメソッド、つまりインスタンス化することなく使用することのできるメソッドである。

アウトレット

(ViewControllerなどの)オブジェクトのプロパティ(パーツ)のうち、(Storyboardなど)ほかのオブジェクトを参照するもの。Storyboadを読み込む際に接続が確立しなおされる。メモリ管理の観点より、他の設定項目と同様にクラスに含めるべき項目なのかどうか検討が必要である。

@propety(weak, nonatmic) IBOutlet UILabel *label;

オブジェクトの割り当てと初期化

オブジェクトは、alloc()メソッドにより仮想メモリ領域からオブジェクトを格納するために必要なメモリ量の割り当てを受け、このあとinit()メソッドによりインスタンス変数を初期化する。シングルトンインスタンスの場合、新たなインスタンスの生成を要求されても、既存のインスタンスを返すような初期化処理を実装しておく必要がある。返り値がnilの場合、初期化に失敗したことを示す。

UIView

アクションシート

デリゲートの記述を行う。

@interface MyViewController : UIViewController<UIActionSheetDelegate>{

アクションシートの初期化と貼付けを行う。初期化の際にデリゲートの宣言をする。

// 初期化(+デリゲートの設定)
UIActionSheet *aSheet = [[UIActionSheet alloc]
initWithTitle:@"選択項目"
delegate:self
cancelButtonTitle:@"キャンセル"
destructiveButtonTitle:nil
otherButtonTitles:@"ボタン1", @"ボタン2", @"ボタン3",nil];
// アクションシートのスタイルを指定
[aSheet setActionSheetStyle:UIActionSheetStyleDefault];
// ビューに貼付ける
[aSheet showInView:[self view]];
// メモリの解放
[aSheet release];

デリゲートメソッド内で処理を記述する

- (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex {
}

NavigationController

Xcodeのプロジェクト設定でNavigationControllerを選択した場合、RootViewController.xibとMainWindow.xibが作成される。MainWindowではUINavigationControllerが読み込まれており、このUINavigationControllerの中に上部のNavigation Barと下部のRootViewControllerがある。下部のRootViewControllerは、UITableViewControllerを継承して作成されており、テーブル状のメニュー画面を作成することが出来る。

この画面をタッチすると別のサブウインドウに移動できるようにするためには、

// NIBファイルを指定してビューコントローラを定義する
DetailViewController *dvc = [[DetailViewController alloc] initWithNibName:@"DetailViewController" bundle:nil];
// メニューを押すと上記ビューコントローラを表示する
[self.navigationController pushViewController:dvc animated:YES];
[dvc release];

とする。

イメージの取得と保存

イメージを取得する

カメラやアルバムからイメージを取得するにはUIImagePickerControllerを使用する。

// 使用できるか出来ないかの判定は、isSourceTypeAvailableメソッドを使う
if ( [UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypePhotoLibrary] ) {
	// UIImageControllerの初期化
	UIImagePickerController *ipc = [[UIImagePickerController alloc] init];
	/* UIImageControllerで取得できるデータは以下の3つ
	 *
	 * SourceTypeCamera             : カメラを起動する
	 * SourceTypePhotoLibrary       : フォトライブラリーを開く
	 * SourceTypeSavedPhotosAlbum   : (カメラがあるiPhoneは)カメラロールから選択する
	 */
	[ipc setSourceType:UIImagePickerControllerSourceTypePhotoLibrary];
	// Delegateをセット
	[ipc setDelegate:self];
	// NOを設定するとイメージを取得できない
	[ipc setAllowsEditing:YES];
	// 指定したViewを一番上に表示する
	[self presentModalViewController:ipc animated:YES];
	// 表示してしまった後はメモリから消去してよい
	[ipc release];
}

イメージを表示する

UIImagePickerControllerのイメージを取得した後に呼び出されるデリゲート関数(didFinishPickingMediaWithInfo)に、取得イメージをImageViewに表示する処理を追記する。UIImagePickerControllerをデリゲートする場合は、UIImagePickerControllerDelegateとUINavigationControllerDelegateをヘッダファイルに記述する必要がある。

- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info {
	// 選択したイメージをUIImageにセットする
	UIImage *image = [info objectForKey:UIImagePickerControllerEditedImage];
	// UIImageをImageViewerにセットする
	[imageView setImage:image];
	// [picker parentViewController]の部分はselfでも良い?
	[[picker parentViewController] dismissModalViewControllerAnimated:YES];
}

イメージを保存する

ImageViewに表示されたイメージをフォトアルバムに保存する。イメージを端末内に保存される前にアプリを終了してしまうと、サムネイルだけが保存されイメージが保存されないので、保存終了後にアラートを表示させるようにする。

UIImage *image = [aImageView image];
if ( image != nil ) {
	// イメージを保存する
	UIImageWriteToSavedPhotosAlbum( image, self, @selector(image:didFinishSavingWithError:contextInfo:), nil );
}

アラート表示部分は以下のように記述する。

// アラートを表示させる部分は、image:didFinishSavingWithError:contextInfo:という書式が必須らしい
- (void)image:(UIImage *)image didFinishSavingWithError:(NSError *)error contextInfo:(void *)contextInfo {
	// アラートを表示する
	UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"保存終了" message:@"画像を保存しました" delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil];
	[alert show];
	[alert release];
}

アプリ内部(アプリのホームディレクトリ内)に画像を保存する場合は、以下のように記述する。

UIImage *image = [aImageView image];
if ( image != nil ) {
	// 保存ディレクトリを指定
	NSString* docDir = [NSHomeDirectory() stringByAppendingPathComponent:@"Documents"];
	// 保存ディレクトリ+ファイル名を指定
	NSString* photoFilePath = [NSString stringWithFormat:@"%@/photo.jpg", docDir];
	// 圧縮率を指定しJPEGファイルで保存する
	[stringWithFormat(image, 0.7f) writeToFile:photoFilePath atomically:YES];
}

保存ディレクトリの指定には、文字列追加を行うことのできるstringByAppendingPathComponentメソッドを使用する。またファイル名の指定には、フォーマットを指定した文字列記述のできるstringWithFormatを使用する。最後にUIImageJPEGRepresentatio関数によって画像データをNSデータ型に変換したあと指定した保存先に保存を行う。

デリゲートとは

デリゲートとは、本来他のクラスのメソッドを用いて処理すべき内容を自分のクラス内で処理できるように、他のクラスのメソッドを自分のクラス内で呼び出して利用することを言う。例えば、ボタンを押せば録音を行うというプログラムで、録音が終了した際にひと手間処理を入れたい場合、本来であれば録音を行うクラスの録音終了メソッド内で処理が行われるが、デリゲートを宣言することでViewController上でこの録音終了処理を行うことが出来る。つまり、本来他のクラスがやるべき仕事を委譲されて自分自身で処理できるようになる。

まず、

@interface MyViewController : UIViewController <AVAudioRecorderDelegate>
{

と書くとこのViewControllerでAVAudioRecorderを代行すると宣言したことになる。次に処理を代行する部分をプログラムに明示する。例えば、

// 録音を行うクラス
AVAudioRecorder *recorder = [[AVAudioRecorder alloc] initWithURL:soundURL settings:nil error:&error];
// 各処理を記述
// self=MyViewControllerにrecorderのデリゲートをセットする
[recorder setDelegate:self];

と書くと、recorderが行う処理の一部をMyViewControllerが代行することが出来る。代行する部分の処理は、

- (void)audioRecorderDidFinishRecording:(AVAudioRecorder *)recorder successfully:(BOOL)flag {
}

などの部分に記述する。各クラスはどのメソッドをデリゲートで使って良いか定義している。デリゲートしたクラスのうち使用出来るメソッドのみ処理を代行できる。

画面の向きを固定する

info.plistを編集する

info.plistにinitial interface orientationの項目を追加し、起動時の画面の向きを設定する。

  • Portrait (bottom home button)
  • Portrait (top home button)
  • Landscape (left home button)
  • Landscape (right home button)

ステータスバーを表示させない場合は、status bar is initialy hiddenの項目も追加する。

IBでUIの構築を行う

Viewの右上の矢印をクリックするとViewの向きを変えることが出来る。横向きの画面を作成する場合は、Viewの向きを変えてから各パーツを貼付ける。ステータスバーを表示させない場合はViewのAttributesを開き、status barをNoneとする。ついでにViewの画面サイズも480×320に変更する。

画面の向きを固定する

UIViewControllerで定義されているshouldAutorotateToInterfaceOrientationというメソッド(コメントアウトされた状態で記述されている)を使い回転を許可する向きを定めることが出来る。固定したい向きを記述することでその向きのときだけ回転する、すなわちその向きに画面が固定される。

// 現在の向きがあらかじめ規定した向きと合致するとYESを返す
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
	// YESを返すときのみ回転を許可する
	return (interfaceOrientation == UIInterfaceOrientationLandscapeRight);
}

InterfaceBuilderとイベント処理

IBで作成したオブジェクトを参照する

プログラムは、IBOutletを宣言したインスタンス変数(アウトレット)を通して、IBで作成したオブジェクトを参照することができる。プログラム側のヘッダファイルで以下のようにアウトレットを宣言する。

#import <UIKit/UIKit.h>

@interface DigitalClock1ViewController : UIViewController {
	// インスタンス変数の宣言
	UILabel *label;
}
@property(nonatomic, retain) IBOutlet UILabel *label;
@end

@propertyで記述されたアクセサに関しては、@implementation部に以下の記述が必要。

@synthesize label;

アウトレットがIB上のどのパーツを参照するのかはInterfaceBuilderで規定する。File’s Ownerを右クリックして先ほど宣言したアウトレットを選択し、これをView上の任意のパーツまでドラッグすることで、アウトレットとパーツとの参照関係が規定される。

オブジェクトに対するアクションを受け取る

ボタンが押された等のアクションをプログラム側で受け取る場合は、アクションを受け取るメソッドにIBActionを宣言する。プログラム側のヘッダファイルで以下のようにIBActionメソッドを宣言する。

#import <UIKit/UIKit.h>

@interface DigitalClock1ViewController : UIViewController {
}

- (IBAction) doKeyDown: (id) sender;

@end

また、@implementationにてメソッドを実装する。

- (IBAction) doKeyDown: (id) sender{
	// sender tagでアクションの発生したオブジェクトを特定することが可能
	NSLog:@"%d", [sender tag];
}

IBActionがIB上のどのパーツのアクションを受け取るのかはInterfaceBuilderで規定する。File’s Ownerを右クリックして先ほど宣言したメソッドを選択し、これをView上の任意のパーツまでドラッグすることで、IBActuionとパーツとの参照関係が規定される。

現在時刻を表示する

タイマーを用いて定期的に実行する

定期的にメソッドを実行するにはNSTimerクラスを利用する。

NSTimer *timer;

まずヘッダファイルでタイマーを定義する。タイマー処理はscheduledTimerWithTimeIntervalを用いる。

timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(tick:) userInfo:nil repeats:YES];

引数は、①呼び出し間隔(秒)②タイマー発火時に呼び出すメソッドのあるオブジェクト③タイマー発火時に呼び出すメソッド④タイマー発火時に呼び出すメソッドに渡す情報⑤繰り返しをするか否かの5項目。@selectorは、メソッド名を記述することでそのメソッドを呼び出すことができる。Objective-Cでは、Selectorを用いることで実行時に動的に呼び出すメソッドを変更できるとのこと。

- (void)viewDidUnload {
	[timer invalidate];
	timer = nil;
}
- (void)dealloc {
	if ( timer)
	[timer invalidate];
	[super dealloc];
}

なおタイマーを停止する際は、invalidateメソッドを使用する。invalidateはreleaseまでしてくれる。

現在時刻を表示する

最後に定期的に呼び出すメソッド内に現在の時刻を取得し、必要な形に整形する処理を記述する。

- (void)tick:(NSTimer *)theTimer {
	// 初期化(init)し現在の時刻を取得する
	NSDate *date = [NSDate date];
	// NSDateFomatter=NSDateの値を整形する
	NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
	[formatter setLocale:[NSLocale currentLocale]];
	// 年月日曜日を全て表示する
	[formatter setDateStyle:NSDateFormatterFullStyle];
	// 時分秒タイムゾーンを全て表示する
	[formatter setTimeStyle:NSDateFormatterFullStyle];
	// Date型からString型に置換する
	NSString *dateStr = [formatter stringFromDate:date];
	// allocしたformatterをreleaseする
	// なおClassObjectである*dateは自動的にメモリ解放されるので記述は不要
	[formatter release];
}

引数のtheTimerは、呼び出し元のタイマー自身を示す。

SyntaxHighlighter+MovableType

SyntaxHighlighterをダウンロード

https://blog.makotokw.com/portfolio/movabletype/syntaxhighlighter/よりプラグインをダウンロードする。zip形式なので解凍。

Movable Type上に配置

解凍するとmt-static, pluginsの2つのファイルが生成されるので、MovableType上の同じ位置にコピーする。
+ plugins\SyntaxHighlighter
+ mt-static\plugins\SyntaxHighlighter

プラグインの修正

MovableTypeの[デザイン]->[テンプレート]から適当なテンプレートを選択し、HTMLのヘッダ部分に以下のタグを埋め込む。タグをヘッダに記述することで、各フォーマットに合わせた表示部分のjavascriptやcssを呼び出す記述がHTMLに加えられる。

<$mt:SyntaxHighlighterInclude brush="csharp,cpp,jscript,xml,java,php,sql,objc" theme="default"$>
<$mt:CCLicenseRDF$>

brushの部分が表示したいテキストのフォーマット。このjavascriptやcssを任意の場所に置きたい場合は、plugins\SyntaxHighlighter\lib\SyntaxHighlighter\Plugin.pmを書き換える必要がある。

ブログを書く

フォーマットで、SyntaxHighlighterかSyntaxHighlighter + Convert Line Breaksを選び、ブログ本文を記述する。コード部分を以下のタグで囲むことでプラグイン側でコードと認識され、整形されて表示が行われる。

標準外のフォーマットに対応させる

標準で対応してるフォーマットは、XMLやC+、phpなど数少ない。他のフォーマットにも対応させる場合は、https://www.undermyhat.org/blog/2009/09/list-of-brushes-syntaxhighligher/より必要な.jsファイルをダウンロードし、配置する。