Can I ask you folks to look over and comment on this code? I think I’ve set up 
the text view and text container correctly to track the size of the text they 
contain, and I can notify my table view delegate when their size changes. The 
problem I’m having is that the table view delegate can note and log the size 
change and everything works well--as long as I don’t really do anything with 
the information. But if the table view delegate notifies the table that the 
corresponding row size changed, everything goes crazy.  

I’m going to take this step by step. Would you comment on my NSTextView 
subclass and tell me if something is wrong in the way I’ve set it up to size 
itself, notify itself, or pass along size-change notifications?

The code below is from three separate source files, but I’m sure you can tell 
which is which.

@protocol SizeWatcher <NSObject>

// Notify of actual size change, along with variables to
// answer the most common questions receiver will want to
// ask:
//    Did width grow? Shrink? Remain the same?
//    Did height grow? Shrink? Remain the same?
//    What is the view's new frame/bounds?

-(void)sizeChangedForView:(NSView*)view
              widthChange:(CGFloat)wc
             heightChange:(CGFloat)hc;

@end

#import "SizeWatcher.h"

@interface CJAutosizingTextView : NSTextView

-(void)addSizeWatcher:(id)obj;
-(void)removeSizeWatcher:(id)obj;
-(void)removeAllSizeWatchers;

-(void)setLayoutManager:(NSLayoutManager*)lm;

@end

#import "CJAutosizingTextView.h"

@interface CJAutosizingTextView ()

@property NSRect oldFrame;
@property NSMutableArray* sizeWatchers;

@end

@implementation CJAutosizingTextView

@synthesize oldFrame;
@synthesize sizeWatchers;

-(instancetype)initWithFrame:(NSRect)frameRect
            andLayoutManager:(NSLayoutManager*)lm
{
  self = [super initWithFrame:frameRect];
  if ( self != nil ) {

    sizeWatchers = nil;
    oldFrame = frameRect;

    NSTextContainer* tc = [[NSTextContainer alloc] init];
    self.textContainer = tc;

    NSNotificationCenter* nc = [NSNotificationCenter defaultCenter];
    [nc addObserver:self
           selector:@selector(frameChanged:)
               name:NSViewFrameDidChangeNotification
             object:self];

    self.layoutManager = lm;

    // Make text container and text view automatically
    // size themselves vertically to fit the text

    tc.widthTracksTextView = YES;
    tc.heightTracksTextView = NO;

    self.horizontallyResizable = NO;
    self.verticallyResizable = YES;

    // Allow superview to resize text view's width

    self.autoresizingMask = NSViewWidthSizable;

  }
  return self;
}

-(instancetype)initWithFrame:(NSRect)frameRect
{
  return [self initWithFrame:frameRect andLayoutManager:nil];
}

-(void)dealloc
{
  NSNotificationCenter* nc = [NSNotificationCenter defaultCenter];
  [nc removeObserver:self
                name:NSViewFrameDidChangeNotification
              object:self];
}

// NSTextView already has a layoutManager: selector to read the
// text container's layout manager. Here we add a convenient
// setter companion

-(void)setLayoutManager:(NSLayoutManager*)lm
{
  self.textContainer.layoutManager = lm;
}

// Maintain the array of size watchers

-(void)addSizeWatcher:(id)obj
{
  if ( [obj conformsToProtocol:@protocol(SizeWatcher)] ) {
    if ( sizeWatchers == nil ) {
      sizeWatchers = [[NSMutableArray alloc] initWithCapacity:1];
    }
    [self.sizeWatchers addObject:obj];
  }
}

-(void)removeSizeWatcher:(id)obj
{
  if ( sizeWatchers != nil ) {
    [sizeWatchers removeObject:obj];
  }
}

-(void)removeAllSizeWatchers
{
  if ( sizeWatchers != nil ) {
    [sizeWatchers removeAllObjects];
  }
}

// When I'm notified that my frame changes, see if the new frame's
// size change and, if so, notify all size watchers

-(void)frameChanged:(NSNotification*)note
{
  // Begin with test that's probably unnecessary, but just to be sure...
  if ( [note object] == self ) {
    NSRect newFrame = self.frame;
    CGFloat wd = newFrame.size.width - oldFrame.size.width;
    CGFloat hd = newFrame.size.width - oldFrame.size.width;
    oldFrame = newFrame;
    if ( wd != 0.0 || hd != 0.0 ) {
      // Notify watchers of actual size change
      for ( NSObject<SizeWatcher>* obj in sizeWatchers ) {
        [obj sizeChangedForView:self widthChange:wd heightChange:hd];
      }
    }
  }
}

@end

--  

Charles

_______________________________________________

Cocoa-dev mailing list (Cocoa-dev@lists.apple.com)

Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com

Help/Unsubscribe/Update your Subscription:
https://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com

This email sent to arch...@mail-archive.com

Reply via email to