SetNeedsDisplay not drawing NSRect - objective-c

My NSRect is being drawn in BrightnessView.m and it is connected to the custom class of NSView on a xib called "brightness". PanelController is connected to another xib called "controller" and that is where the slider is with the action. Panel Controller.h cannot have an outlet to the BrightnessView because it is not connected to the same xib as the one with the view. Instead of an outlet would I need just a pointer to the brightness view or will this be able to work?
I added a regular pointer but this wont work
//Panel Controller.h connected to files owner on xib "Controller"
#import "BrightnessView.h"
#interface PanelController : NSWindowController
{
BrightnessView *_myBrightnessView;
}
#property (assign) BrightnessView *BrightnessView;
#end
//Panel Controller.m
#synthesize BrightnessView = _myBrightnessView;
- (IBAction)sliderChange:(id)sender;
{
[_myBrightnessView.self setNeedsDisplay:YES];
}
//BrightnessView.m connected to the NSView on xib "Brightness"
- (void)drawRect:(NSRect)theRect
{
NSLog(#"the view is drawn");
NSRect contentRect = NSInsetRect([self bounds], 0, 0);
NSBezierPath *path = [NSBezierPath bezierPath];
[path moveToPoint:NSMakePoint(_arrowX, NSMaxY(contentRect))];
[path lineToPoint:NSMakePoint(_arrowX / 2, NSMaxY(contentRect))];
[path lineToPoint:NSMakePoint(NSMaxX(contentRect), NSMaxY(contentRect))];
NSPoint topRightCorner = NSMakePoint(NSMaxX(contentRect), NSMaxY(contentRect));
[path curveToPoint:NSMakePoint(NSMaxX(contentRect), NSMaxY(contentRect))
controlPoint1:topRightCorner controlPoint2:topRightCorner];
[path lineToPoint:NSMakePoint(NSMaxX(contentRect), NSMinY(contentRect))];
NSPoint bottomRightCorner = NSMakePoint(NSMaxX(contentRect), NSMinY(contentRect));
[path curveToPoint:NSMakePoint(NSMaxX(contentRect), NSMinY(contentRect))
controlPoint1:bottomRightCorner controlPoint2:bottomRightCorner];
[path lineToPoint:NSMakePoint(NSMinX(contentRect), NSMinY(contentRect))];
[path curveToPoint:NSMakePoint(NSMinX(contentRect), NSMinY(contentRect))
controlPoint1:contentRect.origin controlPoint2:contentRect.origin];
[path lineToPoint:NSMakePoint(NSMinX(contentRect), NSMaxY(contentRect))];
NSPoint topLeftCorner = NSMakePoint(NSMinX(contentRect), NSMaxY(contentRect));
[path curveToPoint:NSMakePoint(NSMinX(contentRect), NSMaxY(contentRect))
controlPoint1:topLeftCorner controlPoint2:topLeftCorner];
[path lineToPoint:NSMakePoint(_arrowX / 2, NSMaxY(contentRect))];
[path closePath];
[the_Color setFill];
[path fill];
[NSGraphicsContext saveGraphicsState];
NSBezierPath *clip = [NSBezierPath bezierPathWithRect:[self bounds]];
[clip appendBezierPath:path];
[clip addClip];
[NSGraphicsContext restoreGraphicsState];
}
- (void)setArrowX:(NSInteger)value
{
_arrowX = value;
}
www.mediafire.com/?66q8jv3vly1cch4 is an example of this project and how it will not work.

You are creating a new instance of BrightnessView every time the user moves the slider. That is wrong. You should be using your existing BrightnessView instance. Your PanelController needs a property or instance variable that points to the existing instance.

Related

NSPopUpButtonCell Subclass Animates Closed With Standard Appearance

I've created an NSPopUpButtonCell subclass to customize its appearance.
When one of the menu items is selected from the popup it animates the selection with the standard popup appearance on OS X 10.10.
I'd like it to animate with my custom appearance.
Customized PopUp
Animating Selection
My Implementation
- (void)drawBezelWithFrame:(NSRect)frame inView:(NSView *)controlView {
[[NSGraphicsContext currentContext] saveGraphicsState];
NSBezierPath *rectanglePath = [NSBezierPath bezierPathWithRoundedRect:frame xRadius:5.0 yRadius:5.0];
[[NSColor colorWithWhite:0.7 alpha:1.0] setFill];
[rectanglePath fill];
float width = frame.size.width;
float height = frame.size.height;
NSBezierPath *path = [NSBezierPath bezierPath];
[path moveToPoint:CGPointMake(width - 5, height/2 - 2)];
[path lineToPoint:CGPointMake(width - 10, height/2 - 7)];
[path lineToPoint:CGPointMake(width - 15, height/2 - 2)];
[path moveToPoint:CGPointMake(width - 5, height/2 + 2)];
[path lineToPoint:CGPointMake(width - 10, height/2 + 7)];
[path lineToPoint:CGPointMake(width - 15, height/2 + 2)];
[path setLineWidth:2.0];
[[NSColor darkGrayColor] setStroke];
[path stroke];
[NSGraphicsContext restoreGraphicsState];
}
You have to turn off bordered property on Interface Builder.
If bordered is actualy off, you have to switch it on and next off.
Better way is use
- (void)drawBorderAndBackgroundWithFrame:(NSRect)cellFrame inView:(NSView *)controlView

Draw line in Cocoa?

How i can draw a line in a specific window after a button click?
i'm using this:
NSBezierPath * path = [NSBezierPath bezierPath];
[path setLineWidth: 4];
NSPoint startPoint = { 21, 21 };
NSPoint endPoint = { 128,128 };
[path moveToPoint: startPoint];
[path lineToPoint:endPoint];
[[NSColor redColor] set];
[path stroke];
but it work only if i put it in the:
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
how i can solve this?
my goal is to create an application that can draw lines , according to the details(coordinate) received
thank you.
You should not be drawing outside of a view or layer's drawing method (e.g. drawRect:). What you want to do, in broad strokes, is have a view there that draws the line when a flag is set, and when you click the button, set the flag and tell the view to redraw.
When you click the mouse event. This code will create line, curve and draw.
#import <Cocoa/Cocoa.h>
#interface BezierView : NSView {
NSPoint points[4];
NSUInteger pointCount;
}
#end
#import "BezierView.h"
#implementation BezierView
- (id)initWithFrame:(NSRect)frame {
self = [super initWithFrame:frame];
if (self) {
// Initialization code here.
}
return self;
}
- (void)drawRect:(NSRect)rect
{
NSBezierPath *control1 = [NSBezierPath bezierPath];
[control1 moveToPoint: points[0]];
[control1 lineToPoint: points[1]];
[[NSColor redColor] setStroke];
[control1 setLineWidth: 2];
[control1 stroke];
NSBezierPath *control2 = [NSBezierPath bezierPath];
[control2 moveToPoint: points[2]];
[control2 lineToPoint: points[3]];
[[NSColor greenColor] setStroke];
[control2 setLineWidth: 2];
[control2 stroke];
NSBezierPath *curve = [NSBezierPath bezierPath];
[curve moveToPoint: points[0]];
[curve curveToPoint: points[3]
controlPoint1: points[1]
controlPoint2: points[2]];
[[NSColor blackColor] setStroke];
CGFloat pattern[] = {4, 2, 1, 2};
[curve setLineDash: pattern
count: 4
phase: 1];
[[NSColor grayColor] setFill];
[curve fill];
[curve stroke];
}
- (void)mouseDown: (NSEvent*)theEvent
{
NSPoint click = [self convertPoint: [theEvent locationInWindow]
fromView: nil];
points[pointCount++ % 4] = click;
if (pointCount % 4 == 0)
{
[self setNeedsDisplay: YES];
}
}
#end

Update NSRect Color or Redraw

what I am trying to do is have an NSSlider and whenever I change the value I want it to update the color of my NSRect. Will I need to remove my NSRect and redraw it every time I want to change the color or how would I go about this? If I have to remove the Rect and redraw it how would I do that?
Thanks!
Heres the Method
arrowX is just an integer but what I want to change with the slider is the [[NSColor colorWithDeviceWhite:0 alpha:0.4] setFill]; specifically the alpha value between 0 and 1
- (void)drawRect:(NSRect)theRect
{
NSRect contentRect = NSInsetRect([self bounds], 0, 0);
NSBezierPath *path = [NSBezierPath bezierPath];
[path moveToPoint:NSMakePoint(_arrowX, NSMaxY(contentRect))];
[path lineToPoint:NSMakePoint(_arrowX / 2, NSMaxY(contentRect))];
[path lineToPoint:NSMakePoint(NSMaxX(contentRect), NSMaxY(contentRect))];
NSPoint topRightCorner = NSMakePoint(NSMaxX(contentRect), NSMaxY(contentRect));
[path curveToPoint:NSMakePoint(NSMaxX(contentRect), NSMaxY(contentRect))
controlPoint1:topRightCorner controlPoint2:topRightCorner];
[path lineToPoint:NSMakePoint(NSMaxX(contentRect), NSMinY(contentRect))];
NSPoint bottomRightCorner = NSMakePoint(NSMaxX(contentRect), NSMinY(contentRect));
[path curveToPoint:NSMakePoint(NSMaxX(contentRect), NSMinY(contentRect))
controlPoint1:bottomRightCorner controlPoint2:bottomRightCorner];
[path lineToPoint:NSMakePoint(NSMinX(contentRect), NSMinY(contentRect))];
[path curveToPoint:NSMakePoint(NSMinX(contentRect), NSMinY(contentRect))
controlPoint1:contentRect.origin controlPoint2:contentRect.origin];
[path lineToPoint:NSMakePoint(NSMinX(contentRect), NSMaxY(contentRect))];
NSPoint topLeftCorner = NSMakePoint(NSMinX(contentRect), NSMaxY(contentRect));
[path curveToPoint:NSMakePoint(NSMinX(contentRect), NSMaxY(contentRect))
controlPoint1:topLeftCorner controlPoint2:topLeftCorner];
[path lineToPoint:NSMakePoint(_arrowX / 2, NSMaxY(contentRect))];
[path closePath];
//SETTING THE VALUE OF 1 = WHITE AND 0 = BLACK.
[[NSColor colorWithDeviceWhite:0 alpha:0.4] setFill];
[path fill];
[NSGraphicsContext saveGraphicsState];
NSBezierPath *clip = [NSBezierPath bezierPathWithRect:[self bounds]];
[clip appendBezierPath:path];
[clip addClip];
[NSGraphicsContext restoreGraphicsState];
}
You need to add an instance variable that stores your NSColor. In your slider's action method, set the instance variable to the new color, and send setNeedsDisplay:YES to the view.

Multiple instances of a method?

The following code renders a zombie with interchangeable coordinates. The only issue is that I have to duplicate the code and create new coordinate variables myself for each zombie. Is there any way I can create copies of the variables so I only have to have one method for multiple zombies verses three methods and three separate variables for three zombies?
int zombieyCord;
int zombiexCord;
-(void)renderZombie
{
NSBezierPath * path = [NSBezierPath bezierPath];
[path setLineWidth:4];
NSPoint center = {zombieyCord,zombiexCord};
[path moveToPoint: center];
[path appendBezierPathWithArcWithCenter:center
radius:5
startAngle:0
endAngle:360];
[[NSColor greenColor] set];
[path fill];
[[NSColor greenColor] set];
[path stroke];
}
Edit:
The spawning is great and works perfectly, but I want the zombies to move properly. Before, the code was that the method was called upon the player moving. This no longer works because I have to call an integer in the method.
+(void)entityZombie:(int)zombiexCord andY:(int)zombieyCord
{
/* Handle Zombie Movement*/
if (xcord != zombiexCord || ycord != zombieyCord)
{
if (xcord > zombiexCord){zombiexCord=zombiexCord+1;}
if (xcord < zombiexCord){zombiexCord=zombiexCord-1;}
if (ycord < zombieyCord){zombieyCord=zombieyCord-1;}
if (ycord > zombieyCord){zombieyCord=zombieyCord+1;}
/* Handle Zombie Render*/
NSBezierPath * path = [NSBezierPath bezierPath];
[path setLineWidth:4];
NSPoint center = {zombieyCord,zombiexCord};
[path moveToPoint: center];
[path appendBezierPathWithArcWithCenter:center
radius:5
startAngle:0
endAngle:360];
[[NSColor greenColor] set];
[path fill];
[[NSColor greenColor] set];
[path stroke];
}
}
Player movement handling just involves rendering the player then [Entity entityZombie];
But as said above, no longer works.
-(void)renderZombieWithX:(int)xCoord andY:(int)yCoord {
NSBezierPath * path = [NSBezierPath bezierPath];
[path setLineWidth:4];
NSPoint center = {xCoord,yCoord};
[path moveToPoint: center];
[path appendBezierPathWithArcWithCenter:center
radius:5
startAngle:0
endAngle:360];
[[NSColor greenColor] set];
[path fill];
[[NSColor greenColor] set];
[path stroke];
}
Call it like this:
[self renderZombieWithX:10 andY:20];
[self renderZombieWithX:13 andY:37];
//...
Further more there is a dedicated function for creating NSPoints:
NSPoint center = NSMakePoint(xCoord, yCoord);
which you might want to use in favor of:
NSPoint center = {xCoord,yCoord};
(see one of my answer's comments on why)
If performance is critical a cached approach like the following might bring performance gain:
static NSImage *sharedZombieStamp;
- (NSImage *)sharedZombie {
#synchronized(sharedZombieStamp) {
if (!sharedZombieStamp) {
CGFloat lineWidth = 4.0;
CGFloat radius = 5.0;
sharedZombieStamp = [[NSImage alloc] initWithSize:NSMakeSize((lineWidth + radius) * 2, (lineWidth + radius) * 2];
[zombieImage lockFocus];
NSBezierPath *path = [NSBezierPath bezierPath];
[path setLineWidth:4];
NSPoint center = NSMakePoint(lineWidth + radius, lineWidth + radius);
[path moveToPoint: center];
[path appendBezierPathWithArcWithCenter:center
radius:radius
startAngle:0
endAngle:360];
[[NSColor greenColor] set];
[path fill];
[[NSColor greenColor] set];
[path stroke];
[zombieImage unlockFocus];
}
}
return sharedZombieStamp;
}
-(void)renderZombieWithX:(int)xCoord andY:(int)yCoord {
NSImage *zombie = [self sharedZombie];
NSSize zombieSize = [zombie size];
[zombie drawAtPoint:NSMakePoint(xCoord - zombieSize.width / 2, yCoord - zombieSize.height / 2)
fromRect:NSMakeRect(0.0, 0.0, zombieSize.width, zombieSize.height)
operation:NSCompositeSourceOver
fraction:1.0];
}
This code is just out of my head though. Went thru no testing, whatsoever. Use with caution ;)

Drawing two objects in separate files

Player.m
//
// PlayerTestAppDelegate.m
// PlayerTest
//
// Created by Someguy on 5/13/11.
// Copyright 2011 Enginering. All rights reserved.
//
#import "Player.h"
#implementation Player
#synthesize window;
//[NSThread sleepForTimeInterval:.1];
// On the program finished loading, do the following..
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {plyr_size = 10; xcord = 200; ycord = 215; [self DrawPlayer];}
- (void)Clearscreen{NSPoint origin = {0,0}; NSRect rect; rect.origin = origin; rect.size.width=1000; rect.size.height=1000; NSBezierPath * path; path = [NSBezierPath bezierPathWithRect:rect]; [path setLineWidth:4]; [[NSColor whiteColor] set]; [path fill]; [[NSColor whiteColor] set]; [path stroke];}
- (void)DrawPlayer{[self Clearscreen]; NSBezierPath * path = [NSBezierPath bezierPath]; [path setLineWidth:4]; NSPoint center = {ycord,xcord}; [path moveToPoint: center]; [path appendBezierPathWithArcWithCenter: center radius: plyr_size startAngle: 0 endAngle: 360]; [[NSColor grayColor] set]; [path fill]; [[NSColor grayColor] set]; [path stroke];}
//Declare these actions
- (IBAction)PlayerMoveForward:(id)sender {[self PlayerMoveForward];}
- (IBAction)PlayerMoveBackwards:(id)sender {[self PlayerMoveBackwards];}
- (IBAction)PlayerMoveLeft:(id)sender {[self PlayerMoveLeft];}
- (IBAction)PlayerMoveRight:(id)sender {[self PlayerMoveRight];}
// Declare the Methods used for movement
- (void)PlayerMoveForward {xcord=xcord+10; [self DrawPlayer]; [self GetPlayerPostion];}
- (void)PlayerMoveBackwards {xcord=xcord-10; [self DrawPlayer]; [self GetPlayerPostion];}
- (void)PlayerMoveLeft {ycord=ycord-10; [self DrawPlayer]; [self GetPlayerPostion];}
- (void)PlayerMoveRight {ycord=ycord+10; [self DrawPlayer]; [self GetPlayerPostion];}
- (void)GetPlayerPostion {NSLog(#"Player cordinates (%i, %i)",xcord, ycord);}
#end
EntityZombie.m
//
// EntityZombie.m
// PlayerTest
//
// Created by Someguy on 5/13/11.
// Copyright 2011 Enginering. All rights reserved.
//
#import "EntityZombie.h"
#import "Player.h"
#implementation EntityZombie : Player
//- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {[self FollowPlayer];}
- (void)awakeFromNib{[self FollowPlayer];}
- (void)DrawZombie {if ([self CanSpawnZombie] == TRUE){NSBezierPath * path = [NSBezierPath bezierPath]; [path setLineWidth:4]; NSPoint center = {215,200}; [path moveToPoint: center]; [path appendBezierPathWithArcWithCenter: center radius: 18 startAngle: 0 endAngle: 360]; [[NSColor greenColor] set]; [path fill]; [[NSColor greenColor] set]; [path stroke];}}
- (void)FollowPlayer {
NSLog(#"Zombie is following player..");
//zombie_xcord == xcord-1;
//zombie_ycord == ycord-1;
[self DrawZombie];
}
- (BOOL)CanSpawnZombie{return TRUE;}
#end
The zombie will not render and I have the log of
2011-05-13 20:59:22.677 PlayerTest[45207:a0f] Zombie is following player..
Fri May 13 20:59:22 Someguy-MacBook-Pro.local PlayerTest[45207] <Error>: CGContextSetFillColorWithColor: invalid context 0x0
There is no current draw context. The issue is from where you are calling the draw methods. They need to be called from a -(void) drawRect method or, if you'd prefer, you can draw into an image. Either way, there has to be a current draw context.