無選択時の不具合【実践的Macintoshプログラミング解説】

印刷用表示 |テキストサイズ 小 |中 |大 |

CoreData版 Repeating Motif Generator の開発 Repeating Motif Wonderland CoreData 実践的 Macintosh プログラミング解説

LinkIconホーム

更新日 2009-05-24

リサージュ図形が複数選択された時に、表示が適切になる様にする

無選択時の不具合

noSelectionNG.png
 これでうまくいったと思っていたのですが、複数選択状態で全てのリサージュ図形を削除すると不具合が発生する事に気づきました。表示が更新されないのです。何も選択されていないのに"Multiple Selection"と表示されてしまっています。

 データパスのSetterで選択状態を判断していたのですが、複数選択状態から無選択状態に移行しても、データパスのSetterは呼び出されない様です。しかたがないので配列コントローラの選択状態を監視する事にしました。

無選択状態を表すフラグを追加する

 無選択状態の表示がおかしいので、これを修正します。そもそも今までは無選択状態で何も表示していなかったのですが、やはり何らかのメッセージを表示した方がよいでしょう。そこで、無選択状態を表すフラグをインスタンス変数として追加します。

//
//  BezierPathView.h
//  RepeatingMotifGenerator
//
//  Copyright NovemberKou 2008. All rights reserved.
//

#import <Cocoa/Cocoa.h>


@interface BezierPathView : NSView
{
    IBOutlet    NSArrayController   *controller;

    BOOL            isXView, multipleSelection, noSelection;
    NSBezierPath    *dataPath, *displayPath;
}

- (NSBezierPath *)dataPath;
- (void)setDataPath:(NSBezierPath *)newPath;

@end

データパスのSetterを修正する

 データパスのSetterでフラグを更新していましたが、これをやめます。

//
//  BezierPathView.m
//

- (void)setDataPath:(NSBezierPath *)newPath
{
    multipleSelection = [[controller selectedObjects] count] > 1;

    [newPath retain];
    [dataPath release];
    dataPath = newPath;

    [self updateDisplayPath];
}

配列コントローラの選択状態を監視する

 キー値監視を使って配列コントローラの選択状態を監視するために、awakeFromNibにバインディングの設定を追加します。そして、これに対応するダミーのアクセサメソッドを追加します。

 Getterはとにかくメソッドが存在すれば良いだけなので、nilを返すダミーのメソッドを実装します。Setterではフラグを更新して、自身の表示を更新します。

//
//  BezierPathView.m
//

- (void)awakeFromNib
{
    [self bind:@"dataPath"
      toObject:controller
   withKeyPath:[self keyPath]
       options:nil];

    [self bind:@"selectionIndexes"
      toObject:controller
   withKeyPath:@"selectionIndexes"
       options:nil];
}

- (NSIndexSet *)selectionIndexes    {return  nil;}
- (void)setSelectionIndexes:(NSIndexSet *)newIndexSet
{
    multipleSelection = [newIndexSet count] > 1;
    noSelection = [newIndexSet count] == 0;
    [self setNeedsDisplay:YES];
}

フラグによって表示内容を切替える

 noSelectionフラグを判定条件に追加します。メッセージを表示するメソッド名もshowMultipleSelectionMessageからshowMessageに変更しました。

//
//  BezierPathView.m
//

- (void)drawRect:(NSRect)rect
{
    [[NSBezierPath bezierPathWithRect:[self bounds]] stroke];

    if(![self inLiveResize])
        if(multipleSelection || noSelection)
            [self showMessage];
        else
            [displayPath stroke];
}

メッセージを表示する

 フラグによって表示する文字列を変更します。

//
//  BezierPathView.m
//

- (void)showMessage
{
    float       fontSize, whiteSpace, dummySize = 12.0;
    NSSize      messageSize;
    NSString    *message;

    if(noSelection)         message = NSLocalizedString(@"noSelection",nil);
    if(multipleSelection)   message = NSLocalizedString(@"multipleSelection",nil);

    messageSize = [message sizeWithAttributes:[NSDictionary dictionaryWithObjectsAndKeys:
                                               [NSFont messageFontOfSize:dummySize], NSFontAttributeName,
                                               nil]];
    whiteSpace = [self bounds].size.width*0.04;
    fontSize = dummySize/messageSize.width*([self bounds].size.width - whiteSpace*2);
    messageSize = [message sizeWithAttributes:[NSDictionary dictionaryWithObjectsAndKeys:
                                               [NSFont messageFontOfSize:fontSize], NSFontAttributeName,
                                               nil]];

    [message drawAtPoint:NSMakePoint( whiteSpace, ([self bounds].size.height-messageSize.height)/2 )
          withAttributes:[NSDictionary dictionaryWithObjectsAndKeys:
                          [NSFont messageFontOfSize:fontSize], NSFontAttributeName,
                          [NSColor disabledControlTextColor], NSForegroundColorAttributeName,
                          nil]];
}

 Localizable.stringsに"noSelection"のキー値ペアを追加します。

"setAmplitude" = "Change Amplitude";
"setFrequency" = "Change Frequency";
"setPhaseLag" = "Change PhaseLag";
"setResolution" = "Change Resolution";

"cutLissajous" = "Cut Lissajous Figure";
"deleteLissajous" = "Delete Lissajous Figure";
"pasteLissajous" = "Paste Lissajous Figure";

"noSelection" = "No Selection";
"multipleSelection" = "Multiple Selection";

noSelectionOK.png
 さて、これで複数選択状態から全リサージュ図形を削除してみると右の図の様になります。

 "No Selection"と表示されているので成功です。