Prevent the user from manually scrolling NSScrollview? - objective-c

I am using an NSScrollview to scroll programatically. I have hidden the horizental and vertical scrollers but the user is still able to scroll using the mouse wheel.I want to prevent thismanual scrolling.
This is how I am doing the automatic scrolling
- (IBAction)scrollToMidAnimated:(id)sender
{
[NSAnimationContext beginGrouping];
[[NSAnimationContext currentContext] setDuration:2.0];
NSClipView* clipView = [self.scrollView contentView];
NSPoint newOrigin = [clipView bounds].origin;
newOrigin.y = [self.scrollView contentView].frame.size.height/2.0;
[[clipView animator] setBoundsOrigin:newOrigin];
[NSAnimationContext endGrouping];
}
It works perfectly but I want to prevent the user from manual scrolling(I only want to scroll programatically).Is there any way to do that?

Try like this create custom scrollview class and then include below piece of code:-
- (void)scrollWheel:(NSEvent *)theEvent
{
[[self nextResponder] scrollWheel:theEvent];
}

Finally got the solution.I subclassed NSScrollView and overrided its method scrollwheel and left it empty.
- (void)scrollWheel:(NSEvent *)theEvent
{
// Do nothing
}

if you did hide the scrollers and prevent user scrolling, are you sure you want NSScrollView at all in the first place? Just use NSClipView and animate just like you are doing. No need to subclass and override scrollWheel:. Its a little more cleaner and concise.

Related

NSWindow animate size change new subview with auto layout

I have a NSWindowController containing several NSViewController. The windowController displays the view of the first viewController as a subView of the main window.
On a button click the windowController adds the next viewControllers view as subView, adds some layout constraints and animates them so that the first view moves out and the next view moves in. After the animation the first view is removed from its superView.
[nextController setLeftLayoutConstraint:nextLeft];
// ^^^^^^^^^^^^^^^^^^^^^^^ custom category'd property
[containerView addSubview:nextView];
[containerView addConstraints:#[nextWidth, nextHeight, nextTop, nextLeft]];
[NSAnimationContext runAnimationGroup:^(NSAnimationContext *context) {
[context setTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut]];
[context setDuration:0.5];
[[nextLeft animator] setConstant:[self leftOffset]];
[[[[self currentViewController] view] animator] setAlphaValue:-0.25]; // negative alpha to shift the timing
[[[[self currentViewController] leftLayoutConstraint] animator] setConstant:-NSWidth(containerView.frame)];
// ^^^^^^^^^^^^^^^^^^^^ custom category'd property
} completionHandler:^{
[[[self currentViewController] view] setAlphaValue:1.0];
[[[self currentViewController] view] removeFromSuperview];
[[self currentViewController] removeFromParentViewController];
_currentViewController = nextController;
}];
Now the second view is much taller than the first so it changes the windows hight as well.
Unfortunately this window frame change is not animated and the window pops ugly in the right size.
I tried getting the next views hight first to then animate all constraints or something like this. Unfortunately the views are not in the correct size before the animation is done.
Is there any way to animate the window change as well?

Disable roll-out animation for NSOutlineView

Is there a way to stop the roll-out animation for an NSOutlineView. By 'roll-out' I mean the animation that happens when an item is expanded/collapsed, and the children slide down/up.
Create a subclass of NSOutlineView and suppress animation:
-(void) expandItem:(id)item expandChildren:(BOOL)expandChildren
{
[NSAnimationContext beginGrouping];
[[NSAnimationContext currentContext] setDuration:0.0];
[super expandItem:item expandChildren:expandChildren];
[NSAnimationContext endGrouping];
}
Other methods are:
– expandItem:
– expandItem:expandChildren:
– collapseItem:
– collapseItem:collapseChildren:

CABasicAnimation move frame left by 300px

I need help with CABasicAnimation. I am trying to move a NSView left by 300 pixels. I found this SO thread: How to animate the frame of an layer with CABasicAnimation?
Turns out animating the frame is not possible and one of the answer points to a link to QA on Apple's website but it takes me a to a generic page:
http://developer.apple.com/library/mac/#qa/qa1620/_index.html
So, how can I do something as simple as translation of my NSView/CALyer?
Thanks!
NSView has a protocol called NSAnimatablePropertyContainer which allows you to create basic animations for views:
The NSAnimatablePropertyContainer protocol defines a way to add
animation to an existing class with a minimum of API impact ...
Sending of key-value-coding compliant "set" messages to the proxy will
trigger animation for automatically animated properties of its target
object.
The NSAnimatablePropertyContainer protocol can be found here
I recently used this technique to change the origin of a frame:
-(void)setOrigin:(NSPoint)aPoint {
[[self animator] setFrameOrigin:aPoint];
}
Instead of calling the [view setFrameOrigin:], I created another method called setOrigin: which then applies the setFrameOrigin: call to the view's animator.
If you need to change the duration of the animation, you can do so like this (similar to CATransactions):
-(void)setOrigin:(NSPoint)aPoint {
[NSAnimationContext beginGrouping];
[[NSAnimationContext currentContext] setCompletionHandler:^{
...Completion Callback Code goes here...
}];
[[NSAnimationContext currentContext] setDuration:1.0];
[[self animator] setFrameOrigin:aPoint];
[NSAnimationContext endGrouping];
}
The NSAnimationContext is described here
You can animate center property instead. Eg:
//assuming view is your NSView
CGPoint newCenter = CGPointMake(view.center.x - 300, view.center.y);
CABasicAnimation *animation = [CABasicAnimation animation];
//setup your animation eg. duration/other options
animation.fromValue = [NSValue valueWithCGPoint:v.center];
animation.toValue = [NSValue valueWithCGPoint:newCenter];
[view.layer addAnimation:animation forKey:#"key"];

Replacing a NSSplitView subview with Core Animation

I'm trying to figure out how to animate switching (replacing) subviews in a vertically-configured, 2-view NSSplitView. I've got it semi-working using the following methods in my NSSplitView subclass:
To set up the animation:
- (void)awakeFromNib {
// set delegate
[self setWantsLayer:YES];
CATransition *transition = [CATransition animation];
[transition setType:kCATransitionPush];
[transition setSubtype:kCATransitionFromBottom];
[transition setDuration:1.0];
[self setAnimations:[NSDictionary dictionaryWithObject:transition
forKey:#"subviews"]];
}
And to perform it:
- (void)replaceRightView:(NSView *)newView animated:(BOOL)animate {
NSRect currentSize = [[[self subviews] objectAtIndex:1] frame];
[newView setFrame:currentSize];
if (animate) {
[[self animator] replaceSubview:[[self subviews] objectAtIndex:1]
with:newView];
} else {
[self replaceSubview:[[self subviews] objectAtIndex:1]
with:newView];
}
}
However, this code has the effect of pushing the entire NSSplitView off, rather than just the subview on the right side of the split.
Is there a way to animate just the subview transition? Perhaps I'm using the wrong animation key ("subviews")? Other animation methods would be fine too.
Thanks!
It's probably not the cleanest way, but I ended up using an NSView 'container' subclass with custom addSubview: and replaceSubview:withView: methods that modify the new subview's frame to match the container view's frame, which is then structured into the NSSplitView subview I wanted to animate. I then setup the CATransition on the container view, and everything worked as I wanted.

How do I trigger a callback when a NSAnimationContext ends?

I have an animation which moves some views around. When this animation completes I want the window to recalculate the keyview loop. My code is simmilar to the follow mock code:
[NSAnimationContext beginGrouping];
[newView setAlpha: 0.0]; //hide newView
[self addSubView:newView];
//position the views
[[oldView animator] setFrame: newFrame1];
[[newView animator] setFrame: newFrame2];
[[newView animator] setAlpha: 1.0]; //fade-in newView
[NSAnimationContext endGrouping];
[[self window] recalculateKeyViewLoop];
The problem with this code is that recalculateKeyViewLoop is called before the views are in their new positions which means that the keyviewloop is wrong.
How do I fix this?
My first though is to call recalculateKeyViewLoop in a callback from when the animation ends but I can't figure out how to do this.
Something that's not so obvious, or at least wasn't to me, is that there are two animations going on when you do a setFrame:, with keys frameSize and frameOrigin.
Depending on what your original and final frames are you may need to register yourself as a delegate for one or both of them.
I'd also recommend that you make a copy of the animation you get back from -animationForKey: and store your modified copy in the animations dictionary of your object. This way your delegate will only be called at the conclusion of that particular objects' animator duration, versus all objects animating that key.
eg.
CAAnimation *animation = [[view animationForKey:#"frameOrigin"] copy];
animation.delegate = self;
[view setAnimations:[NSDictionary dictionaryWithObject:animation forKey:#"frameOrigin"]];
In this way your animation object will supersede the default animation object for that view. Then implement whichever delegate methods you're interested in.
You should be able to send -animationForKey: to your views to get a CAAnimation instance, then set yourself as its delegate and implement the method that Adam mentioned.
if you use CAAnimation this has an animationDidStop:finished: delegate method..
http://developer.apple.com/documentation/GraphicsImaging/Reference/CAAnimation_class/Introduction/Introduction.html#//apple_ref/occ/cl/CAAnimation
Hope this helps
You can use a completion handler like this:
[NSAnimationContext runAnimationGroup:^(NSAnimationContext *context){
// Start some animations.
[[myView animator] setFrameSize:newViewSize];
[[myWindow animator] setFrame:newWindowFrame display:YES];
} completionHandler:^{
// This block will be invoked when all of the animations started above have completed or been cancelled.
NSLog(#"All done!");
}];
Check out my post here: How would I do this iOS animation on OSX?
I wrote a class that handles this for you, using blocks. Hopefully your target allows blocks! :)
If you are animating an NSWindow (as opposed to an NSView), the other answers will not work and you should do this instead:
CAAnimation *animation = [CABasicAnimation animation];
animation.delegate = self;
self.window.animations = #{#"frame": animation};
[[self.window animator] setFrame:NSMakeRect(0, 0, 400, 200) display:YES];
which will then call the animationDidStop:finished: method on your delegate (which in the above code would be self)