アーカイブ

Posts Tagged ‘UIKit’

bookwormのソースコードを公開します

以前、プライベートでiOSの書籍アプリを開発していた頃があり、単独の書籍タイトル向けにライセンス販売したりしておりました。例えばコレとか。

所がAppleの方針変更があり、書籍はストアアプリと課金で購入できる書籍データに分離しろというお達しが出て単品の書籍アプリは禁止されてしまい、開発は長続きする事なく終わってしまいました。

製品寿命も尽きてますし、当時の関係者の了承も得られたので、ソースを公開する事にします。

https://github.com/marvelph/OpenBookworm

独自のマークアップ形式のテキストコンテンツを、縦書きでリフローするタイプの書籍アプリになってます。iPhoneやiPadのポートレートでも動作しますが、iPadでランドスケープ動作させると見開きになります。シークバーでページを移動させると、トランジションがちょっと面白いかもしれません。

製品のコンテンツを抜いて著作権切れの童話に差し替えたりしたので、開発中の試行錯誤の履歴は残っていない状態です。

Xcode8でビルドして動かしてみましたが、検索とかフォントサイズ変更とか、動かなくなっている機能もあるようです。

カテゴリー:開発 タグ: ,

UITableViewCellのサブビューをハイライトさせる

UITableViewをタップすると、行がハイライト表示されます。背景は青く、文字は白く。
これはタップを検出したときに、UITableViewCellのsetHighlighted:animated:が呼び出された時の応答です。
ここに、若干の謎があります。

通常UITableViewCellはサブビューとして、UIImageViewやUILabelを持っています。この二つのクラスは、setHighlighted:というメソッドを持っているので、それが呼び出されているのでしょう。
しかし、UIViewならどんなクラスであってもサブビューとして追加できる事を考えると、これは妙です。
サブビューのクラスが上記二種類かどうか判定しているような、残念な実装かもしれせん。
少なくても、UIHighlightableなどというプロトコルも、見当たりません。
これは、独自のUIViewのサブクラスを用意した場合に、ハイライトに応答する方法が無いという事です。

解決方法
力技的には、UITableViewCellのsetHighlighted:animated:をオーバーライドしてしまう事を考えると思いますが、もっと良い方法が見つかりました。
実はUITableViewCellはサブビューをハイライトしようとした時は、該当するUIViewがsetHighlighted:を実装しているかどうか調べます。
実装していれば呼び出すという事です。
つまり、プロトコルのオプショナルメソッドや、非形式プロトコルと同様な扱いになっています。
以下は「MyView」と文字を表示するだけのUIViewのサブクラスですが、UITableViewCellに追加するとちゃんとハイライトします。

@interface MyView : UIView {
    BOOL highlighted;
}

@property (nonatomic, getter=isHighlighted) BOOL highlighted;

@end

@implementation MyView

- (id)initWithFrame:(CGRect)frame {
    if (self = [super initWithFrame:frame]) {
         self.backgroundColor = [UIColor clearColor];
    }
    return self;
}

- (void)drawRect:(CGRect)rect {
    if (highlighted) {
        [[UIColor whiteColor] set];
    }
    else {
        [[UIColor blackColor] set];
    }
    [@"MyView" drawInRect:self.bounds withFont:[UIFont systemFontOfSize:[UIFont systemFontSize]]];
}

@synthesize highlighted;

- (void)setHighlighted:(BOOL)aHighlighted {
    if (highlighted != aHighlighted) {
        highlighted = aHighlighted;
        [self setNeedsDisplay];
    }
}

@end
カテゴリー:開発 タグ: ,

iPhone OS 3.0のUIActionSheetは動的メニューとして使える

dynamic_action_sheet
なぜなら、項目を何件でも表示できるから。

iPhone OS 2.xまでのUIActionSheetは、項目の件数が増えると画面上にはみ出してしまって、ボタンが押せなくなってしまう限界があり、件数に限界がありました。
iPhone OS 3.0のUIActionSheetはこの部分が改良されて、はみ出る時は自動的にリスト表示に切り替わります。

カテゴリー:開発 タグ: ,

UIAlertView表示中に端末を回転する

UIAlertViewの表示中に端末を回転すると、いったんアラートが消えて回転後に再表示されます。
単に見せ方がこうなっているだけだと考えていたのですが、再表示のタイミングで次のようなイベントが発生しています。

  1. UIAlertViewのdelegateにalertView:didDismissWithButtonIndexが飛ぶ。この時のボタンインデックスは、cancelButtonIndex
  2. UIAlertViewが再表示される。

つまり、一度アラートは閉じています。
ここで問題は、cancelButtonIndexに大して何らかの処理を書いてしまっている場合です。
回転をサポートする場合はもちろんですが、cancelButtonIndexは処理無しにしておくべきだと思われます。

カテゴリー:開発 タグ: ,

Interface BuilderでビューのAutosizingが設定できない時

Interface Builderで、ビューのAutosizingを設定できない事があります。
この場合、コントロールが無反応になります。
最初は理由が良く判らなかったのですが、次の条件で設定不可になるようです。

  • 対象のビューがxibのトップレベルオブジェクトの一つ。
  • 対象のビューにSimulated Inteface Elementsが一つでも設定されている。

ポイントは後者です。一旦Simulated Inteface ElementsをNoneにしてやればAutosizingを設定できるので、その後に再設定すれば良いです。

カテゴリー:開発 タグ:

UINavigationControllerのUIToolbarサポート

iPhone 3.0になって、UINavigationControllerがUIToolbarを管理してくれるようになりました。UIToolbarを自前で管理しようとすると、UIViewControllerの管理するviewを階層化する必要があったり、その影響でUITableViewControllerが使えなくなったりと面倒な事が多かったのですが、UINavigationControllerに任せるとかなり楽になります。
具体的には、次のような事をやってくれます。

  • UINavigationBarのように、UIToolbarをUIViewControllerの管理するviewの外側で管理してくれる
    viewの階層構造が複雑化しませんし、UITableViewControllerも使えます。
  • UINavigationBarのように、UIToolbarに含まれるUIBarButtonItemをUIViewControllerの遷移に合わせて自動的に交換してくれる

概ね、UINavigationBarサポートと同等という事になります。

所がInterface BuilderでUINavigationControllerにUIToolbarを設置しようとすると、思ったように行かなかったりします。

UIBarButtonItemが設置できないように見える

Interface BuilderでUIBarButtonItemをUINavigationControllerのviewのツールバー部分にドラッグしても、ツールバー上に挿入ポイントが表示されません。しかし、そのままドロップすれば普通に挿入可能です。UIViewに自分で設置したUIToolBarの場合は挿入ポイントが表示されるので、挿入不能と勘違いしてしまいそうです。
あるいはxibの階層リストの方で、UIViewControllerの直下にUIBarButtonをドロップする事もできます。

UIControlが設置できないように見える

UIViewに自分で設置したUIToolBarの場合は、UIControl(UISegmentedControl等)を設置する事もできました。この場合、UIBarButtonItemがUIControlをラップした状態になります。所がUINavigationControllerのツールバー部分にUIControlをドロップしても、ツールバーに入れる事ができません。代わりに、UIViewControllerの管理するviewに入ってしまいます。しかし、他のUIToolBarに設置済みのUIBarButtonItemにラップされたUIControlを、UINavigationControllerのツールバーにドラッグアンドドロップする事ができます。
つまり、作業用のUIToolBarを適当なUIViewに設置して、一旦そこに用意したUIControlを移動してしまえば良いんです。

以上の話はiPhone SDK 3.0の時点ですが、遠からず改良されると思います。

カテゴリー:開発 タグ:

UITextFieldの入力文字数を制限する

ユーザに文字入力して貰う時に、最大文字数で制限したい時があります。
残念ながらUITextFieldには、maxTextLengthみたいな名前のプロパティはありません。

このような場合、UITextFieldDelegateのtextField:shouldChangeCharactersInRange:replacementString:を使う事ができます。
文字入力やバックスペースが入った時、カットやペーストを行った時に、rangeに置き換え範囲、stringに置き換え文字列が渡ってきます。
現在のテキストに対して置換を実行してやれば入力確定後の文字列が得られるので、文字数をチェックして受け入れるか拒否します。

- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
    NSMutableString *text = [[textField.text mutableCopy] autorelease];
    [text replaceCharactersInRange:range withString:string];
    return [text length] < <最大文字数>
}

文字数のチェックにlengthを使っていますが、これはサロゲートペアを処理しないので、見た目の文字数でチェックしたい場合はグリフを数えてください。

問題点

実は日本語環境では問題があります。予測変換で単語を選択入力すると、textField:shouldChangeCharactersInRange:replacementString:が発生しません。キーボードから文字を入れた時のみ発生するようです。
そのため、パスワード入力とか日本語キーボードを禁止している時しか当てになりません。
iPhone OSのバグのような気もしますね。
現状、文字の切り落としを併用して対処しています。

カテゴリー:開発 タグ: ,