How to save custom color to UserDefaults - uicolor

I am trying to use some non-standard colors (i.e. not UIColor blueColor, but a darker blue color, a darker green, red...etc...) and when I try to save the user selected UIColor object to UserDefaults, it is failing. Below is the code I am using to create the custom colors, followed by the code to save the UIColor objects to the UserDefaults.
Any help would be appreciated.
Thanks!
// create the custom color
UIColor *selectedColor = [UIColor colorWithRed:0.0 green:0.5 blue:0.0 alpha:1.0];
// save the color to user defaults
NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
// archive it into a data object
/*** Fails on this next line ***/
NSData *dataColor = [NSKeyedArchiver archivedDataWithRootObject: selectedColor];
// write the data into the user defaults
[prefs setObject: dataColor forKey:colorKey];
[prefs synchronize];

The problem here is that UIColor is not "key-coding" compliant (I am not sure about the correct term), you should have an error like this:
*** -[NSUserDefaults setObject:forKey:]: Attempt to insert non-property value
It cannot be saved into NSUSerDefaults, you need to saved them using a NSKeyArchiver or convert the color values in to something key-code compliant. For example I have followed the NSStringFromCGRect approach using categories. This is my code:
UIColorAdditions.h
#import <Foundation/Foundation.h>
#interface UIColor (UIColorAdditions)
+ (NSString *)stringFromUIColor:(UIColor *)color;
#end
#interface NSString (UIColorAdditions)
+ (UIColor *)colorFromNSString:(NSString *)string;
#end
UIColorAdditions.m
#import "UIColorAdditions.h"
#implementation UIColor (UIColorAdditions)
+ (NSString *)stringFromUIColor:(UIColor *)color {
return [NSString stringWithFormat:#"%#", color ];
}
#end
#implementation NSString (UIColorAdditions)
+ (UIColor*)colorFromNSString:(NSString *)string {
// The string should be something like "UIDeviceRGBColorSpace 0.5 0 0.25 1
NSArray *values = [string componentsSeparatedByString:#" "];
CGFloat red = [[values objectAtIndex:1] floatValue];
CGFloat green = [[values objectAtIndex:2] floatValue];
CGFloat blue = [[values objectAtIndex:3] floatValue];
CGFloat alpha = [[values objectAtIndex:4] floatValue];
UIColor *color = [UIColor colorWithRed:red green:green blue:blue alpha:alpha];
return color;
}
#end
An example of use:
NSString *strColor = [UIColor stringFromUIColor:[UIColor colorWithRed:0.5 green:0 blue:0.25 alpha:1.0]];
[[NSUserDefaults standardUserDefaults] setObject:strColor forKey:kViewBgColorKey];
There are multiple solutions, (better ones), I just posted mine.

Related

addSublayer over video preview in OSX

I'm trying to add a simple text label over a video preview. The video preview works fine, but the text label is no where to be found. The compiler isn't giving me any errors or warnings either. I know I'm mixing dot notation in there, but I'm not sure if that's my root cause or not.
This is a clip from my .m file:
#interface AVRecorderDocument ()
#property (retain) AVCaptureVideoPreviewLayer *previewLayer;
#property (nonatomic, strong) NSTextField *labelVideoOverlay;
#end
- (void)windowControllerDidLoadNib:(NSWindowController *) aController
{
CALayer *previewViewLayer = [[self previewView] layer];
self.labelVideoOverlay = [[NSTextField alloc] initWithFrame:CGRectMake(100, 120, 200, 40)];
self.labelVideoOverlay.stringValue = #"TEST";
self.labelVideoOverlay.backgroundColor = [NSColor blackColor];
self.labelVideoOverlay.textColor = [NSColor whiteColor ];
[previewViewLayer setBackgroundColor:CGColorGetConstantColor(kCGColorBlack)];
AVCaptureVideoPreviewLayer *newPreviewLayer = [[AVCaptureVideoPreviewLayer alloc] initWithSession:[self session]];
[newPreviewLayer setFrame:[previewViewLayer bounds]];
[newPreviewLayer setAutoresizingMask:kCALayerWidthSizable | kCALayerHeightSizable];
[previewViewLayer addSublayer: self.labelVideoOverlay.layer];
[previewViewLayer addSublayer:newPreviewLayer];
[self setPreviewLayer:newPreviewLayer];
[newPreviewLayer release];
}
It looks like although you've added the video preview as a sublayer, you still need to add the overlay as a subview to your main view. I did something similar like this recently (just with an UIImageView), but the principal should still be the same.
self.overlayImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"overlaygraphic.png"]];
self.overlayImageView.translatesAutoresizingMaskIntoConstraints = NO;
[[self view] addSubview:self.overlayImageView];
So, you could do something like:
self.labelVideoOverlay = [[NSTextField alloc] initWithFrame:CGRectMake(100, 120, 200, 40)];
self.labelVideoOverlay.stringValue = #"TEST";
self.labelVideoOverlay.backgroundColor = [NSColor blackColor];
self.labelVideoOverlay.textColor = [NSColor whiteColor ];
[[self view] addSubview:self.labelVideoOverlay];

NSTextView double vision

I must be doing something wrong here:
My Cocoa app has a scrollview around a custom view which in turn has a textview. I only expect to see one "This is a " string but there the extra one up in the corner.
I have reduced the code to something very minimal and still do not understand what my error is, so here I am fishing for a clue.
The view controller for the custom view follows, but for simplicity here is a link to the project.
#import "TTTSimpleCtrlView.h"
#interface TTTSimpleCtrlView ()
#property (strong,nonatomic) NSTextView *tv1;
#property (strong,nonatomic) NSTextStorage *ts;
#end
#implementation TTTSimpleCtrlView
- (void) awakeFromNib {
NSFont *font = [NSFont fontWithName:#"Courier New Bold" size:20.0f];
NSMutableParagraphStyle *styleModel = [[NSParagraphStyle defaultParagraphStyle] mutableCopy];
[styleModel setLineHeightMultiple:1.0];
// [styleModel setLineSpacing:fontRect.size.height * 2];
NSDictionary *textAttrs = [NSDictionary dictionaryWithObjectsAndKeys: font, NSFontAttributeName,
[NSColor blackColor] ,NSForegroundColorAttributeName,
[NSColor whiteColor], NSBackgroundColorAttributeName,
styleModel, NSParagraphStyleAttributeName,
nil];
NSString *pilcrowStr = #"This is a test.";
NSAttributedString *s = [[NSAttributedString alloc] initWithString:pilcrowStr attributes:textAttrs];
NSRect rect = [s boundingRectWithSize:NSMakeSize(INFINITY,INFINITY)options:0];
NSLayoutManager *lm = [[NSLayoutManager alloc] init];
NSTextContainer *tc = [NSTextContainer new];
[tc setContainerSize:s.size];
[lm addTextContainer:tc];
_ts = [[NSTextStorage alloc] init];
[_ts setAttributedString:s];
[_ts addLayoutManager:lm];
[lm replaceTextStorage:_ts];
rect.origin.x = 10;
rect.origin.y = rect.size.height;
NSTextView *v = [[NSTextView alloc] initWithFrame:rect textContainer:tc];
[v setDrawsBackground:YES];
[self addSubview:v];
}
- (BOOL) isFlipped {
return YES;
}
- (void)drawRect:(NSRect)rect
{
NSLog(#"drawRect & %lu subviews",self.subviews.count);
for (NSTextView *v in self.subviews) {
if(CGRectIntersectsRect(v.frame, rect) || CGRectContainsRect(rect, v.frame)) {
[v drawRect:rect];
NSLog(#"frame = %#",NSStringFromRect(v.frame));
}
}
[super drawRect:rect];
}
You are calling:
[super drawRect:rect];
and you are drawing the text yourself in your draw function.
In effect you are drawing the text and cocoa is drawing the text for you as well.
So don't call super.

NSString *text to NSString *icon?

I am making an app that is a standalone menu item and the basis for the code is sample code I found on a website. The sample code uses a number as the menu icon, but I want to change it to an image.
I want it to be like other apps where it shows icon.png when not clicked and icon-active.png when clicked.
The current code is this:
- (void)drawRect:(NSRect)rect {
// Draw background if appropriate.
if (clicked) {
[[NSColor selectedMenuItemColor] set];
NSRectFill(rect);
}
// Draw some text, just to show how it's done.
NSString *text = #"3"; // whatever you want
NSColor *textColor = [NSColor controlTextColor];
if (clicked) {
textColor = [NSColor selectedMenuItemTextColor];
}
NSFont *msgFont = [NSFont menuBarFontOfSize:15.0];
NSMutableParagraphStyle *paraStyle = [[NSMutableParagraphStyle alloc] init];
[paraStyle setParagraphStyle:[NSParagraphStyle defaultParagraphStyle]];
[paraStyle setAlignment:NSCenterTextAlignment];
[paraStyle setLineBreakMode:NSLineBreakByTruncatingTail];
NSMutableDictionary *msgAttrs = [NSMutableDictionary dictionaryWithObjectsAndKeys:
msgFont, NSFontAttributeName,
textColor, NSForegroundColorAttributeName,
paraStyle, NSParagraphStyleAttributeName,
nil];
[paraStyle release];
NSSize msgSize = [text sizeWithAttributes:msgAttrs];
NSRect msgRect = NSMakeRect(0, 0, msgSize.width, msgSize.height);
msgRect.origin.x = ([self frame].size.width - msgSize.width) / 2.0;
msgRect.origin.y = ([self frame].size.height - msgSize.height) / 2.0;
[text drawInRect:msgRect withAttributes:msgAttrs];
}
Also, I found a post describing a method on how to do this, but it did not work for me. The url to that is this: http://mattgemmell.com/2008/03/04/using-maattachedwindow-with-an-nsstatusitem/comment-page-1#comment-46501.
Thanks!
Use an NSImage and draw it where desired. For example:
NSString *name = clicked? #"icon-active" : #"icon";
NSImage *image = [NSImage imageNamed:name];
NSPoint p = [self bounds].origin;
[image drawAtPoint:p fromRect:NSZeroRect
operation:NSCompositeSourceOver fraction:1.0];
If this is for a status item and you just want an icon with no programmatic drawing, drop the view and set the status item's image and alternateImage. The former is what the status item uses normally; the status item switches to the alternate image (if it has one) when the user opens its menu.

Problems faced while customizing NSTextFieldCell

I am trying to make cells in an outline view just like we have for users in Skype's message window.
For this I created a custom class:
IconNameCell.h
#interface IconNameCell : NSTextFieldCell {
//#private
NSImage *userImage; // size (17,17)
NSImage *statusIcon; // size (14,14)
NSString *cellText;
}
#property (readwrite, retain) NSImage *userImage;
#property (readwrite, retain) NSImage *statusIcon;
#property (readwrite, retain) NSString *cellText;
- (void)drawWithFrame:(NSRect)cellFrame inView:(NSView *)controlView;
#end
IconNameCell.m
#implementation IconNameCell
#synthesize userImage;
#synthesize statusIcon;
#synthesize cellText;
- (void)drawWithFrame:(NSRect)cellFrame inView:(NSView *)controlView{
#try{
// Inset the cell frame to give everything a little horizontal padding
NSRect anInsetRect = NSInsetRect(cellFrame,2.0,0);
//FIXME: flip coordinates and size can be set in accessor methods
// setting userImage and statusIcon in flipped coordinate
[userImage setFlipped:YES];
[statusIcon setFlipped:YES];
// setting size of image and icon
[userImage setSize:NSMakeSize(25.0, 25.0)];
[statusIcon setSize:NSMakeSize(15.0, 17.0)];
// setting attributes of cell text
NSMutableParagraphStyle *dParagraphStyle = [[NSMutableParagraphStyle alloc] init];
[dParagraphStyle setAlignment:NSLeftTextAlignment];
NSColor *colorOfText;
if ([self isHighlighted]) {
colorOfText = [NSColor whiteColor];
}
else {
colorOfText = [NSColor blackColor];
}
NSMutableDictionary * dTitleAttributes = [[NSMutableDictionary alloc] initWithObjectsAndKeys:
colorOfText,NSForegroundColorAttributeName,
[NSFont systemFontOfSize:11.0],NSFontAttributeName,
dParagraphStyle, NSParagraphStyleAttributeName,
nil];
// getting sizes
NSSize cellTextSize = [cellText sizeWithAttributes:dTitleAttributes];
NSSize userImageSize = [userImage size];
NSSize statusIconSize = [statusIcon size];
// making layout boxes for all elements
// vertical padding between the lines of text
float dVerticalPadding = 2.0;
// horizontal padding between two images
float padBtwnImgs = 4.0;
// horizontal padding between image and text
float padBtwnImgText = 6.0;
NSString *userImageName = [userImage name];
NSLog(#"userImageName - %# / cellText- %#",userImageName,cellText); // getting null for userImageName
//if ([userImageName isEqualToString:#"current_D.png"]) {
//FIXME: this is juggad and should be corrected
NSRange rangeOfComma = [cellText rangeOfString:#","];
if (rangeOfComma.length !=0 ) {
//<#statements#>
// userImage box: center the userImage vertically inside of the inset rect
NSRect cellTitleBox = NSMakeRect(anInsetRect.origin.x,
anInsetRect.origin.y + anInsetRect.size.height*.5 - cellTextSize.height*.5,
cellTextSize.width,
cellTextSize.height);
// drawing cell text
[cellText drawInRect:cellTitleBox withAttributes:dTitleAttributes];
}
else {
// userImage box: center the userImage vertically inside of the inset rect
NSRect userImageBox = NSMakeRect(anInsetRect.origin.x,
anInsetRect.origin.y + anInsetRect.size.height*.5 - userImageSize.height*.5,
userImageSize.width,
userImageSize.height);
// statusIcon box: center the statusIcon vertically inside of the inset rect
NSRect statusIconBox = NSMakeRect(userImageBox.origin.x + userImageBox.size.width + padBtwnImgs,
anInsetRect.origin.y + anInsetRect.size.height*.5 - statusIconSize.height*.5,
statusIconSize.width,
statusIconSize.height);
// cellTitleBox: vertically aligning text
NSRect cellTitleBox = NSMakeRect(statusIconBox.origin.x + statusIconBox.size.width + padBtwnImgText,
anInsetRect.origin.y + anInsetRect.size.height*.5 - cellTextSize.height*.5,
cellTextSize.width,
cellTextSize.height);
// drawing user image
[userImage drawInRect:userImageBox fromRect:NSZeroRect operation:NSCompositeSourceOver fraction:1.0];
// drawing user status
[statusIcon drawInRect:statusIconBox fromRect:NSZeroRect operation:NSCompositeSourceOver fraction:1.0];
// drawing cell text
[cellText drawInRect:cellTitleBox withAttributes:dTitleAttributes];
}
}
#catch (NSException *e) {
NSLog(#"IconNameCell -%#",e);
}
}
#end
2nd, I assigned text field cell for outline view the class: IconNameCell in IB
3rd, I used this code in delegate to set image, icon and name of user in custom cell-
- (void)outlineView:(NSOutlineView *)outlineView willDisplayCell:(id)cell forTableColumn:(NSTableColumn *)tableColumn item:(id)item{
if ([[tableColumn identifier] isEqualToString:#"userInfo"]) {
// some relevant code
if( [[item onlineStatus] intValue] == 0 ){
[cell setStatusIcon:[NSImage imageNamed:#"offline.png"]];
}
else{
[cell setStatusIcon:[NSImage imageNamed:#"online.png"]];
}
//similarly, setting attribute for userImage and cellText
}
I am facing two problems with it: 1. Application is frequently crashing when I am selecting one or the other row in outline view. Earlier when I had not taken the customized cell but three different columns for - user image, user status and user name it was working fine! 2. For the log: NSLog(#"userImageName - %# / cellText- %#",userImageName,cellText); I am getting (null) as userImageName, although I should get some string value for it.
Can anyone suggest me some solution for it??
Thanks,
Miraaj
Application is frequently crashing when I am selecting one or the other row in outline view.
Please edit your question to include the crash log.
2. For the log: NSLog(#"userImageName - %# / cellText- %#",userImageName,cellText); I am getting (null) as userImageName, although I should get some string value for it.
Have you set the image's name with setName: or obtained it from imageNamed:? Otherwise, it doesn't have one, and this output is correct.

Archiving and Unarchiving results in Bad Access

I'm having trouble setting up a model object to save the visual state of user generated CALayers in a simple graphics application for the iphone.
I'm attempting to save the background color and frame of all the current layers on screen by passing those properties to model objects which implement the NSCoding protocol and then into an NSMutableArray which the app delegate owns. Then I archive the array with NSKeyedArchiver and store it in the NSUserDefaults.
Each CALayer's backgroundColor property is converted to a UIColor to be encoded by the model object for storage. I think that I'm unarchiving the array incorrectly or not restoring state from the unarchived array correctly. When I attempt to access the UIColor object that was store in the model object, I get an EXC_BAD_ACCESS.
I thought it was possibly a bug with encoding UIColor objects so tried pulling the values out of the CGColorRef with the CGColorGetComponents function and storing them in an array to encode and archive, but I had the same result of bad access after unarchiving, so I think I'm just doing it wrong.
This is my model object:
#interface AILayerData : NSObject <NSCoding> {
UIColor* color;
CGRect frame;
}
#property (retain) UIColor* color;
#property (assign) CGRect frame;
#end
#implementation AILayerData
#synthesize color;
#synthesize frame;
- (void)encodeWithCoder:(NSCoder *)coder;
{
[coder encodeObject:color forKey:#"color"];
[coder encodeCGRect:frame forKey:#"frame"];
}
- (id)initWithCoder:(NSCoder *)coder;
{
self = [[AILayerData alloc] init];
if (self != nil)
{
color = [coder decodeObjectForKey:#"color"];
frame = [coder decodeCGRectForKey:#"frame"];
}
return self;
}
#end
And this is my archiving implementation:
#implementation AppDelegate
- (void)applicationWillTerminate:(UIApplication *)application {
NSArray *layersArray = viewController.view.layer.sublayers;
dataArray = [NSMutableArray array];
for(AILayer *layer in layersArray)
{
AILayerData *layerData = [[AILayerData alloc] init];
layerData.frame = layer.frame;
UIColor *layerColor = [UIColor colorWithCGColor:layer.backgroundColor];
layerData.color = layerColor;
[dataArray addObject:layerData];
[layerData release];
}
[[NSUserDefaults standardUserDefaults] setObject:[NSKeyedArchiver archivedDataWithRootObject:layerDataArray] forKey:#"savedArray"];
}
#end
And here is where I restore state:
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
spaceView = [[AISpaceView alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
self.view = spaceView;
[spaceView release];
spaceView.delegate = self;
NSUserDefaults *currentDefaults = [NSUserDefaults standardUserDefaults];
NSData *dataRepresentingSavedArray = [currentDefaults objectForKey:#"savedArray"];
if (dataRepresentingSavedArray != nil) {
[self restoreStateWithData:dataRepresentingSavedArray];
}
}
- (void)restoreStateWithData:(NSData *)data
{
NSArray *savedLayers = [NSKeyedUnarchiver unarchiveObjectWithData:data];
if (savedLayers != nil) {
NSArray *restoredLayers = [[NSArray alloc] initWithArray:savedLayers];
for(AILayerData *layerDataObject in restoredLayers) {
UIColor *layerColor = layerDataObject.color;
AILayer *newLayer = [[AILayer alloc] init];
newLayer.backgroundColor = layerColor.CGColor;
newLayer.frame = layerDataObject.frame;
newLayer.isSelected = NO;
[self.view.layer addSublayer:newLayer];
[newLayer release];
}
[restoredLayers release];
[spaceView.layer layoutSublayers];
}
}
#end
Any help with this is greatly appreciated. I'm pretty much a noob. I was encoding, archiving and unarching an NSArray of NSNumbers converted from the color's floats in pretty much the same way and getting bad access.
You certainly want to retain the color in initWithCoder:
color = [[coder decodeObjectForKey:#"color"] retain];
or, with the dot syntax as color was declared as a retain property:
self.color = [coder decodeObjectForKey:#"color"];
You are over-releasing layerColor: You don't own it (layerDataObject does), but you are releasing it.
It looks like NSCoder for iPhone doesn't respond to -encodeWithCGRect:
Source: http://17.254.2.129/iphone/library/documentation/Cocoa/Reference/Foundation/Classes/NSCoder_Class/Reference/NSCoder.html

Resources