2013年6月20日木曜日

iPadでresignFirstResponderが機能しない



iPadでUIModalPresentationFormSheetを使ってModal Viewを表示した場合、UITextFiledのタップでキーボードを表示したあと、textFieldShouldReturn:でresignFirstResponderを行ってもキーボードが消えないという現象があります。
続けて入力を行う可能性を考慮し、ひとつのコントロールがresignFirstResponderしてもいちいちキーボードを隠さないという仕様だそうです。
それでも直ぐにキーボードを消したい場合はUIViewControllerに次のようなコードを追加します。

- (BOOL)disablesAutomaticKeyboardDismissal {
    return NO;
}

iPadでは画面構成によってはUIViewControllerがいくつも登場するため、どれにしかければ有効なのか悩まされました。
Modal Viewの場合はそれを表示するUINavigationControllerをサブクラス化し、それに上記メソッドを実装すると解決します。

参照:resignFirstResponder Don't work?

2013年6月14日金曜日

All tests did not finish



Xcodeでテストを実行すると"All tests did not finish"と警告がでる。
Application Testの場合、Application起動中にTestが終了してまうのが原因だそうで、書き込みどおりsleepさせてみたら警告がでなくなった。
Some of my unit tests tests are not finishing in XCode 4.4

ということで、テスト項目が少ないときにこの警告が出ても気にしなくてよいようです。

2013年6月10日月曜日

Base Localizationの追加


古いバージョンで作ったプロジェクトにBase Localizationの追加する手順

Projectを選択
Localizationの"Use Base Internationalization"をクリック。

ベースとなる言語を選択

Finishをクリック。


------------------------------
メニューからの操作する場合
 PROJECTを選択
 Editorメニューの"Add Localization"をクリック
 言語選択サブメニューの"Other" > "Base(Base)"をクリック
------------------------------

Base.lprojが追加され、en.lprojフォルダからstoryboardが削除され
infoPlist.stringsだけになる。

同様にEditorメニューの"Add Localization"で"Japanese"を追加する。
ja.lprojが追加される。
MainStoryboard.strings、Localizable.stringsを追加する。
MainStoryboardの部分はStoryboardと一致させる。
StoryboardのUI部品はMainStoryboard.stringsに、NSLocalisedStringで参照するものはLocalizable.stringファイルに設定する。
MainStoryboard.stringsは後述のibtool で作成するものでよい。

storyboadへUI部品を追加したときのstringsファイルの更新
Terminalを起動
Base.jprojへ移動
ibtool MainStoryboard.storyboard --generate-strings-file NewStuff.strings
NewStuff.stringsを開き、追加された行をMainStoryoard.stringsへコピーする。

毎回コマンドを入力するのは面倒なので、コマンドのaliasを作っておくと便利です。
alias設定の方法は"mac osx terminal alias"などで検索してみてください。
例:
alias newstrings="cd (プロジェクトのパス)/Base.lproj;lbtool Mainstoryboard_iPhone.storyboard --generate-string-file new.strings"

参照:
Internationalize Your App


2013年6月9日日曜日

iOS WorkspaceでFramework(Library) Projectを作る方法



XCodeでWoekspaceを作り、Applicarion ProjectとFrameowork Projectを関連づける方法です。
検索するといくつも出てはきますが、バージョンによって手順が異なっており、XCode 4.6.1にズバりのものが見つかりません。
一番近かったのはIntroduction to Using Static Libraries in iOSで、これは既存プロジェクトからFrameworkを分離する方法ですが、これに沿った手順で次の要領で新規に作ることができます。
  1. 新規Workspaceを作る。


  2. Frameworkプロジェクトを作る。

    Workspaceに追加する前にプロジェクトを閉じる。
  3. WorkspaceにFrameworkプロジェクトの.xcodeprojを追加する。
    File>New>またはWorkspaceの左ペイン右クリックからAdd FIles to ...をクリック。
     

    同様にApplicationプロジェクトの.xcodeprojを追加する。
  4. Workspaceで各々のプロジェクト内のtargetを選択、Workspaceでbuildする。
    正しくbuildされれるとTestFrameworkの下のproduct内のlibTestFramework.aの赤字が黒に変わる。赤のままのときはWorkspaceのProject選択をチェック。もし別にTestFrameworkプリジェクトが開いたら閉じる。
  5. Workspace でApplicationプロジェクトのtargetを選択。Build Phase>Link Binary With LibrariesでFrameworkの.aを追加する。



  6. Framework内のクラスを使うファイルに#importを追加。
    "TestFramework/TestFramework.h"
    "TestFramework"はtargetの"Product Name"に設定されている名前。"TestFramework.h"は使用するクラスのヘッダファイル。

  7. Build Phases 公開するヘッダの設定
    プロジェクト作成時に追加されるヘッダは予め含まれているが、その後プロジェクトに追加するヘッダファイルを公開する場合は、手動で追加する。
Framework Project のBuild Setting リンカオプション(-ObjC)は自動的に設定されますが、Application Projectには設定されていません。このままでも問題に場合が多いと思いますが、Framework に Category が含まれている場合はApplication Projectにもこのオプションを追加する必要があります

 "selector not recognized" が発生する場合は多分これが原因です。

Building Objective-C static libraries with categories



2013年6月8日土曜日

uncaught exceptionのstackのアドレスをシンボル表示する方法



たまにstackのアドレスだけがありシンボル表示がなく、どこで発生しているエラーかわからないメッセージで終了することがあります。

エラーメッセージの例:
2013-06-08 14:27:49.160 Test[29884:11303] *** Terminating app due to uncaught exception 'NSRangeException', reason: '*** -[__NSArrayM removeObjectAtIndex:]: index 1 beyond bounds [0 .. 0]'
*** First throw call stack:

(0x1c90012 0x10cde7e 0x1c321c4 0x20bb 0xacb5b3 0x1c4f376 0x1c4ee06 0x1c36a82 0x1c35f44 0x1c35e1b 0x1bea7e3 0x1bea668 0x11ffc 0x1b4d 0x1a75)

こんな場合にuncaught exceptionの内容を表示する方法が紹介されていました。
Xcode 4.2 debug doesn't symbolicate stack call

次のコードはこのページからの引用です。
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions

{
    .....
    NSSetUncaughtExceptionHandler(&uncaughtExceptionHandler);
}


void uncaughtExceptionHandler(NSException *exception) {
    NSLog(@"CRASH: %@", exception);
    NSLog(@"Stack Trace: %@", [exception callStackSymbols]);

}

出力例:
2013-06-08 14:27:49.153 Test[29884:11303] Stack Trace: (
0   CoreFoundation                      0x01c9002e __exceptionPreprocess + 206
1   libobjc.A.dylib                     0x010cde7e objc_exception_throw + 44
2   CoreFoundation                      0x01c321c4 -[__NSArrayM removeObjectAtIndex:] + 212
3   Test                                0x000020bb -[MyViewController remove] + 91
4   Foundation                          0x00acb5b3 __NSFireDelayedPerform + 380
.....
)

エラーによっては手がかりになる情報も含まれてきます。仕込んでおいて損はないでしょう。

追記
swiftの場合
        NSSetUncaughtExceptionHandler{ exception in
            print(exception)
            print(exception.callStackSymbols)

        }
参照リンク:

How should I use NSSetUncaughtExceptionHandler in Swift