• Open Menu Close Menu
  • Apple
  • Shopping Bag
  • Apple
  • Mac
  • iPad
  • iPhone
  • Watch
  • TV
  • Music
  • Support
  • Search apple.com
  • Shopping Bag

Lists

Open Menu Close Menu
  • Terms and Conditions
  • Lists hosted on this site
  • Email the Postmaster
  • Tips for posting to public mailing lists
Re: Animating NSSplitPane position
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: Animating NSSplitPane position


  • Subject: Re: Animating NSSplitPane position
  • From: Antonio Nunes <email@hidden>
  • Date: Thu, 16 Jun 2011 07:05:53 +0100

On 16 Jun 2011, at 03:48, Graham Cox wrote:

> I'm using -setPosition:ofDividerAtIndex: and I call this using the view's animator:
>
> 	[[mySplitPane animator] setPosition:position ofDividerAtIndex:0];
>
> But it doesn't animate and just jumps into position. I see other apps manage this (e.g. Mail) so how's it done?

Hi Graham,

Having recently experienced the same limitation, I found we need to manually resize the subviews (through their animator). I posted a message here, on June 1st, titled "Animated Split View", in which I exposed a solution and asked for feedback on it (got none). Meanwhile I have improved the code, since I found the first iteration not to be robust enough. The new version follows:

==========
@interface NSSplitView (NSSplitView_Animation)

- (CGFloat)catPositionOfDividerAtIndex:(NSInteger)index;
- (void)catAnimateDividerIndexes:(NSArray *)indexes toPositions:(NSArray *)newPositions;
- (void)catAnimateDividerAtIndex:(NSInteger)index toPosition:(CGFloat)newPosition;

@end


@protocol NMD_AnimatingSplitViewDelegate <NSObject>

@required
@property (readwrite, assign)	BOOL splitViewIsAnimating;

@end
==========

==========
@implementation NSSplitView (NSSplitView_Animation)

- (CGFloat)catPositionOfDividerAtIndex:(NSInteger)index
{
	NSRect frame = [[self.subviews objectAtIndex:index] frame];
	return self.isVertical ? NSMaxX(frame) : NSMaxY(frame);
}


- (void)catAnimateDividerIndexes:(NSArray *)indexes toPositions:(NSArray *)newPositions
{
	NSUInteger numberOfSubviews = self.subviews.count;

	NSAssert(indexes.count == newPositions.count, @"indexes and newPositions arrays must have same object count.");
	NSAssert(indexes.count < numberOfSubviews, @"Trying to move too many dividers");

	NSRect newRect[numberOfSubviews];

	for (NSUInteger i = 0; i < numberOfSubviews; i++) {
		newRect[i] = [[self.subviews objectAtIndex:i] frame];
	}

	for (NSNumber *indexObject in indexes) {
		NSInteger	index = [indexObject integerValue];
		CGFloat		newPosition = [[newPositions objectAtIndex:[indexes indexOfObject:indexObject]] doubleValue];
		if (self.isVertical) {
			CGFloat oldMaxXOfRightHandView = NSMaxX(newRect[index + 1]);
			newRect[index].size.width = newPosition - NSMinX(newRect[index]);
			CGFloat dividerAdjustment = (newPosition < NSWidth(self.bounds)) ? self.dividerThickness : 0.0;
			newRect[index + 1].origin.x = newPosition + dividerAdjustment;
			newRect[index + 1].size.width = oldMaxXOfRightHandView - newPosition - dividerAdjustment;
		} else {
			CGFloat oldMaxYOfBottomView = NSMaxY(newRect[index + 1]);
			newRect[index].size.height = newPosition - NSMinY(newRect[index]);
			CGFloat dividerAdjustment = (newPosition < NSHeight(self.bounds)) ? self.dividerThickness : 0.0;
			newRect[index + 1].origin.y = newPosition + dividerAdjustment;
			newRect[index + 1].size.height = oldMaxYOfBottomView - newPosition - dividerAdjustment;
		}
	}

	if ([self.delegate respondsToSelector:@selector(splitViewIsAnimating)]) {
		 ((id<NMD_AnimatingSplitViewDelegate>)self.delegate).splitViewIsAnimating = YES;
	}
	[CATransaction begin]; {
		[CATransaction setAnimationDuration:0.25];
		[CATransaction setAnimationTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]];
		[CATransaction setCompletionBlock:^{
			if ([self.delegate respondsToSelector:@selector(splitViewIsAnimating)]) {
				((id<NMD_AnimatingSplitViewDelegate>)self.delegate).splitViewIsAnimating = NO;
			}
		}];

		for (NSUInteger i = 0; i < numberOfSubviews; i++) {
			[[[self.subviews objectAtIndex:i] animator] setFrame:newRect[i]];
		}
	} [CATransaction commit];
}


- (void)catAnimateDividerAtIndex:(NSInteger)index toPosition:(CGFloat)newPosition
{
	NSUInteger numberOfSubviews = self.subviews.count;

	NSAssert(index < numberOfSubviews - 1, @"index should be less than number of subviews - 1");

	[self catAnimateDividerIndexes:[NSArray arrayWithObject:[NSNumber numberWithInteger:index]]
					   toPositions:[NSArray arrayWithObject:[NSNumber numberWithDouble:newPosition]]];
}

@end
==========

As you'll have noticed, I prefix all my category methods with "cat", in an attempt to be (OS X) future proof.

Since the resize behaviour of the subviews for the project I'm working on is tightly controlled, I need to know if the splitview is animating, hence I added a protocol to be adopted by parties who want to disable splitview delegate constraint checking during the animation.

I found one needs to be very careful to ensure the width/height of the subviews + thickness of the dividers add up to the width/height of the splitview. I am currently using this on a splitview with three subviews (two dividers). The code is still quite young, so it may yet prove to need some tweaks, but it's been performing fine since I implemented the above a number of days ago.

Examples of how to use the category:
==========
	if (self.isShowingLeftSideList == NO) {
		// Make sure the subview is not collapsed
		[[self.splitView.subviews objectAtIndex:0] setHidden:NO];
		[self.splitView catAnimateDividerAtIndex:0
									  toPosition:DEFAULT_LEFT_SIDE_SPLITTER_WIDTH];
	} else {
		[self.splitView catAnimateDividerAtIndex:0
									  toPosition:0];
	}
==========
	if (NSWidth([[self.splitView.subviews objectAtIndex:0] bounds]) > NSWidth(self.splitView.bounds) - DEFAULT_OUTLINESPLITTER_WIDTH - 1) {
		[self.splitView catAnimateDividerIndexes:[NSArray arrayWithObjects:
												  [NSNumber numberWithInteger:0],
												  [NSNumber numberWithInteger:1],
												  nil]
						   toPositions:[NSArray arrayWithObjects:
										[NSNumber numberWithDouble:DEFAULT_LEFT_SIDE_SPLITTER_WIDTH],
										[NSNumber numberWithDouble:NSWidth(self.splitView.frame) - DEFAULT_OUTLINESPLITTER_WIDTH],
										nil]];
	} else {
		[self.splitView catAnimateDividerAtIndex:1
									  toPosition:NSWidth(self.splitView.frame) - DEFAULT_OUTLINESPLITTER_WIDTH];
	}
==========


-António

-----------------------------------------
Forgiveness is not an occasional act;
it is a permanent attitude.

--Martin Luther King, Jr
-----------------------------------------




_______________________________________________

Cocoa-dev mailing list (email@hidden)

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:

This email sent to email@hidden

  • Follow-Ups:
    • Re: Animating NSSplitPane position
      • From: Graham Cox <email@hidden>
References: 
 >Animating NSSplitPane position (From: Graham Cox <email@hidden>)

  • Prev by Date: Re: Animating NSSplitPane position
  • Next by Date: Re: Animating NSSplitPane position
  • Previous by thread: Re: Animating NSSplitPane position
  • Next by thread: Re: Animating NSSplitPane position
  • Index(es):
    • Date
    • Thread