For custom animation, I mean something like Clear does.
Many existing questions like this talks about dynamic height, and I wonder how to create a new row with dynamic height, using animation.
e.g. The animation of showing this:
For custom animation, I mean something like Clear does.
Many existing questions like this talks about dynamic height, and I wonder how to create a new row with dynamic height, using animation.
e.g. The animation of showing this:
Here's sample code for flashing a row. Not animated per-se.
@interface DTableView : NSTableView {
   NSTrackingArea       *trackingArea;
   NSUInteger mouseHoverRow;
   NSUInteger mouseHoverColumn;
   NSUInteger lastHoverRow;
   NSUInteger lastMouseHoverColumn; 
   NSUInteger      flashRow; // temporary flash
   DTableFlashType flashType;
   BOOL            flashToggle;
   NSUndoManager *textEditUndoManager; // private mgr, separate from global document undoManager!
}
@property (assign, nonatomic) NSUInteger      flashRow;
@property (assign, nonatomic) DTableFlashType flashType;
- (void)customDrawFlashRow:(NSRect)dirtyRect; // override point for subclassers
@end
@implementation DTableView
@synthesize flashRow;
@synthesize flashType;
-(void)awakeFromNib
{
   flashRow = NSNotFound;
}
// override point for subclassers.
// the default implementation flashes flashRow, depending on 
- (void)customDrawFlashRow:(NSRect)dirtyRect
{
   ///// NOTE! customDrawFlashRow is overridden by Director DScenesView
   if ( flashRow != NSNotFound )
   {
      NSRect rowFrame = [self frameOfCellAtColumn:0 row:flashRow];
      if ( NSIntersectsRect(rowFrame, dirtyRect) )
      {
         rowFrame.origin.x = dirtyRect.origin.x;
         rowFrame.size.width = dirtyRect.size.width;
         if ( flashType == kDTableFlashYellow )
         {
            flashToggle = !flashToggle;
            if ( flashToggle ) [[NSColor whiteColor] set];
            else               [[NSColor yellowColor] set];
         }
         else
            [[NSColor whiteColor] set];
         NSFrameRect(rowFrame);
      }
   }
}
- (void)drawRect:(NSRect)dirtyRect
{
   //getCpuCyclesMark();
   sVisRectIncrement++; // invalidate cache
   sDoingDrawRect = true;
   [super drawRect:dirtyRect];
   [self customDrawFlashRow:dirtyRect]; // overridden by Director DScenesView
   sDoingDrawRect = false;
}
- (void)setFlashRow:(NSUInteger)iRow // note: DScenesView overrides this
{
   // first, deal with previous flashRow if applicable
   if ( flashRow >= 0 && flashRow < [self numberOfRows] )
   {
      NSRect rowFrame = [self frameOfCellAtColumn:0 row:flashRow];
      rowFrame.origin.x = [self visibleRect].origin.x;
      rowFrame.size.width = [self visibleRect].size.width;
      [self setNeedsDisplayInRect:rowFrame];
   }
   flashRow = iRow;
   if ( flashRow >= 0 && flashRow < [self numberOfRows] )
   {
      NSRect rowFrame = [self frameOfCellAtColumn:0 row:flashRow];
      rowFrame.origin.x = [self visibleRect].origin.x;
      rowFrame.size.width = [self visibleRect].size.width;
      [self setNeedsDisplayInRect:rowFrame];
   }
}
In my controller class:
     [toScenesTable setFlashRow:row];
     [self performSelector:@selector(endFlash:) withObject:self afterDelay:0.2]; // :@selector(endFlash)
and an endFlash: selector
- (void)endFlash:(id)sender
{
   [toScenesTable setFlashRow:NSNotFound];
   //[toScenesTable setSelectionHighlightStyle:0];
}
 
    
    Here are NSTableView category methods I've added that scrolls in a useful way, about 25% from top.
#pragma mark NSTableView category
@implementation NSTableView (VNSTableViewCategory)
-(NSInteger)visibleRow
{
   NSRect theRect = [self visibleRect];
   NSRange theRange = [self rowsInRect:theRect];
   if ( theRange.length == 0 )
      return -1;
   else if ( NSLocationInRange( [self editedRow], theRange ) )
      return [self editedRow];        
   else if ( NSLocationInRange( [self clickedRow], theRange ) )
      return [self clickedRow];      
   else if ( NSLocationInRange( [self selectedRow], theRange ) )
      return [self selectedRow];
   else
      return theRange.location + (theRange.length/2);
}
-(NSRange)visibleRows
{
   NSRect theRect = [self visibleRect];
   NSRange theRange = [self rowsInRect:theRect];
   return theRange;
}
-(void)scrollRowToVisibleTwoThirds:(NSInteger)row
{
   NSRect theVisRect = [self visibleRect];
   NSUInteger numRows = [self numberOfRows];   
   NSRange visibleRows = [self rowsInRect:theVisRect];
   //NSUInteger lastVisibleRow = NSMaxRange(visibleRows);
   if ( NSLocationInRange( row, visibleRows ) )
   {
      if ( row - visibleRows.location < (visibleRows.length * 2 / 3) )
      {
         // may need to scroll up
         if ( visibleRows.location == 0 )
            [self scrollRowToVisible:0];
         else if ((row - visibleRows.location) > 2 )
            return;
      }
   }
   NSRect theRowRect = [self rectOfRow:row];
   NSPoint thePoint  = theRowRect.origin;
   thePoint.y -= theVisRect.size.height / 4; // scroll to 25% from top
   if (thePoint.y < 0 )
      thePoint.y = 0;
   NSRect theLastRowRect = [self rectOfRow:numRows-1];
   if ( thePoint.y + theVisRect.size.height > NSMaxY(theLastRowRect) )
      [self scrollRowToVisible:numRows-1];
   else
   {
      [self scrollPoint:thePoint]; // seems to be the 'preferred' method of doing this
      // kpk note: these other approaches cause redraw artifacts in many situations:
//      NSClipView *clipView = [[self enclosingScrollView] contentView];
//      [clipView scrollToPoint:[clipView constrainScrollPoint:thePoint]];
//      [[self enclosingScrollView] reflectScrolledClipView:clipView];
//      [self scrollRowToVisible:row];
//      [[[self enclosingScrollView] contentView] scrollToPoint:thePoint];
//      [[self enclosingScrollView] reflectScrolledClipView:[[self enclosingScrollView] contentView]];       
   }
}
