Re: Animating NSSplitPane position
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