NSMenuitem tempelate image tint color not changing - objective-c

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

Related

Color tinting an UIImage

I'm really stuck on tinting an UIImage with a certain tint. The UIImage is in a custom TableView cell (if that makes any difference).
Looking at the picture, the UIImage has been replaced by 0.5 opacity yellow and the original image has been discarded. I want a tint on an existing image, not overwrite the image entirely. (p.s: the numbers in the picture are labels that are on top of the image, they are unrelated).
I realize there are similiar threads on stackoverflow and I must have tried out all the code that there is :-) Nothing is working. I don't expect anyone to post any code, just a push in the right direction!
Explanation of the code below:
myImage is the image
color is the color, probably yellow, with an opacity of 0.5
I use [util tintedImageWithColor] with both myImage and color.
It comes out as the picture above.
Does anyone have an idea, a push in the right direction?
Updated
MasterViewController.m
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
ArticleCell *cell = [tableView dequeueReusableCellWithIdentifier:#"Cell" forIndexPath:indexPath];
Article *article = self.articles[indexPath.row];
Utils *util = [[Utils alloc] init];
// Populate cell components
UIImage *myImage = [UIImage imageWithData:article.post_image_thumbnail_data];
// Set image tint
UIColor *color = [UIColor colorWithRed:(160/15.0) green:(97/15.0) blue:(5/55.0) alpha:0.5];
myImage = [util tintedImageWithColor:(UIImage *) myImage:(UIColor *) color blendingMode:kCGBlendModeDestinationIn highQuality:YES];
cell.imageView.image = myImage;
return cell;
}
Utils.m
- (UIImage *)tintedImageWithColor:(UIImage*)image : (UIColor *)tintColor blendingMode:(CGBlendMode)blendMode highQuality:(BOOL) yerOrNo;
{
CGSize size = CGSizeMake(70, 70);
UIGraphicsBeginImageContextWithOptions(size, NO, 0.0f);
if (yerOrNo) {
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetShouldAntialias(context, true);
CGContextSetAllowsAntialiasing(context, true);
CGContextSetInterpolationQuality(context, kCGInterpolationHigh);
}
[tintColor setFill];
CGRect bounds = CGRectMake(0, 0, size.width, size.height);
UIRectFill(bounds);
[image drawInRect:bounds blendMode:blendMode alpha:1.0f];
if (blendMode != kCGBlendModeDestinationIn)
[image drawInRect:bounds blendMode:kCGBlendModeDestinationIn alpha:0.4];
UIImage *tintedImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return tintedImage;
}
Utils.h
- (UIImage *)tintedImageWithColor:(UIImage*)image : (UIColor *)tintColor blendingMode:(CGBlendMode)blendMode highQuality:(BOOL) yerOrNo;
I've found this piece on code in one of my projects, I don't know the exact source, but it should works, use kCGBlendModeDestinationIn:
- (UIImage *)tintedImageWithColor:(UIColor *)tintColor blendingMode:(CGBlendMode)blendMode highQuality:(BOOL) yerOrNo;
{
UIGraphicsBeginImageContextWithOptions(self.size, NO, 0.0f);
if (yerOrNo) {
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetShouldAntialias(context, true);
CGContextSetAllowsAntialiasing(context, true);
CGContextSetInterpolationQuality(context, kCGInterpolationHigh);
}
[tintColor setFill];
CGRect bounds = CGRectMake(0, 0, self.size.width, self.size.height);
UIRectFill(bounds);
[self drawInRect:bounds blendMode:blendMode alpha:1.0f];
if (blendMode != kCGBlendModeDestinationIn)
[self drawInRect:bounds blendMode:kCGBlendModeDestinationIn alpha:1.0];
UIImage *tintedImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return tintedImage;
}

NSView draw permanently to background

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 //

Image with rounded corners and border

Trying to get an image with rounded corners and stroke,
but there is something I do wrong, because app hangs during this:
- (UIImage *)roundedCornerImage:(NSInteger)radius{
UIGraphicsBeginImageContextWithOptions(self.size, NO, 0);
CGRect box = CGRectInset((CGRect){CGPointZero, self.size}, self.size.width * 0.9f, self.size.height * 0.9f);
UIBezierPath *ballBezierPath = [UIBezierPath bezierPathWithOvalInRect:box];
[[UIColor blackColor] setStroke];
[ballBezierPath setLineWidth:4.0];
[ballBezierPath stroke];
[[UIBezierPath bezierPathWithRoundedRect:(CGRect){CGPointZero, self.size}
cornerRadius:radius]addClip];
[self drawInRect:(CGRect){CGPointZero, self.size}];
UIImage* result = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return result;
}
Forget messing with the layer, it's a performance killer in scroll views. Just generate a new image instead:
+(UIImage *)makeRoundedImage:(UIImage *)image withRadius:(CGFloat)radius
{
CGRect itemFrame = CGRectMake(0, 0, radius*2, radius*2);
// The source image
UIImageView *imageView = [UIImageView new];
imageView.frame = itemFrame;
imageView.contentMode = UIViewContentModeScaleToFill;
[imageView setImage:image];
imageView.layer.cornerRadius = radius;
imageView.layer.masksToBounds = YES;
// Make an image of our client item
UIGraphicsBeginImageContextWithOptions(itemFrame.size, NO, 0.0);
[imageView.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *returnImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
// Fini
return returnImage;
}
Just use
[imageView.layer setCornerRadius:5.0f];
[imageView.layer setBorderWidth:5.0f];
Don;t forget to #import Quartzcore;

Clear before drawInRect for Mac App

I want to animate the width of my button in my Mac App and at the same time have a background image and a foreground image. For that I'm using #kgn's awesome library BBlock (https://github.com/kgn/BBlock).
Problem is that the background image seems to get drawn behind each other, so when the button scales down, you can't even see the background image animation and it seems to be cut off.
This way of "animating" the width change works if I just use setImage, but then I don't get the benefits of having a background image.
I've made a custom button that scales the button incrementally (basically animates it). It changes the size of the button by one pixel each run and changes the image, background image and alternate background image by one pixel as well:
- (void)scaleTestButtonUntilWidth:(int)width{
[NSTimer scheduledTimerRepeats:YES withTimeInterval:timeInterval andBlock:^{
if (self.width == width) {
return;
}
int newSize;
if (width > self.width) {
newSize = self.width += 1;
}else if (width < self.width){
newSize = self.width -= 1;
}
self.width = newSize;
[self setImage:self.image];
[self setAlternateBackgroundImage:[self alternateBGImage]];
[self setBackgroundImage:[self BGImage]];
}];
}
The setBackgroundImage looks like this and the implementation for setAlternateBackgroundImage is the same:
- (void)setBackgroundImage:(NSImage *)backgroundImage{
[self setImage:[self imageWithBackgroundImage:backgroundImage
andIcon:self.image]];
[self setButtonType:NSMomentaryChangeButton];
[self setBordered:NO];
}
Which calls the method that actually draws the image:
- (NSImage *)imageWithBackgroundImage:(NSImage *)background andIcon:(NSImage *)icon{
return [NSImage imageForSize:background.size withDrawingBlock:^{
NSRect bounds = NSZeroRect;
bounds.size = background.size;
NSRect iconRect = NSZeroRect;
iconRect.size = icon.size;
iconRect.origin.x = round(background.size.width*0.5f-iconRect.size.width*0.5f);
iconRect.origin.y = round(background.size.height*0.5f-iconRect.size.height*0.5f);
[background drawInRect:bounds fromRect:NSZeroRect operation:NSCompositeSourceOver fraction:1.0f];
[icon drawInRect:iconRect fromRect:NSZeroRect operation:NSCompositeSourceOver fraction:1.0f];
}];
}
With the help of this method:
+ (NSImage *)imageForSize:(NSSize)size withDrawingBlock:(void(^)())drawingBlock{
if(size.width <= 0 || size.width <= 0){
return nil;
}
NSImage *image = [[NSImage alloc] initWithSize:size];
[image lockFocus];
drawingBlock();
[image unlockFocus];
#if !__has_feature(objc_arc)
return [image autorelease];
#else
return image;
#endif
}
I wonder if you would be better off subclassing NSButton and overwriting drawRect
Maybe try saving and restoring the graphics context.
return [NSImage imageForSize:background.size withDrawingBlock:^{
NSRect bounds = NSZeroRect;
bounds.size = background.size;
NSRect iconRect = NSZeroRect;
iconRect.size = icon.size;
iconRect.origin.x = round(background.size.width*0.5f-iconRect.size.width*0.5f);
iconRect.origin.y = round(background.size.height*0.5f-iconRect.size.height*0.5f);
CGContextRef context = [[NSGraphicsContext currentContext] graphicsPort];
CGContextSaveGState(context);
[background drawInRect:bounds fromRect:NSZeroRect operation:NSCompositeSourceOver fraction:1.0f];
[icon drawInRect:iconRect fromRect:NSZeroRect operation:NSCompositeSourceOver fraction:1.0f];
CGContextRestoreGState(context);
}];

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.