NSView draw permanently to background - objective-c

I'm trying to draw some things on the background of my windows. Therefore I subclassed the NSView of the window and added some drawing code like this:
- (void)drawRect:(NSRect)dirtyRect {
float color = 0.95;
[[NSColor colorWithDeviceRed:color green:color blue:color alpha:1.0] set];
NSRectFill(NSMakeRect(320, 0, 220, NSHeight(dirtyRect)-60));
}
This works great, but as soon as I open a NSComboBox or if I activate a checkbox, the background of these elements erases my just drawn rect.
I don't understand this, because checking for example the checkbox causes, that drawRect is called (I added a NSLog). Only resizing the window draws my rect again.
EDIT:
here is a screenshot of the problem:

I sometimes face the same problem. I think the following is what I use.
/// .h
#interface BackgroundView1 : NSView {
NSImage *myImage;
}
// .m
- (void)awakeFromNib {
[self setupBackgroundImage];
}
- (void)setupBackgroundImage {
NSColor *c = [NSColor colorWithDeviceRed:0.0f/255.0f green:55.0f/255.0f blue:150.0f/255.0f alpha:1.0f];
if (myImage == nil)
myImage = [self createColorImage:NSMakeSize(1,1):c];
}
- (void)drawRect:(NSRect)rect {
[myImage drawInRect:NSMakeRect([self bounds].origin.x,[self bounds].origin.y,[self frame].size.width,[self frame].size.height)
fromRect:NSMakeRect(0,0,[myImage size].width, [myImage size].height)
operation:NSCompositeCopy
fraction:1.0];
}
// Start Functions //
- (NSImage *)createColorImage:(NSSize)size :(NSColor *)color {
NSImage *image = [[NSImage alloc] initWithSize:size];
NSBitmapImageRep *rep = [[NSBitmapImageRep alloc]
initWithBitmapDataPlanes:NULL
pixelsWide:size.width
pixelsHigh:size.height
bitsPerSample:8
samplesPerPixel:4
hasAlpha:YES
isPlanar:NO
colorSpaceName:NSCalibratedRGBColorSpace
bytesPerRow:0
bitsPerPixel:0];
[image addRepresentation:rep];
[image lockFocus]; // Lock focus of image, making it a destination for drawing
[color set];
NSRectFill(NSMakeRect(0,0,size.width,size.height));
[image unlockFocus];
return image;
}
// End Functions //

Related

NSMenuitem tempelate image tint color not changing

I have customview which has image view image i'm setting NSImageNameMenuOnStateTemplate to imageview with tint color but tint color is not applying
NSImage *tintImage = [self tintedImage:[NSImage imageNamed:NSImageNameMenuOnStateTemplate] withTintColor:NSColor.whiteColor];
myimageView.image = tintImage
-(NSImage*)tintedImage:(NSImage*)image withTintColor:(NSColor*)color{
NSImage *tinted = [image copy];
[tinted lockFocus];
[color set];
NSRect imageRect = {NSZeroPoint, [image size]};
NSRectFillUsingOperation(imageRect, NSCompositingOperationSourceAtop);
[image unlockFocus];
return tinted;
}
Any help most appreciated ..
You really should avoid using lockFocus/unlockFocus. They're deprecated and you've misused them by locking one image and unlocking another. Use `+[NSImage imageWithSize:flipped:drawingHandler:]
As a category addition to NSImage:
- (NSImage *)imageWithSolidFillColor:(NSColor *)color
{
return [NSImage imageWithSize:self.size flipped:false drawingHandler:^BOOL(NSRect dstRect) {
[self drawInRect:dstRect fromRect:NSZeroRect operation:NSCompositingOperationSourceOver fraction:1.0];
[color set];
NSRectFillUsingOperation(dstRect, NSCompositeSourceAtop);
return YES;
}];
}
Change [image unlockFocus]; with [tinted unlockFocus];
for macOS you need to set setTemplate to NO
[tinted setTemplate:NO];
HTH

How to display animated GIF in Objective C on top of the layered View?

I am trying to draw animated gif on my screen in mac OSX app .
I used this code to insert the gif: I can see the Gif as 1 picture it doesn't animates
only static picture :( what should I add to make it animated ?
#import <Cocoa/Cocoa.h>
#import <Quartz/Quartz.h>//for drawing circle
#import "sharedPrefferences.h"
#interface GenericFanSubView : NSView
{
NSColor * _backgroundColor;
NSImageView* imageView;
}
- (void)setBackgroundColor :(NSColor*)color;
- (void)insertGif1;
- (void)insertGif2;
- (void)insertGif3;
#end
#import "GenericFanSubView.h"
#define PI 3.14285714285714
#implementation GenericFanSubView
- (id)initWithFrame:(NSRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code here.
imageView = [[NSImageView alloc]initWithFrame:CGRectMake(0, 0,self.frame.size.width,self.frame.size.height)];
[imageView setAnimates: YES];
}
return self;
}
- (void)drawRect:(NSRect)dirtyRect
{
[super drawRect:dirtyRect];
// Drawing code here.
[self drawCircleInRect];
_backgroundColor = [NSColor whiteColor];
[self insertGif1];
}
-(void)drawCircleInRect
{
//draw colored circle here
CGContextRef context = [[NSGraphicsContext // 1
currentContext] graphicsPort];
// ********** Your drawing code here ********** // 2
CGContextSetFillColorWithColor(context,[self NSColorToCGColor:(_backgroundColor)]);
float radius1 = self.frame.size.height/2;
float startAngle = 0;
float endAngle = endAngle = PI*2;
CGPoint position = CGPointMake(self.frame.size.height/2,self.frame.size.height/2);//center of the view
CGContextBeginPath(context);
CGContextAddArc(context, position.x, position.y, radius1, startAngle, endAngle, 1);
CGContextDrawPath(context, kCGPathFill); // Or kCGPathFill
}
- (void)setBackgroundColor :(NSColor*)color
{
_backgroundColor = color;
[self setNeedsDisplay:YES];
}
- (CGColorRef)NSColorToCGColor:(NSColor *)color
{
NSInteger numberOfComponents = [color numberOfComponents];
CGFloat components[numberOfComponents];
CGColorSpaceRef colorSpace = [[color colorSpace] CGColorSpace];
[color getComponents:(CGFloat *)&components];
CGColorRef cgColor = CGColorCreate(colorSpace, components);
return cgColor;
}
//curentlly calling only this 1
- (void)insertGif1
{
[imageView removeFromSuperview];
[imageView setImageScaling:NSImageScaleNone];
[imageView setAnimates: YES];
imageView.image = [NSImage imageNamed:#"FanBlades11.gif"];
[self addSubview:imageView];
}
#end
Edit: I discovered the source of the problem:
I was adding my class (that represents gif inside the circle) on top of RMBlurredView
and the animations doesn't work when I adding it as subview ,However it works on all the other views I added.
Any ideas what could be the reason inside the RMBlurredView to stop my NSImageView from animating ?
Edit:
I think [self setWantsLayer:YES]; is the reason I am not getting animations
how can I still get the animation with this feature enabled?
Edit:
Here is a simple sample with my problem
http://snk.to/f-cdk3wmfn
my gif:This is my gif it is invisible on white background color
"You must disable the autoscaling feature of the NSImageView for the
animation playback to function. After you've done that, no extra
programming required. It works like a charm!"
--http://www.cocoabuilder.com/archive/cocoa/108530-nsimageview-and-animated-gifs.html
imageView.imageScaling = NSImageScaleNone;
imageView.animates = YES;
needed for layer backed views:
if the image view is in a layer backed view or is layer backed itself:
imageView.canDrawSubviewsIntoLayer = YES;
working example using the question's own gif:
NSImageView *view = [[NSImageView alloc] initWithFrame:CGRectMake(10, 10, 50, 50)];
view.imageScaling = NSImageScaleNone;
view.animates = YES;
view.image = [NSImage imageNamed:#"FanBlades2_42x42.gif"];
view.canDrawSubviewsIntoLayer = YES;
NSView *layerview = [[NSView alloc] initWithFrame:CGRectMake(0, 0, 60, 60)];
layerview.wantsLayer = YES;
[layerview addSubview:view];
[self.window.contentView addSubview:layerview];

Cache NSView Context

I'm trying to store NSView Current Context and draw it later to NSView again. I was wondering what is the fastest and most efficient way to do this?
You can draw your content to an NSImage and just redraw the image later, but you need to invalidate the cache when needed (this depends on what your view does).
Example:
#interface SOCacheView : NSView
#end
#implementation SOCacheView
{
NSImage *_cache;
}
- (void)drawRect:(NSRect)dirtyRect
{
[super drawRect:dirtyRect];
if (!_cache) [self prepareImage];
[_cache drawAtPoint:NSZeroPoint fromRect:self.bounds operation:NSCompositeSourceOver fraction:1.0];
}
- (void)prepareImage
{
_cache = [[NSImage alloc] initWithSize:self.bounds.size];
[_cache lockFocus];
// do the drawing here...
[[NSColor blueColor] setFill];
NSRectFill(NSMakeRect(0, 0, NSWidth(self.bounds)/2, NSHeight(self.bounds)));
[[NSColor redColor] setFill];
NSRectFill(NSMakeRect(NSWidth(self.bounds)/2, 0, NSWidth(self.bounds)/2, NSHeight(self.bounds)));
[_cache unlockFocus];
}
To invalidate the cached drawing, just set _cache to nil.
Here's a sample project for you to play with 😉

Objective C - Help with changing background color when UIButton is pressed

I am new to programing and any help is appreciated. I am trying to change the background color of a button once it has been pressed. I have tried setBackgroundColor without success. I am not sure that it is compatible with UIButton. Is there any way to programatically accomplish such a task? All thoughts and suggestions are appreciated.
I woudl suggest creating a simple image that contains the background color you want and setting that via the existing methods in the UIButton. (check Wrights Answer for the doc link).
UIButton* button = [UIButton buttonWithType:UIButtonTypeCustom];
NSString* fileLocation = [[NSBundle mainBundle] pathForResource:#"buttonBG" ofType:#"png"];
UIImage* bgImage = [UIImage imageWithContentsOfFile:fileLocation];
if (bgImage != nil) { // check if the image was actually set
[button setBackgroundImage:bgImage forState:UIControlStateHighlighted];
} else {
NSLog(#"Error trying to read the background image");
}
That should do the trick. There might be an even better way to create the necessary image on the fly, but that's stuff I'm not firm in.
[edit: a bit more verbose code ]
Assuming you have an unadorned custom button with a title of "On" for the normal state:
- (IBAction) toggleButtonState {
if ([toggleButton titleForState:UIControlStateNormal] == #"On") {
[toggleButton setTitle: #"Off" forState:UIControlStateNormal];
[toggleButton setBackgroundColor:[UIColor redColor]];
}
else {
[toggleButton setTitle: #"On" forState:UIControlStateNormal];
[toggleButton setBackgroundColor:[UIColor greenColor]];
}
}
All the other buttons have an image placed in front of the view, so at most you'll see the corners change if the image doesn't completely fill the space.
I'd also suggest using an image, but for learning purposes, this will work.
Ive just been having the same issue and ended up using a UIButton subclass to tackle the issue. I used gradients simply because it looked a bit better if you have no need for them you can simply remove them. I have explained the process I used and included the full code at the bottom of the post.
Firstly add properties for the layers.I created two layers one for the base gradient and one for a gloss to add a little bit of style.
#interface gradientButton()
#property (nonatomic, strong) CAGradientLayer* gradientLayer;
#property (nonatomic, strong) CAGradientLayer* glossyLayer;
#end
Then either in -(void)awakeFromNib or in -(id)initWithFrame:(CGRect)frame
,depending on if you will load from storyboard or code respectively, configure the gradients and add the layers, round your corners off and customize the font highlight color.
-(void)awakeFromNib
{
_gradientLayer = [[CAGradientLayer alloc] init];
_gradientLayer.bounds = self.bounds;
_gradientLayer.position = CGPointMake(self.bounds.size.width/2, self.bounds.size.height/2);
[self.layer insertSublayer:_gradientLayer atIndex:0];
self.layer.cornerRadius = 5.0f;
self.layer.masksToBounds = YES;
self.layer.borderWidth = 1.0f;
_glossyLayer = [[CAGradientLayer alloc] init];
_glossyLayer.bounds = CGRectMake(0, 0, self.bounds.size.width, self.bounds.size.height/2);
_glossyLayer.position = CGPointMake(self.bounds.size.width/2, self.bounds.size.height/4);
[self.layer addSublayer:_glossyLayer];
[self setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
[self setTitleColor:[UIColor yellowColor] forState:UIControlStateHighlighted];
}
Next, override - (void)drawRect:(CGRect)rect to apply your layers and define your colors.
#define GRADIENT_TOP [UIColor colorWithRed:38.0/255.0 green:78.0/255.0 blue:54.0/255.0 alpha:1]
#define GRADIENT_BOTTOM [UIColor colorWithRed:44.0/255.0 green:71.0/255.0 blue:56.0/255.0 alpha:1]
#define GLOSS_TOP [UIColor colorWithRed:0.70f green:0.70f blue:0.70f alpha:0.95f]
#define GLOSS_BOTTOM [UIColor colorWithRed:0.70f green:0.70f blue:0.70f alpha:0.35f]
#define GRADIENT_SELECTED_TOP [UIColor colorWithRed:138.0/255.0 green:178.0/255.0 blue:154.0/255.0 alpha:1]
#define GRADIENT_SELECTED_BOTTOM [UIColor colorWithRed:114.0/255.0 green:171.0/255.0 blue:156.0/255.0 alpha:1]
- (void)drawRect:(CGRect)rect
{
[_gradientLayer setColors:#[(id)[GRADIENT_TOP CGColor],(id)[GRADIENT_BOTTOM CGColor]]];
[_glossyLayer setColors:#[(id)[GLOSS_TOP CGColor], (id)[GLOSS_BOTTOM CGColor]]];
[super drawRect:rect];
}
Finally, and the bit we've all been waiting for, override -(void)setHighlighted:(BOOL)highlighted{
so we can apply the highlight effect were looking for.
-(void)setHighlighted:(BOOL)highlighted{
[super setHighlighted:highlighted];
if(highlighted)
[_gradientLayer setColors:#[(id)[GRADIENT_SELECTED_TOP CGColor],(id)[GRADIENT_SELECTED_BOTTOM CGColor]]];
else
[_gradientLayer setColors:#[(id)[GRADIENT_TOP CGColor],(id)[GRADIENT_BOTTOM CGColor]]];
}
There we have it, now just drag out a UIButton modify the class and your all good. Heres the full Implementation so you can copy it straight out. http://pastebin.com/nUVeujyp
Check out the UIButton Class Reference.
Regular UIButtons do not have the backgroundColor option.
My suggestion would to use the UISegmentedControl, which has the tinColor option.
I have created a subclass which get background color and creates an UIImage for each state.
For me it's more useful a subclass instead a category, so that's up to you.
#implementation ROCRoundColorButton
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
UIColor *darkColor;
darkColor = [self darkColorFromBackgroundColor];
[self setBackgroundImage:[self imageWithColor:self.backgroundColor withSize:self.frame.size] forState:UIControlStateNormal];
[self setBackgroundImage:[self imageWithColor:darkColor withSize:self.frame.size] forState:UIControlStateSelected];
}
return self;
}
-(id)initWithCoder:(NSCoder *)aDecoder
{
self = [super initWithCoder:aDecoder];
if (self) {
UIColor *darkColor;
darkColor = [self darkColorFromBackgroundColor];
[self setBackgroundImage:[self imageWithColor:self.backgroundColor withSize:self.frame.size] forState:UIControlStateNormal];
[self setBackgroundImage:[self imageWithColor:darkColor withSize:self.frame.size] forState:UIControlStateSelected];
}
return self;
}
/*
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect
{
// Drawing code
}
*/
#pragma mark -
#pragma mark Private methods
- (UIColor *)darkColorFromBackgroundColor
{
const float* components = CGColorGetComponents( self.backgroundColor.CGColor );
CGFloat red = components[0];
CGFloat green = components[1];
CGFloat blue = components[2];
CGFloat alpha = components[3];
if (red > 0) {
red -= 0.1;
}
if (green > 0) {
green -= 0.1;
}
if (blue > 0) {
blue -= 0.1;
}
UIColor *darkColor = [UIColor colorWithRed:red green:green blue:blue alpha:alpha];
return darkColor;
}
- (UIImage *)imageWithColor:(UIColor *)color withSize:(CGSize)size
{
UIGraphicsBeginImageContext(size);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetFillColorWithColor(context, [color CGColor]);
//CGContextFillRect(context, CGRectMake(0, 0, size.width, size.height));
UIBezierPath *roundedRect = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(0, 0, size.width, size.height) cornerRadius:5];
[roundedRect fillWithBlendMode: kCGBlendModeNormal alpha:1.0f];
[color setFill];
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return image;
}
In fact, you can use it in the storyboard, changing the class and setting de background color in the view.

cocoa hello world screensaver

I have been studying NSView and as such I thought I would give a shot at a screen saver. I have been able to display and image in an NSView but I can't seen to modify this example code to display a simple picture in ScreenSaverView.
http://www.mactech.com/articles/mactech/Vol.20/20.06/ScreenSaversInCocoa/
BTW great tutorial that works with Snow Leopard.
I would think to simply display an image I would need something that looked like this...
What am I doing wrong?
//
// try_screensaverView.m
// try screensaver
//
#import "try_screensaverView.h"
#implementation try_screensaverView
- (id)initWithFrame:(NSRect)frame isPreview:(BOOL)isPreview
{
self = [super initWithFrame:frame isPreview:isPreview];
if (self) {
[self setAnimationTimeInterval:1]; //refresh once per sec
}
return self;
}
- (void)startAnimation
{
[super startAnimation];
NSString *path = [[NSBundle mainBundle] pathForResource:#"leaf" ofType:#"JPG" inDirectory:#""];
image = [[NSImage alloc] initWithContentsOfFile:path];
}
- (void)stopAnimation
{
[super stopAnimation];
}
- (void)drawRect:(NSRect)rect
{
[super drawRect:rect];
}
- (void)animateOneFrame
{
//////////////////////////////////////////////////////////
//load image and display This does not scale the image
NSRect bounds = [self bounds];
NSSize newSize;
newSize.width = bounds.size.width;
newSize.height = bounds.size.height;
[image setSize:newSize];
NSRect imageRect;
imageRect.origin = NSZeroPoint;
imageRect.size = [image size];
NSRect drawingRect = imageRect;
[image drawInRect:drawingRect fromRect:imageRect operation:NSCompositeSourceOver fraction:1];
}
- (BOOL)hasConfigureSheet
{
return NO;
}
- (NSWindow*)configureSheet
{
return nil;
}
#end
NSRect bounds = [self bounds];
NSSize newSize;
newSize.width = bounds.size.width;
newSize.height = bounds.size.height;
[image setSize:newSize];
I don't know why you're doing this.
NSRect imageRect;
imageRect.origin = NSZeroPoint;
imageRect.size = [image size];
A.k.a. [self bounds].size.
NSRect drawingRect = imageRect;
[image drawInRect:drawingRect fromRect:imageRect operation:NSCompositeSourceOver fraction:1];
i.e., [image drawInRect:[self bounds] fromRect:[self bounds] operation:NSCompositeSourceOver fraction:1].
If you're trying to draw the image at its natural size, there's no reason to ever send it a setSize: message. Cut out that entire first part, and the rest should work just fine.
If you're trying to fill the screen (which would be scaling, which would contradict the comment), set the drawingRect to [self bounds], not imageRect. This does exactly as it reads:
image,
draw into (the bounds of the view),
from (the image's entire area).
[image
drawInRect:[self bounds]
fromRect:imageRect
â‹®
];
Neither the natural-size-fixed-position draw nor the full-screen draw is an effective screen saver. The latter is irredeemable; you can make the former useful by animating the image around the screen.
I had a similar issue, so I'll post my solution. OP was attempting to load image contents via NSBundle's mainBundle. Instead, you'll have more luck fetching the screensaver's bundle and loading files from there, like so:
NSBundle *saverBundle = [NSBundle bundleForClass:[self class]];
NSImage *image = [[NSImage alloc] initWithContentsOfFile:[saverBundle pathForResource:#"image" ofType:#"png"]];
Since you are doing your drawing in animateOneFrame, try removing your overridden drawRect.