Animate NSButton resizing when modifying imagePosition property - objective-c

I have an NSButton subclass in an autolayout environment. The subclass implements mouseEntered: and mouseExited: methods, which should change the positioning of the button's image. On mouseEntered: it is changed to NSImageLeft (showing the title) and on mouseExited: it is changed to NSImageOnly (not showing the title).
Autolayout takes care of the resizing of the NSButton subclass in my view hierarchy, but it does not look smooth as it is not using CoreAnimation for resizing the button width according to whether the title is to be shown or not. How can I let the resizing happen with an animation?

Open an animation group, enable implicit animations, change the button image position, and then force layout:
[NSAnimationContext runAnimationGroup:^(NSAnimationContext *context){
context.allowsImplicitAnimation = YES;
button.imagePosition = NSImageOnly;
[button.window layoutIfNeeded];
} completionHandler:nil];

I solved it by using the following methods:
- (void)mouseEntered:(NSEvent *)theEvent {
NSSize titleStringSize = [self.title sizeWithAttributes:#{NSFontAttributeName:[NSFont boldSystemFontOfSize:11.0]}];
self.widthConstraint.animator.constant = titleStringSize.width+self.originalWidth+5.0;
}
- (void)mouseExited:(NSEvent *)theEvent {
self.widthConstraint.animator.constant = self.originalWidth;
}
originalWidth and widthConstraint are properties of my NSButton subclass that are defined during awakeFromNib.

Related

NSScrollView doesn't call drawRect for documentView

I have NSScrollView with drawable content.
It works fine with scrolling until I press Cmd+Tab or hide window in any other way.
When I open window with scrollView second time it doesn't redraw content on scrolling.
so drawRect function for documentView doesn't work anymore.
setting content view (I need to redraw it on scrolling because self.documentView is much wider than window's width)
[self.documentScroll setDocumentView:self.documentView];
in NSClipView
-(void) drawRect:(NSRect)dirtyRect {
[self setCopiesOnScroll: NO];
[super drawRect:dirtyRect];
NSArray* ar = self.subviews;
NSView* docView = ar.firstObject;
BOOL b = docView.needsDisplay;
}
docView.needsDisplay is always NO after window was hidden and shown second time. On application's launch it's always YES and drawRect method calls for documentView on every scrolling event

NSScrollview not scrolling programmatically?

Note:Both Horizontal and vertical scrollers are visible on the screen and work fine.But I cant make them move Programatically.
I am working on a cocoa desktop application.I am using the NSScrollview in my Mainmenu.xib file and I am creating an outlet in its owner which is Appdelegate.h .
Here is the outlet
#property(nonatomic,retain) IBOutlet NSScrollView* scrollview;
When I try to set a new referencing outlet of my NSScrollview from the interface builder and take the line to file's owner I only see one option "delegate".I dont see the outlet scrollview.
So I connect the scrollview to the delegate in file's owner (As I cant see the scrollview outlet).
Now I am trying to do auto scrolling in my code.Here is the code
for(int a=0;a<10000;a++)
{
NSPoint pointToScrollTo = NSMakePoint ( 100+a,100+a ); // Any point you like.
[[self.scrollview contentView] scrollToPoint: pointToScrollTo];
[self.scrollview reflectScrolledClipView: [self.scrollview contentView]];
}
This code does not work and The scroller does not scroll automatically.
I am trying to make a slow scrolling animation with this code.
NSClipView has a scrollToPoint: method which can be used to scroll programmatically:
- (IBAction)scrollToMid:(id)sender
{
CGFloat midYPoint = [self.scrollView contentView].frame.size.height/2.0;
[[self.scrollView contentView] scrollToPoint:NSMakePoint(0.0, midYPoint)];
[self.scrollView reflectScrolledClipView:[self.scrollView contentView]];
}
If you want animated scrolling, you have to set the boundsOrigin via animator proxy. (Because neither NSScrollView nor NSClipView expose an animatable scroll point property)
- (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];
}
Better way to get this, flip the view. I have worked on this to get the scroll view to top.
-(void)scrollToTop:(NSScrollView *)scrollView
{
NSPoint newScrollOrigin;
if ([[scrollView documentView] isFlipped]) {
newScrollOrigin=NSMakePoint(0.0,0.0);
} else {
newScrollOrigin=NSMakePoint(0.0,NSMaxY([[scrollView documentView] frame]) -NSHeight([[scrollView contentView] bounds]));
}
[[scrollView documentView] scrollPoint:newScrollOrigin];
}
That should be called in windowDidLoad to get the position .
-(void)windowDidLoad
{
[super windowDidLoad];
[self scrollToTop:_myScroll];
}
In the identity inspector of your File's Owner, check its class. It must be set to NSApplication. You have to change it to AppDelegate to get the outlet scrollview to which you can connect your scrollview.
But the better option is to have a NSObject in your xib and set its class to AppDelegate. (However xcode create it by default, check if a item "AppDelegate" is present in the box "objects" below "Placeholders" where FileOwner is present). Now drag a Referencing outlet line from your scrollview to that object. It will show the IBOutlet object name of your Appdelegate class.
UPDATE:
You cannot create a slow animation using a for loop. The for loop is so fast that you will see only the final result, not the complete scrolling. You need to use core animation for slow animation or NSTimer to delay scrolling to make it slow.
Plus please try using [scrollview documentView] , everywhere in place of [scrollview contentView] . Although i haven't given it a try.
Ok I worked on it, and this is the final result
If your scroll bar is on top, the following will move it to end:
[scroll.contentView scrollToPoint:NSMakePoint(0, ((NSView*)scroll.contentView).frame.size.height - scroll.contentSize.height)];
[scroll reflectScrolledClipView: [scroll contentView]];
You may modify the above as per your need.
Also even your code is working, but you need to take the relative points in place of any point.
So instead of placing your code in the for loop, test it by dragging a button on your xib, and in its button action write:
NSPoint pointToScrollTo = NSMakePoint ( 100,100 ); // Any point you like.
[[scrollview contentView] scrollToPoint: pointToScrollTo];
[scrollview reflectScrolledClipView: [scrollview contentView]];
Now run the application, set the scroll of scrollview to some random position. Then click the button and check if scroll moved somewhere.
If it works then your code is fine, your only need to set the points accordingly. It worked for me.
I found that if you are using NSViewController, you need to do the 'scrollPoint' in your viewWillAppear method, rather than in viewWillLoad. FYI.

Zooming UIImage in a UIScrollView

I currently have a custom class that implements UIScrollView.
I created one UIImageView and add it to a subview of the UIScrollView. It scrolls vertically and horizontally perfectly, but it seems to be ignoring the zooming stuff. Here is my code:
- (void) setUpBoard
{
// add board
UIImageView *imageHolder = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"grid.jpg"]];
[self addSubview:imageHolder];
self.contentSize = CGSizeMake(imageHolder.frame.size.width, imageHolder.frame.size.height);
self.indicatorStyle = UIScrollViewIndicatorStyleWhite;
self.maximumZoomScale = 5.0;
self.minimumZoomScale = 0.2;
self.zoomScale = 0.7;
self.clipsToBounds = YES;
}
How do I get it to zoom using standard pinch gestures? Any help is appreciated. Thanks!
From the UIScrollView Class Reference Overview:
The UIScrollView class can have a delegate that must adopt the UIScrollViewDelegate protocol. For zooming and panning to work, the delegate must implement both viewForZoomingInScrollView: and scrollViewDidEndZooming:withView:atScale:; in addition, the maximum (maximumZoomScale) and minimum (minimumZoomScale) zoom scale must be different.
I suspect that either you haven't assigned a delegate to the scroll view, or the delegate doesn't implement both of those methods.

Grey border around view when using NSBorderlessWindowMask

I'm having the exact same problem as in this question:
Gray border when using NSBorderlessWindowMask
However, the accepted answer (as in the comments) of removing the window shadow doesn't seem to work, at least on Lion.
I've subclassed NSWindow, and created a borderless window in this manner:
-(id)initWithContentRect:(NSRect)contentRect styleMask:(NSUInteger)aStyle backing:(NSBackingStoreType)bufferingType defer:(BOOL)flag {
self = [super initWithContentRect:contentRect
styleMask:(NSBorderlessWindowMask | NSResizableWindowMask)
backing:bufferingType
defer:flag];
[self setMovableByWindowBackground:YES];
[self setOpaque:NO];
[self setBackgroundColor:[NSColor clearColor]];
[self setHasShadow:YES];
[self setLevel:NSMainMenuWindowLevel];
return self;
}
Please note that this app will only be run on Lion (so NSResizableWindowMask doesn't change the appearance). I tried disabling the shadow, and toggling numerous settings for my window but I can't seem to remove this grey border:
Nowhere in my code do I add a border. I simply have a NSSplitView added in Interface Builder in a window. During runtime I add the colored view as a subview to the left pane of the split view, completely filling the bounds of the left split view.
Edit: This happens even using a simple NSView, not even a split view.
TL;DR: Why does my NSView have a grey border around it?
ok got it. to remove the shadow simply add this to your NSWindow subclass:
- (BOOL)hasShadow {
return NO;
}
and to remove the border you need to know that this border is coming from the view - not the window (just like you said it in your edit). So you have to disable the border for the view with this code:
[myview setBorderType:NSNoBorder];

Custom NSView Fill Paints Over Bottom Bar

I have a window which has a custom NSView and has a bottom bar with controls on it, one of which is an NSColorWheel.
For simplicity sake the Window is 332px high, with the custom NSView being 300px high and the bottom bar being 32px high.
The bottom bar is created as part of my awakeFromNib when the app loads the window using the following code:
[[self window] setAutorecalculatesContentBorderThickness:YES forEdge:NSMinYEdge];
[[self window] setContentBorderThickness: 32.0 forEdge: NSMinYEdge];
In my custom NSView class I fill the rectangle with color. Everything works fine when the app loads using the following in my NSView class:
- (void)drawRect:(NSRect)dirtyRect
{
dirtyRect = [self bounds];
NSColor * mNewColor = [NSColor blackColor];
[mNewColor set];
[NSBezierPath fillRect:dirtyRect];
}
However, if I subsequently call a method that changes the color of the custom NSView when a color wheel in the bottom bar is changed, the bottom bar gets overwritten with the color. The following code illustrates this method (this code is in the custom NSView class:
- (void)changeBackgroundColor:(NSNotification *)notification
{
NSLog(#"Changed background color");
NSRect mRect = [self bounds];
NSColor * mNewColor = [theColorWell color];
[mNewColor set];
[NSBezierPath fillRect:mRect];
[self setNeedsDisplay:YES];
}
Resizing the window instantly corrects the problem, but obviously I don't want the user to have to resize the window for an obvious bug!
What I don't understand is why my bounds appear to be mapping to the parent window and not the custom NSView when I call setNeedsDisplay and yet the bound correctly adjust when I resize the window using the mouse (even if just by 1 pixel).
Do I somehow need to account for the bottom bar on the redraw?
Any and all help much appreciated.
You should do all your drawing in the drawRect: method of your custom NSView. Cocoa automatically sets up the graphics context for you when it calls this method - things may not draw correctly if you perform drawing operations in other methods.
Your code in drawRect: could set the colour to the the current background colour as specified by your NSColorWell and fill the dirtyRect rectangle with this.
Then in the other method just call [self setNeedsDisplay:YES]; and then drawRect: will automatically be called to redraw the view.
See here for more information: http://developer.apple.com/mac/library/documentation/Cocoa/Conceptual/CocoaViewsGuide/SubclassingNSView/SubclassingNSView.html#//apple_ref/doc/uid/TP40002978-CH7-SW4 (in particular the Drawing View Content section)