Switching between custom views using Toolbar - objective-c

I have a single MainMenu.xib file and I have a window by the name "Preferences"
Also I have four different Custom Views with different Heights. and a Toolbar on that window.
I have linked every toolbar item to the -(void) switchViews:(id)sender; function. The views are linked and are showing up and are also changing but the problem is that if I change from one view to another and then back or to another, the content i.e. buttons, labels, checkboxes on the views keep moving upwards, the window still keeps on changing to right heights, but if I switch enough times the content would disappear from the view.. Any kind of help would be useful.
http://imgur.com/bV3NHa0,zgJCacA
http://imgur.com/bV3NHa0,zgJCacA#1
// FOR CHANGING VIEWS IN THE PREFERENCES WINDOW
- (IBAction)switchView:(id)sender {
long int tag = [sender tag];
NSView *view = [self viewForTag:tag];
NSView *previousView = [self viewForTag:currentViewTag];
currentViewTag = tag;
NSRect newFrame = [self newFrameForNewContentView:view];
[NSAnimationContext beginGrouping];
if ([[NSApp currentEvent] modifierFlags] & NSShiftKeyMask) {
[[NSAnimationContext currentContext] setDuration:1.0];
}
[[[[self preferences] contentView] animator] replaceSubview:previousView with:view];
[[[self preferences] animator] setFrame:newFrame display:YES];
[NSAnimationContext endGrouping];
}
- (NSView *)viewForTag:(long int)tag {
NSView *view = nil;
switch (tag) {
case 0:
view = generalView;
break;
case 1:
view = timesView;
break;
case 2:
view = menubarView;
break;
case 3: default: view = aboutView;
break;
}
return view;
}
- (NSRect)newFrameForNewContentView:(NSView *)view {
NSWindow *window = [self preferences];
NSRect newFrameRect = [window frameRectForContentRect:[view frame]];
NSRect oldFrameRect = [window frame];
NSSize newSize = newFrameRect.size;
NSSize oldSize = oldFrameRect.size;
NSRect frame = [window frame];
frame.size = newSize;
frame.origin.y -= (newSize.height - oldSize.height);
return frame;
}

with the problem you're describing and glancing at your code i would start to suspect this line...
frame.origin.y -= (newSize.height - oldSize.height);

Related

NSToolbar with custom views

Hi so I've got a problem that i can't really figure out what the cause is. I made an app so far that has a NSToolbar that has custom views in it so that you can switch between views on the toolbar (just like safari's preference window toolbar views). I was using CCodings tutorial on youtube but i have the problem of the views moving by themselves, for example i click on view 1 and when i click on view 2 view one changes and the view moves up so that you can only see half of when i put on the view, I'm using Xcode 6 and OS X 10.10 so i don't know if that would be one cause but i thank you in advance if you can help me, heres the MainWindowController.m file:
Also the views should be setup correctly
-(NSRect)newFrameForNewContentView:(NSView*)view {
NSWindow *window = [self window];
NSRect newFrameRect = [window frameRectForContentRect:[view frame]];
NSRect oldFrameRect = [window frame];
NSSize newSize = newFrameRect.size;
NSSize oldSize = oldFrameRect.size;
NSRect frame = [window frame];
frame.size = newSize;
frame.origin.y -= (newSize.height - oldSize.height);
return frame;
}
-(NSView *)viewForTag:(int)tag {
NSView *view = nil;
switch (tag) {
case 0:
view = smallView;
break;
case 1:
view = mediumView;
break;
case 2: default:
view = largeView;
break;
}
return view;
}
- (BOOL)validateToolbarItem:(NSToolbarItem *)item {
if ([item tag] == currentViewTag) return NO;
else return YES;
}
-(void)awakeFromNib {
[[self window] setContentSize:[smallView frame].size];
[[[self window] contentView] addSubview:smallView];
[[[self window] contentView] setWantsLayer:YES];
}
-(IBAction)switchView:(id)sender {
int tag = [sender tag];
NSView *view = [self viewForTag:tag];
NSView *previousView = [self viewForTag:currentViewTag];
currentViewTag = tag;
NSRect newFrame = [self newFrameForNewContentView:view];
[NSAnimationContext beginGrouping];
if ([[NSApp currentEvent] modifierFlags] & NSShiftKeyMask)
[[NSAnimationContext currentContext] setDuration:1.0];
[[[[self window] contentView] animator] replaceSubview:previousView with:view];
[[[self window] animator] setFrame:newFrame display:YES];
[NSAnimationContext endGrouping];
}
Seems that you also need to change NSWidnow frame apart from contenView.
Also as for me for switching views by click events its better to use NSTabView.

Animate NSSplitview and window size

What I'm trying to achieve is that when the user press a button the window grows and the extra space is taken by a panel of NSSplitview that uncollapses and grows to fill the space.
I can easily animate the window resize and the splitview growing independently but when I try to put the two animation together one inevitably happens before the other or by steps. First the window partly resize, then the splitview resize follow, then the window finish resizing and finally the splitview finishes as well. Any ides on why that might be happening?
Here is the code I use:
- (IBAction)helpButtonPressed:(id)sender
{
if ([sender isKindOfClass:[NSMenuItem class]]) [sender setState:![sender state]];
NSWindow *window = [[[self windowControllers] objectAtIndex:0] window];
NSRect oldFrame = [window frame];
CGFloat windowWidthAdd;
if ([sender state]) windowWidthAdd = HELP_WIDTH; else windowWidthAdd = -HELP_WIDTH;
NSRect newFrame = NSMakeRect(oldFrame.origin.x, oldFrame.origin.y,oldFrame.size.width+windowWidthAdd, oldFrame.size.height);
[[NSAnimationContext currentContext] setDuration:0.3f];
[NSAnimationContext beginGrouping];
[[window animator] setFrame:newFrame display:YES];
if ([sender state]) [self uncollapseRightView]; else [self collapseRightView];
[NSAnimationContext endGrouping];
}
-(void)collapseRightView
{
NSView *right = [[self.splitView subviews] objectAtIndex:1];
NSView *left = [[self.splitView subviews] objectAtIndex:0];
NSRect leftFrame = [left frame];
NSRect overallFrame = [self.splitView frame];
[right setHidden:YES];
[[left animator] setFrameSize:NSMakeSize(overallFrame.size.width,leftFrame.size.height)];
}
-(void)uncollapseRightView
{
NSView *left = [[self.splitView subviews] objectAtIndex:0];
NSView *right = [[self.splitView subviews] objectAtIndex:1];
[right setHidden:NO];
CGFloat dividerThickness = [self.splitView dividerThickness];
// get the different frames
NSRect leftFrame = [left frame];
// Adjust left frame size
leftFrame.size.width = (leftFrame.size.width-HELP_WIDTH-dividerThickness);
NSRect rightFrame = [right frame];
rightFrame.origin.x = leftFrame.size.width + dividerThickness;
rightFrame.size.width = HELP_WIDTH;
[[left animator] setFrameSize:leftFrame.size];
[[right animator] setFrame:rightFrame];
}
If you are looking a little closer at the class reference of NSSplitView, you will recognize it conforms to NSAnimatablePropertyContainer. That means NSSplitView will provide you with an "animating proxy of itself". If you call animator on your NSSplitView you'll get this proxy, on which you should be able to change properties in an animated fashion.
For adjusting the animation duration and timing function, use NSAnimationContext the same way you already did.
Last but not least: Did you recognize NSSplitView's minPossiblePositionOfDividerAtIndex: and maxPossiblePositionOfDividerAtIndex:?

Remove Multiple instances of NSView

I have this code which draws a Window and NSView, and have set up tracking to when mouse entered and exits it increases/decreases the size of my window. The problem is when the mouse events are called and the int value of the width and height are increased, the window redraws itself to the new hight and leaves the old one there, how can I remove the old one and just draw the new one? thanks!
- (void)toggleHelpDisplay:(int)value
{
// Create helpWindow.
NSRect mainFrame = [[NSScreen mainScreen] frame];
NSRect helpFrame = NSZeroRect;
float width = 90;
float height = 90;
helpFrame.origin.x = (mainFrame.size.width - width) / 2.0;
helpFrame.origin.y = 200.0;
helpFrame.size.width = width + value;
helpFrame.size.height = height + value;
helpWindow = [[BrightnessView windowWithFrame:helpFrame] retain];
// Configure window.
[helpWindow setReleasedWhenClosed:YES];
[helpWindow setHidesOnDeactivate:NO];
[helpWindow setCanHide:NO];
[helpWindow setCollectionBehavior:NSWindowCollectionBehaviorCanJoinAllSpaces];
[helpWindow setIgnoresMouseEvents:NO];
// Configure contentView.
NSView *contentView = [helpWindow contentView];
[contentView setWantsLayer:YES];
CATextLayer *layer = [CATextLayer layer];
layer.opacity = 0;
[contentView setLayer:layer];
CGColorRef bgColor = CGColorCreateGenericGray(0.0, 0.6);
layer.backgroundColor = bgColor;
CGColorRelease(bgColor);
layer.string = (shadyEnabled) ? HELP_TEXT : HELP_TEXT_OFF;
layer.contentsRect = CGRectMake(0, 0, 1, 1);
layer.fontSize = 40.0;
layer.foregroundColor = CGColorGetConstantColor(kCGColorWhite);
layer.borderColor = CGColorGetConstantColor(kCGColorWhite);
layer.borderWidth = 4.0;
layer.cornerRadius = 4.0;
layer.alignmentMode = kCAAlignmentCenter;
[window addChildWindow:helpWindow ordered:NSWindowAbove];
float helpOpacity = (([NSApp isActive] ? 1 : 0));
[[[helpWindow contentView] layer] setOpacity:helpOpacity];
//track mouse so that once hovered make larger.
self.helpView = contentView;
trackingArea = [[[NSTrackingArea alloc] initWithRect:[self.helpView bounds]
options:NSTrackingMouseEnteredAndExited | NSTrackingActiveAlways
owner:self
userInfo:nil] autorelease];
[self.helpView addTrackingArea:trackingArea];
}
- (void)mouseEntered:(NSEvent *)event;
{
NSLog(#"entered");
[self toggleHelpDisplay:+100];
}
- (void)mouseExited:(NSEvent *)event;
{
NSLog(#"exited");
[self toggleHelpDisplay:-100];
}
It looks like you're recreating your help window every time your mouse enters or exits, when all you want to do is change its frame. Why not use the code you have to create the window once, and in your mouseDown method just change the frame with setFrame:display:
[helpWindow setFrame:NSMakeRect(helpWindow.frame.origin.x,helpWindow.frame.origin.y,helpWindow.size.width +100,helpWindow.size.height +100) display:YES];

Using NSPoint Mouse Tracking on NSView

I have this method in App Delegate that makes a window and content view but I want to be able to track the mouse using an NSPoint on entered and exiting the view. The problem is I do not want to make a NSView Custom class and want to do it all within my AppDelegate. The mouse tracking (at the bottom) does not work, any suggestions?
- (void)toggleHelpDisplay
{
// Create helpWindow.
NSRect mainFrame = [[NSScreen mainScreen] frame];
NSRect helpFrame = NSZeroRect;
float width = 75;
float height = 75;
helpFrame.origin.x = (mainFrame.size.width - width) / 2.0;
helpFrame.origin.y = 200.0;
helpFrame.size.width = width;
helpFrame.size.height = height;
helpWindow = [[BrightnessView windowWithFrame:helpFrame] retain];
// Configure window.
[helpWindow setReleasedWhenClosed:YES];
[helpWindow setHidesOnDeactivate:NO];
[helpWindow setCanHide:NO];
[helpWindow setCollectionBehavior:NSWindowCollectionBehaviorCanJoinAllSpaces];
[helpWindow setIgnoresMouseEvents:NO];
// Configure contentView.
NSView *contentView = [helpWindow contentView];
[contentView setWantsLayer:YES];
CATextLayer *layer = [CATextLayer layer];
layer.opacity = 0;
[contentView setLayer:layer];
CGColorRef bgColor = CGColorCreateGenericGray(0.0, 0.6);
layer.backgroundColor = bgColor;
CGColorRelease(bgColor);
layer.string = ? HELP_TEXT : HELP_TEXT_OFF;
layer.contentsRect = CGRectMake(0, 0, 1, 1.2);
layer.fontSize = 40.0;
layer.foregroundColor = CGColorGetConstantColor(kCGColorWhite);
layer.borderColor = CGColorGetConstantColor(kCGColorWhite);
layer.borderWidth = 4.0;
layer.cornerRadius = 4.0;
layer.alignmentMode = kCAAlignmentCenter;
[window addChildWindow:helpWindow ordered:NSWindowAbove];
float helpOpacity = (([NSApp isActive] ? 1 : 0));
[[[helpWindow contentView] layer] setOpacity:helpOpacity];
//track mouse so that once hovered make larger.
NSPoint mouseLocation = [[self window] mouseLocationOutsideOfEventStream];
if (NSPointInRect(mouseLocation, helpFrame)) {
NSLog(#"mouse over");
}
else
{
NSLog(#"mouse not over");
}
}
You should be able to do this using NSTrackingArea; You'd do something like this (typed in the browser, not tested):
self.helpView = contentView; // Need to store a reference to the view if you want to convert from its coordinate system
// Set up a tracking area
NSTrackingArea *trackingArea = [[[NSTrackingArea alloc] initWithRect:[self.helpView bounds]
options:NSTrackingActiveInActiveApp | NSTrackingMouseEnteredAndExited | NSTrackingMouseMoved
owner:self
userInfo:nil] autorelease];
[self.helpView addTrackingArea:trackingArea];
- (void)mouseEntered:(NSEvent *)event;
{
NSPoint location = [self.helpView convertPoint:[event locationInWindow] fromView:nil];
// Do whatever you want to do in response to mouse entering
}
- (void)mouseExited:(NSEvent *)event;
{
NSPoint location = [self.helpView convertPoint:[event locationInWindow] fromView:nil];
// Do whatever you want to do in response to mouse exiting
}
- (void)mouseMoved:(NSEvent *)event;
{
NSPoint location = [self.helpView convertPoint:[event locationInWindow] fromView:nil];
// Do whatever you want to do in response to mouse movements
}

Non-dirty view gets redrawn

I do have a strange problem. I programmed a a dockview.
It's basically a NSView with three vertical subviews inside. So I have a left dock, a middle view and a right dock. Both docks are collapsable. The middle view gets resized while collapsing.
I'm using CoreAnimation to animate the collapsing process. This works almost correct.
If I uncollapse the left dock, the right docks drawRect method gets called although it's not dirty.
If I deactivate the animator and set the new frame size directly, the right dock not gets redrawn while uncollapsing so I assume the calculation of the frames is correct.
Here is my code for collapsing the docks:
- (void) mouseDown:(NSEvent *)event
{
// Exit method if already animating a dock
if(is_animating)
{
return;
}
NSPoint firstPoint = [self convertPoint:[event locationInWindow] fromView:nil];
unsigned long divider = [self mousePointInCollapseRect:firstPoint];
// If not clicked on a divider don't animate anything
if(divider == NSNotFound)
{
return;
}
NSView* main_view = [[self subviews] objectAtIndex:VIEW_MIDDLE];
NSView* collapse_view = [[self subviews] objectAtIndex:(divider == 0)? VIEW_LEFT : VIEW_RIGHT];
NSRect cframe = [collapse_view frame]; // New collapsable frame
NSRect mframe = [main_view frame]; // New main frame
if([self isSubviewCollapsed:collapse_view] == NO)
{
if(divider == 0)
{
cframe.size.width = 0.0;
mframe.origin.x -= (dock_width_left + DIVIDER_THICKNESS);
mframe.size.width += (dock_width_left + DIVIDER_THICKNESS);
}
else
{
mframe.size.width += (collapse_view.frame.size.width + DIVIDER_THICKNESS);
cframe.size.width = 0.0;
cframe.origin.x = mframe.origin.x + mframe.size.width;
}
// Turn off autoresizesSubviews on the collapsible subview
[collapse_view setAutoresizesSubviews:NO];
[NSAnimationContext beginGrouping];
[[NSAnimationContext currentContext] setDuration:(ANIMATION_DURATION)];
[[collapse_view animator] setFrame:cframe];
[[main_view animator] setFrame:mframe];
[NSAnimationContext endGrouping];
}
else
{
// Uncollapse dock
if(divider == 0)
{
// Left dock
cframe.size.width = dock_width_left;
mframe.origin.x = (COLLAPSEBAR_THICKNESS + dock_width_left + DIVIDER_THICKNESS);
mframe.size.width -= (dock_width_left + DIVIDER_THICKNESS);
}
else
{
// Right dock
mframe.size.width -= (dock_width_right + DIVIDER_THICKNESS);
cframe.size.width = dock_width_right;
cframe.origin.x = mframe.origin.x + mframe.size.width + DIVIDER_THICKNESS;
}
[NSAnimationContext beginGrouping];
[[NSAnimationContext currentContext] setDuration:ANIMATION_DURATION];
[[main_view animator] setFrame:mframe];
[[collapse_view animator] setFrame:cframe];
[NSAnimationContext endGrouping];
//Restore Autoresizing on subview
[self performSelector:#selector(restoreAutoResizingOfSubview:) withObject:collapse_view afterDelay:ANIMATION_DURATION + 0.05];
}
is_animating = YES;
[self performSelector:#selector(animationEnded) withObject:nil afterDelay:ANIMATION_DURATION + 0.05];
}
So why gets a non-dirty view redrawn?