0

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:

enter image description here

Community
  • 1
  • 1
Harry Ng
  • 1,070
  • 8
  • 20
  • A bit unclear. Please be more specific, what kind of animation does your row need? – Daniyar Jan 12 '16 at 14:48
  • I edited with the addition of image, indicating the animation I want to work on. – Harry Ng Jan 12 '16 at 14:58
  • You need to combine existing `NSTableView` adding row animation and custom `CoreAnimation`-one by transforming several `CALayer`s during that. https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/CoreAnimation_guide/CoreAnimationBasics/CoreAnimationBasics.html – Daniyar Jan 12 '16 at 15:27
  • Thanks. I would love to see some references to example code. – Harry Ng Jan 13 '16 at 00:48
  • Take a look at this question http://stackoverflow.com/questions/16113417/my-calayer-transform-holds-after-animation-but-the-perspective-dissapears – Daniyar Jan 13 '16 at 06:46
  • @Astoria the question there refers to iPhone, does it serve the same in OS X? – Harry Ng Jan 13 '16 at 07:43
  • almost the same for your case. – Daniyar Jan 13 '16 at 08:14
  • Thanks, I will give it a try – Harry Ng Jan 13 '16 at 08:14
  • @Astoria I have checked the links, the animation itself works. However, how could I scroll down the view a bit more to show the animation? I am currently using NSCollectionView, and it bounces back with a short offset. – Harry Ng Jan 14 '16 at 08:09
  • I think It's a topic for another question, isn't it? – Daniyar Jan 14 '16 at 11:35
  • @Astoria you are right, I forgot writing NSTableView here. I was struggling from inside, whether I should use NSTableView or NSCollectionView. – Harry Ng Jan 14 '16 at 13:36

2 Answers2

0

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];
}
Keith Knauber
  • 752
  • 6
  • 13
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]];       
   }

}
Keith Knauber
  • 752
  • 6
  • 13