Title

タブとナビゲーション(とテーブル)を同時に使った時に横画面にすると、縦方向が明らかに狭いので横にボタン持ってきてみた・・・。

写真 2013-08-20 1 03 42

本の閲覧画面のページスクロールする部分も元々はiPhone5の縦画面で余った画面用だったので、横にしたときも下でなくて横に表示するようにしましたが、同じようなイメージ。

stringByAddingPercentEscapesUsingEncoding(NSString)の使い道

URLをパーセントエンコーディングするとき、stringByAddingPercentEscapesUsingEncodingを使ってはいけないという記述をよく見ますが、用途次第です。

このメソッドは「$&+,/:;=?@」をエンコードしてくれません。
しかしこれが逆に必要な時もあって、具体的には非アスキーなURLをエンコードする場合です。

例えば以下のようなURLですね。
http://exmple.com/日本語/index.html

このURLはこのままだとNSURLなどに渡せません。
日本語部分をエスケープする必要があります。

しかしながら「/」や「:」までエスケープしてしまってはURLでなくなってしまいます。
stringByAddingPercentEscapesUsingEncodingはこのような時に使います。

「$&+,/:;=?@」は日本語まじりの場合でも必ずエスケープしないとURLの構成要素と区別がつきませんのでエスケープされているが、その他の非ASCII文字はエスケープされていないというような場合に利用できるわけですね。
どこで出現するかと言えば、NSURLConnectionなど使って通信しているとホスト側からリダクレイとされたりしたときに返ってくることがあります。

iPhone5対応

iPhone5とiOS6の地雷原から未だに完全に抜け出せていません、こんにちは。
しかしながら、ようやく解決の目処がついてきております。

iOS5.0のときほどではないにしても、今回もギャンブル性が高いアップデートでした。
なにせ解像度が変わることが正式に発表されてから2,3日以内にsubmitしないとiPhone5の発売に間に合わないというスケジュールでしたからネ。

次はiPad miniの噂も出てきていますがどうでしょうか。
電子書籍ビューアにとっては少し小さいタブレットは理想的だと思いますので、もし本当に発売されるのであれば、また地雷原が広がっているとしても果敢に早期対応する意気込みではあります・・。

iPad Retinaの時も実機が手元に来る前にアップデートしましたからね!
(この時は奇跡的?にうまくいきました)

Retina用@2xがついた画像から半分の解像度の画像を自動生成

iOSでは通常の画像(image.png)と、解像度が倍のRetina用画像(image@2x.png)を両方用意しないといけませんが、すごく面倒です。

そこでimage@2x.pngを入れるとimage.pngを自動生成するアプリケーションを作ってみました。
奇数の大きさを持つファイルや@2xが名前についていないファイルは何もしません。
また、既にファイルが存在している場合は上書きします。

remove@2x

縮小は4画素を平均して1画素を作る方法で計算しています(画素平均法)。
輝度の端数は切り捨てです。
png画像のみ対応。需要があればjpegも対応しますが、ひとまずこれで。

ダウンロード

何かありましたらコメントかtwitter(@rhotta)までお願いします。


MacOSでも@2xのついたファイルのscaleが2.0になってしまう件

MacOSX(Lion)上で、NSImage – initWithContentsOfFileで画像ファイルを読み込んだ時、ファイル名に@2xがついているとNSImage.sizeの大きさが半分になっていることがある怪現象に遭遇しました。
ルールはわかりませんが、@2xがついていても半分にならないときもあり、すごい謎。

iOSで使うUIImageならば、@2xが付いているとRetina用画像ということで解像度倍なのですが、MacOSにはRetinaという概念はないはずなので@2xというファイル名で何かが変わるのは変だと思うのですが・・・。

なお、下記のようなコードでCGImageからサイズを取得すると正しいサイズが取得できます。

+ (NSSize)getImageSize:(NSImage*)image{
    CGImageRef cgImage = [image CGImageForProposedRect:nil context:nil hints:nil];
    NSSize imageSize;
	imageSize.width = CGImageGetWidth(cgImage);
	imageSize.height = CGImageGetHeight(cgImage);
    return imageSize;
}

という事はつまり、UIImageで言うところのscaleが2.0になっている、という事なのでしょうが、NSImageにはscaleプロパティが無いのでわかりません。

噂のRetinaMacOSXへの布石なのか、ただのバグなのか、はたまた僕の理解不足なのかわかりませんが、とにかく謎です。
分かる方いらっしゃいましたら教えて下さい。

環境はMacOS SDK 10.7,MacOS Lionです。

いろいろな日付の計算(曜日・日付・日本の祝日)

カレンダーを作るにあたって必要な以下の点を考察してみます。
特に祝日の計算は日付クラスなんかを使っても出来ないことが多いので頑張りが必要です。
・日付→任意の曜日
・月が何日までか
・日本の祝日、国民の休日にあたるかどうか

まず任意の日付の曜日を得る方法。
これは簡単。有名なツェラーの公式を使います。

if (y*366+m*31+d < 1582*366+10*31+15) return -1;
return (y + y/4 - y/100 + y/400 + (13*m+8)/5 + d) % 7;

次に任意の月の最後の日(=日数)を計算する方法。
2月以外は固定なので、要するに閏年の計算になります。
閏年を4年に1度と勘違いしている場合がありますが、それだけだと誤差が蓄積するので以下のルールになっています。
1.西暦が4で割り切れる年
2.ただし、100で割り切れる場合は例外
3.ただし、400で割り切れる場合は2のルールは適用しない

int last;
switch(m){
	case 1:last = 31;break;
	case 2:
		//うるう年の算出
		if(y % 4 ==0){
			if(y % 100 == 0) {
				if(y % 400 == 0){
					last=29;		
				}else{
					last=28;
				}
			}else{
				last=29;
			}
		}else{
			last=28;
		}
		break;
	case 3:last = 31;break;
	case 4:last = 30;break;
	case 5:last = 31;break;
	case 6:last = 30;break;
	case 7:last = 31;break;
	case 8:last = 31;break;
	case 9:last = 30;break;
	case 10:last = 31;break;
	case 11:last = 30;break;
	case 12:last = 31;break;
	default: return 0;
}
return last;

最後に祝日・休日の計算方法について
今後制定される祝日については予測のしようがないので仕方が無いとして、既に制定されている祝日について考えます。

日付が固定されている祝日は楽ですが、ハッピーマンデーや振り替え休日がありますので注意が必要です。
また2007年の法律改正により、振替休日のルールが若干複雑になっています。

また、春分・秋分の日については法令としては前年になるまで確定しませんが、天文学的な春分日・秋分日と違う日になることはまず無いと推測できるので、計算により導いてみます。

以下は春分と秋分日の近似計算です。西暦1851年~2150年の間で利用できます。

//春分(3月)
int GetVernalEquinoxDay()
{

	int y = m_Systime.wYear;
	int  d;
	if(y<1851) return -1;
	else if(y < 1900){
		d = (int)(19.8277 + 0.242194 * (double)(y-1980) - ((y-1983)/4));
	}else if(y < 1980){
		d = (int)(20.8357 + 0.242194 * (double)(y-1980) - ((y-1983)/4));
	}else if(y < 2100){
		d = (int)(20.8431 + 0.242194 * (double)(y-1980) - ((y-1980)/4));
	}else if(y < 2151){
		d = (int)(21.8510 + 0.242194 * (double)(y-1980) - ((y-1980)/4));
	}else return -1;
	return d;
}

//秋分(9月)
int GetAutumnalEquinoxDay()
{

	int y = m_Systime.wYear;
	int  d;
	if(y<1851) return -1;
	else if(y < 1900){
		d = (int)(22.2588 + 0.242194 * (double)(y-1980) - ((y-1983)/4));
	}else if(y < 1980){
		d = (int)(23.2588 + 0.242194 * (double)(y-1980) - ((y-1983)/4));
	}else if(y < 2100){
		d = (int)(23.2488 + 0.242194 * (double)(y-1980) - ((y-1980)/4));
	}else if(y < 2151){
		d = (int)(24.2488 + 0.242194 * (double)(y-1980) - ((y-1980)/4));
	}else return -1;
	return d;
}

最後に祝日です。 現在と2007年の改定前の祝日を拾うようにしてみました。 これに加えて、振替休日を考慮する必要があります。 2007より前は日曜日に重なった場合は月曜日を休日に、 2007年以降は「その日後においてその日に最も近い「国民の祝日」でない日」が休日です。


int c = (d-1) / 7 + 1;
if(m==9)
	autumnalEquinoxDay =GetAutumnalEquinoxDay();
if(m==3)
	vernalEquinoxDay = GetVernalEquinoxDay();
if(m==1 && d==1){
	return DAY_NEWYEARSDAY;
}else if((y >= 1949 && y < 2000) &&  m==1 && d==15){
	//1949-1999 1月、15日
	return DAY_SEIJINNOHI;
}else if((y >= 2000) &&  m==1 && w==1 && c == 2){
	//2000~、1月、月曜日、第2
	return DAY_SEIJINNOHI;
}else if(y>= 1967 && m==2 && d==11){
	//1967~、2月、11日
	return DAY_KENKOKUKINENBI;
}else if(y>= 1949 && y<1989 && m==4 && d==29){
	//1949~1988、4月、29日
	return DAY_TENNOUTANJOBI;
}else if(y>=1989 && m==4 && d==29){
	if(y>=2007) return DAY_SHOWANOHI;
	//1989~、4月、29日
	return DAY_MIDORINOHI;
}else if(y>=1949 && m==5 && d==3){
	//1949~、5月、3日
	return DAY_KENPOUKINENBI;
}else if(y>=1986 && m==5 && d==4){
	if(y>=2007) return DAY_MIDORINOHI;
	//1986~、5月、4日
	return DAY_KOKUMINNOKYUJITU5;
}else if(y>=1949 && m==5 && d==5){
	//1949~、5月、5日
	return DAY_KODOMONOHI;
}else if(y>=1996  && y<2003  && m==7 && d==20){
	//1949~2002、7月、20日
	return DAY_UMINOHI;
}else if(y>=2003 && m==7 && w==1 &&c==3){
	//2003~、7月、月曜、第3
	return DAY_UMINOHI;
}else if(y>=1966  && y<2003  && m==9 && d==15){
	//1966~2002、9月、15日
	return DAY_KEIROUNOHI;
}else if(y>=2003 && m==9 && w==1 &&c==3){
	//2003~、9月、月曜、第3
	return DAY_KEIROUNOHI;
}else if(y>=1966  && y<2003  && m==10 && d==10){
	//1966~2002、10月、10日
	return DAY_TAIKUNOHI;
}else if(y>=2003 && m==10 && w==1 &&c==2){
	//2003~、10月、月曜、第2
	return DAY_TAIKUNOHI;
}else if(y>=1948 && m==11 && d==3){
	//1948~、11月、3日
	return DAY_BUNKANOHI;
}else if(y>=1948 && m==11 && d==23){
	//1948~、11月、23日
	return DAY_KINROUKANSYANOHI;
}else if(y>=1989 && m==12 && d==23){
	//1989~、11月、23日
	return DAY_TENNOUTANJOBI2;
}else if(y>=2003 && m==9 && GetNumOfWeek(d-1)==3 && w-1 == 1 
	&& (d+1)==m_AutumnalEquinoxDay){
	//2003~、9月、前日が第3月曜日、翌日が秋分の日
	return DAY_KOKUMINNOKYUJITU9;
}else if(m==3 && d==m_VernalEquinoxDay){
	//春分
	return DAY_SYUNBUNNOHI;
}else if(m==9 && d==autumnalEquinoxDay){
	//秋分
	return DAY_SYUUBUNNOHI;
}

accelerometer:didAccelerate:がDeprecatedになっている

加速度センサを使うアプリを新たに作っています。
ふとドキュメントを見たら、accelerometer:didAccelerate:が、(Deprecated in iOS 5.0.)になっていました。

代替は手段はどうするんだろうって悩んだのでメモ。

    motionManager_ = [[CMMotionManager alloc] init];
    motionManager_.accelerometerUpdateInterval = 1.0/60.0;

    NSOperationQueue *currentQueue = [NSOperationQueue currentQueue];
    accelerationValueFirst_ = YES;

    [motionManager_ startAccelerometerUpdatesToQueue:currentQueue
            withHandler:^(CMAccelerometerData *accelerometerData, NSError *error)
    {
        CMAcceleration acceleration = accelerometerData.acceleration;

        UIAccelerationValue x = acceleration.x;
        UIAccelerationValue y = acceleration.y;
        UIAccelerationValue z = acceleration.z;
        //お好きな処理
	}

ブロックを使う方法が推奨されているようです。
・・・と思いきやbeginAnimations:context:はDeprecatedになっていませんね。

CMMotionManagerはiOS4.0から利用できるようです。

WindowsでのUnicode合成文字問題

Unicodeでは同じ文字を表すのに複数のコードが存在します。
具体的には合成文字(precomposed character)があります。
例えば「が」のような文字を「か」+「゛」で表す方法と「が」そのものとして表す方法があります。
よって、等価な文字列でもコードは異なるという事があり得ます。

Windowsでは基本的に合成文字は使われないようです。
一方iOSでは合成文字が使われます。

iOSではファイル名を扱おうとすると、自動的にUnicodeの正規化が行われますので、あまり意識しなくても等価な文字列は等価なコードになります。
一方でWindowsではそれが行われないようです。

つまり、Windows上では「こんばんは.txt」という全く同じファイル名のファイルが同じフォルダに複数同時に存在出来ます。
(表示上は全く同じだが、実はコードが違う)
プログラムをする上でこの問題は非常にやっかいです。

MultiByteToWideChar等のAPIで正規化が出来ればいいのですが、何故か出来ません。
(dwFlagsの説明を見るとすごく出来そうなのにやってみると何故かできない)

iOSのファイル名をなんとかWindows側に持ってこれないかと、色々捏ねくりまわしていたら以下の方法で出来たので貼っておきます。

非常に回りくどい事をしていて正しいのかどうかは不明です。。もっと良い方法やご意見ありましたら是非コメントを。

(14/03/21追記)
Windows Vista以降では、NormalizeStringというそのままズバリのAPIがあるようです。
(追記ここまで)

//MultiByte(主にUTF-8)をNFC正規化したワイド文字列に変換する
int MultiByteToWideCharNFC(
  UINT CodePage,         // コードページ = CP_UTF8を指定
  DWORD dwFlags,         // 文字の種類を指定するフラグ = NULL を指定
  LPCSTR lpMultiByteStr, // 変換する文字列
  int cchMultiByte,      // 変換する文字列のバイト数。-1で省略可
  LPWSTR lpWideCharStr,  // 変換先のバッファ
  int cchWideChar        // バッファのサイズ(バイト数でなく文字数)
  )
{

	//一旦WideCharに変換
	int lengthWideChar = MultiByteToWideChar(CodePage,dwFlags,lpMultiByteStr,cchMultiByte,NULL,0);
	if(!lengthWideChar){
		return 0;
	}
	TCHAR* wideTemp = new TCHAR[lengthWideChar];

	//TCHAR wBufferTemp[_MAX_PATH*10];
	if(MultiByteToWideChar(CodePage,dwFlags,lpMultiByteStr,cchMultiByte,wideTemp,lengthWideChar) ==0){
		delete[] wideTemp;
		return 0;
	}

	//一度MultiByteに戻す、同時に合成文字をNFC正規化
	int lengthMultiByte = WideCharToMultiByte(CP_ACP,WC_COMPOSITECHECK,wideTemp,-1,NULL,0,NULL,NULL);
	if(!lengthMultiByte){
		delete[] wideTemp;
		return MultiByteToWideChar(CodePage,dwFlags,lpMultiByteStr,cchMultiByte,lpWideCharStr,cchWideChar);
	}

	char* multibyteTemp = new char[lengthMultiByte];


	if(WideCharToMultiByte(CP_ACP,WC_COMPOSITECHECK,wideTemp,-1,multibyteTemp,lengthMultiByte,NULL,NULL) ==0){
		delete[] wideTemp;
		delete[] multibyteTemp;
		return MultiByteToWideChar(CodePage,dwFlags,lpMultiByteStr,cchMultiByte,lpWideCharStr,cchWideChar);
	}


	//WideCharに戻す
	int result = MultiByteToWideChar(CP_ACP,0,multibyteTemp,-1,lpWideCharStr,cchWideChar);
	delete[] wideTemp;
	delete[] multibyteTemp;
	return result;
}

iPhoneアプリプログラミング最初の1歩

iOSのプログラミングはObjectie-Cで行います。
C言語や他の環境のGUIプログラミングの経験があるという前提で最初に一番理解し難いのはメモリの管理だと思います。

最新のObjective-Cではガーベジコレクションといった便利な機能が利用できますがiOSでは利用できません。
よって設計者が自前でメモリ管理をしないといけません。

retain,release,alloc,deallocの使い分けが最初さっぱりわからないんですよね。

最初疑問が解決しにくかった思い出を元にヒントを箇条書きにしています。

・allocすると実体が確保され、参照カウントが1になる。
・retainすると参照カウントが1上がり、releaseすると1下がる
・参照カウントが0になると解放される

(ここまでは良くある説明で分かりやすいが、さらに初心者を苦しめるのが以下の機能)

・autorelease poolの中でautoreleaseをすると、autorelease poolが解放されるときに自動的にreleaseが呼ばれる
・autorelease poolはイベントサイクルごとに自動的に処理されているので、イベントの処理の中でautoreleaseしておけばとりあえずその処理が終わった後にreleaseされると思っていい。
・OSのAPIの中には実体を確保した上でautoreleaseされてオブジェクトが返されるものがある。
 見分け方はオブジェクトっぽい名前で始まっていてクラスメソッド(+で始まるやつ)になっているもの。
・よって、イベント処理後も残したいならばretainしておかないと自動的に解放されてしまう。
・逆にautoreleaseされている状態なのにreleaseで参照カウントを0にしてしまうとイベント処理後にクラッシュする。

カレンダー

2024年4月
1234567
891011121314
15161718192021
22232425262728
2930  

▲Pagetop