This question already has an answer here:
Closed 10 years ago.
Possible Duplicate:
how to convert nsstring to uicolor?
not an objective-c programmer but am not sure if I can't do something like this. I know I'm returning a string value but just want a terse way of getting at these values:
NSMutableArray *colorsArray=[[NSMutableArray alloc] init];
[colorsArray addObject:#"whiteColor"];
[colorsArray addObject:#"blueColor"];
// val5 is either 0 or 1
[view setBackgroundColor:[UIColor [colorsArray objectAtIndex:val5]]];
Is this possible?
thx in advance
Why don't you store directly colors instead of NSString?
NSMutableArray *colorsArray=[[NSMutableArray alloc] init];
[colorsArray addObject:[UIColor whiteColor]];
[colorsArray addObject:[UIColor blueColor]];
if (val5 < colorsArray.count) // Doesn't hurt any to be safe, and this way you won't get a crash if something messes up your val5 at some point
{
// val5 is either 0 or 1
[view setBackgroundColor:[colorsArray objectAtIndex:val5]];
}
else
{
NSLog(#"Error - Color not expected - leaving color the way it is.")
}
You code as it is doesn't compile, syntax is invalid since you are trying to do [UIColor #"whiteColor"] which is not a valid syntax. You could use performSelector: but this is senseless unless you really need to store colors as strings.
Or if you must use NSString instances in the array:
NSMutableArray *colorsArray=[[NSMutableArray alloc] init];
[colorsArray addObject:#"whiteColor"];
[colorsArray addObject:#"blueColor"];
SEL colorSelector = NSSelectorFromString([colorsArray objectAtIndex:val5]);
[view setBackgroundColor:[UIColor performSelector:colorSelector]];
NSMutableArray *colorsArray = [[NSMutableArray alloc] init];
[colorsArray addObject:#"whiteColor"];
[colorsArray addObject:#"blueColor"];
UIColor *color = [UIColor performSelector:NSSelectorFromString([colorsArray objectAtIndex:1])];
view.backgroundColor = color;
Related
I am not sure if I am writing this code correctly.
Fruit * o1 = [[Fruit alloc] initWithName:kFruitOrange imageView:orange1] ;
fruitArray = [[NSMutableArray arrayWithObjects:o1, nil] retain]; // retain array for later use.
There are actually several fruits. Since I alloc them, then assign them to an array in my class init method. When and how should I release these objects.
CLARIFICATION.
If I release
[o1 release] after saving in array, I get "exc_bad_access" errors when looking at the array in other methods.
In my viewDidUnload method, I have gone through the array and released each object manually.
for (Fruit * f in fruitArray) {
[f release];
}
I have never seen anyone else do this in code before, so I am thinking that this is not the correct way to do it?
UPDATE
-(Fruit *) initWithName:(enum fruitTypes)fruitName imageView:(UIImageView *)iv{
if((self = [super init])){
name = #"Fuit Object";
NSLog(#"creating orange colour %i", fruitName);
switch (fruitName) {
case kFruitOrange:
NSLog(#"creating orange colour ");
colour = [UIColor orangeColor] ;
break;
case kFruitBanana:
colour = [UIColor yellowColor];
break;
case kFruitKiwi:
colour = [UIColor greenColor];
break;
case kFruitBlue:
colour = [UIColor blueColor];
break;
default:
NSLog(#"COLOUR NOT FOUND");
break;
}
value = fruitName;
imageView = iv;
center = iv.center;
[colour retain];
[imageView retain];
[name retain];
return self;
}
return nil;
}
** ANSWER WAS I NEEDED TO RETAIN THE VARIABLES I SET IN MY FRUIT.M CLASS.
Release objects right after you add them to array - standard objective-c containers retain their elements and then release them when container itself is deallocated, so you don't need to worry about memory management yourself.
So when you create and fill your array release your elements:
Fruit * o1 = [[Fruit alloc] initWithName:kFruitOrange imageView:orange1] ;
fruitArray = [[NSMutableArray arrayWithObjects:o1, nil] retain];
[o1 release];
And in your viewDidUnload method (and in dealloc) just release your array, not its elements:
[fruitArray release];
The reason you get errors if you put [o1 release]; in your code is that in that case you release your object twice and it have been retain just once - when allocated.
I've been working a product display app but it has a memory leak that causes it to crash after too many categories have been loaded up. The app works via a SplitViewController that lists the categories down the left and, once tapped, the product images show in the detailViewController on the right.
Selecting category after category eventually crashes the app.
I've used the instruments -> Leaks tool to trace the problem and I get told that NSString appendString is a leak. The number of strings leaked seems to match the number of products in the category selected so I'm guessing one of my loops holds the problem but, after playing around with AutoreleasePools I haven't solved it yet.
My code:
This method is called when the category is selected and parses an XML document
- (NSMutableArray*) processXML{
//NSAutoreleasePool *pool4 = [[NSAutoreleasePool alloc] init];
// Initialize the productEntries MutableArray declared in the header
products = [[NSMutableArray alloc] init];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSMutableString *documentsDirectory = [[NSMutableString stringWithFormat:#"%#", [paths objectAtIndex: 0]] autorelease];
// paths to save inputs to
NSString *productsFile = [documentsDirectory stringByAppendingFormat: #"/products2.xml"];
NSData *data = [NSData dataWithContentsOfFile: productsFile];
// Create a new rssParser object based on the TouchXML "CXMLDocument" class, this is the object that actually grabs and processes the RSS data
NSError *error = nil;
CXMLDocument *rssParser = [[[CXMLDocument alloc] initWithData:(NSData *)data encoding:NSUTF8StringEncoding options:0 error:&error] autorelease];
// Create a new Array object to be used with the looping of the results from the rssParser
NSArray *resultNodes = NULL;
//NSString *xPathStart, *xPathEnd, *category, *finalStr;
NSString *xPathStart = [[NSString stringWithFormat:#""] autorelease];
NSString *xPathEnd = [[NSString stringWithFormat:#""] autorelease];
NSString *category = [[NSString stringWithFormat:#""] autorelease];
NSString *finalStr = [[NSString stringWithFormat:#""] autorelease];
NSString *detailStr = [[NSString stringWithFormat: detailItem] autorelease];
// category to be parsed - build up xPath expression
if([detailStr isEqualToString: #"On Order Stock"]) {
xPathStart = #"/products/product[instock='2";
xPathEnd = #"']";
finalStr = [NSString stringWithFormat:#"%#%#", xPathStart, xPathEnd];
} else {
xPathStart = #"/products/product[category='";
category = detailItem;
xPathEnd = #"']";
finalStr = [NSString stringWithFormat:#"%#%#%#", xPathStart, category, xPathEnd];
}
resultNodes = [rssParser nodesForXPath: finalStr error:nil];
// Loop through the resultNodes to access each items actual data
for (CXMLElement *resultElement in resultNodes) {
Product *productItem = [[Product alloc] init];
[productItem setCode: [[[resultElement childAtIndex: 1] stringValue] autorelease]];
[productItem setImage: [[[resultElement childAtIndex: 5] stringValue] autorelease]];
// Add the product object to the global productEntries Array so that the view can access it.
[products addObject: productItem];
[productItem release];
}
//[pool4 release];
return products;
}
As you can see I went a little crazy with autoReealse on my strings. The other code segment that displays the images could be the problem although Leaks does mention processXML directly.
- (void) displayImages:(NSMutableArray *)anArray {
// create scrollView object
scrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height - 100)];
scrollView.pagingEnabled = NO;
scrollView.scrollEnabled = YES;
scrollView.backgroundColor = [UIColor colorWithRed:0.9 green:0.9 blue:0.9 alpha:1];
scrollView.userInteractionEnabled = YES;
//create info area below scrollView
infoView = [[UIScrollView alloc] initWithFrame:CGRectMake(0, self.view.frame.size.height - 100, self.view.frame.size.width, 100)];
[infoView setContentSize:CGSizeMake(self.view.frame.size.width, 100)];
infoView.backgroundColor = [UIColor colorWithRed:0.1 green:0.1 blue:0.1 alpha:1];
infoView.scrollEnabled = NO;
[barcodeImgView setImage:[UIImage imageNamed:#"barcode2.jpg"]];
[infoView addSubview:codeLbl];
[infoView addSubview:nameLbl];
[infoView addSubview:priceLbl];
[infoView addSubview:dimensionsLbl];
[infoView addSubview:stockLbl];
[infoView addSubview:commentsLbl];
[infoView addSubview:barcodeImgView];
infoView.userInteractionEnabled = YES;
[codeLbl setText:[[NSString stringWithFormat:#""] autorelease]];
[nameLbl setText:[[NSString stringWithFormat:#""] autorelease]];
[priceLbl setText:[[NSString stringWithFormat:#""] autorelease]];
[commentsLbl setText:[[NSString stringWithFormat:#""] autorelease]];
[stockLbl setText:[[NSString stringWithFormat:#""] autorelease]];
[dimensionsLbl setText:[[NSString stringWithFormat:#""] autorelease]];
// hold x and y of each image
int x = 30;
int y = 50;
int noOfImages = [anArray count];
int maxRowWidth = (noOfImages / 3) + 1;
int xcount = 0; // position across the row, reset to zero and drop image down when equal to (noOfImages / 3) + 1
//NSAutoreleasePool *displayPool = [[NSAutoreleasePool alloc] init];
for(int i = 0; i < noOfImages; i++) {
// declare Product object to hold items in anArray
Product *prod = [[Product alloc] init];
prod = [anArray objectAtIndex: i];
// try for image in Documents folder, later checks it exists and if not uses Resource location
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSMutableString *documentsDirectory = [[NSString stringWithFormat:#"%#", [paths objectAtIndex: 0]] autorelease];;
// paths to save inputs to
NSString *imgName = [[NSString stringWithFormat:#"%#/%#", documentsDirectory, [prod image]] autorelease];
NSString *productName = [[NSString stringWithFormat:#"%#", [prod code]] autorelease];
// create and size image
UIImage *image = [UIImage imageWithContentsOfFile: imgName];
// set up button
UIButton *button= [UIButton buttonWithType:UIButtonTypeRoundedRect];
[button addTarget:self action:#selector(imageButtonClick:) forControlEvents:(UIControlEvents)UIControlEventTouchDown];
[button setTitle:productName forState:UIControlStateNormal];
button.titleLabel.font = [UIFont systemFontOfSize: 0];
[button setTitleColor: [UIColor colorWithRed:0.1 green:0.1 blue:0.1 alpha:1] forState: UIControlStateNormal];
CGSize imageSize = image.size;
CGFloat height = imageSize.height;
CGFloat width = imageSize.width;
CGFloat ratio = 160 / width; // get ratio to divide height by
UIGraphicsBeginImageContext(CGSizeMake((height * ratio),160));
CGContextRef context = UIGraphicsGetCurrentContext();
[image drawInRect: CGRectMake(0, 0, height * ratio, 160)];
UIImage *smallImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
// create frame for image
CGRect newFrame = CGRectMake(x, y, 160,160);
UILabel *codeLabel = [[UILabel alloc] initWithFrame:CGRectMake(x, y - 20, 170, 20)];
codeLabel.text = productName;
codeLabel.textColor = [UIColor colorWithRed:0.1 green:0.1 blue:0.1 alpha:1];
codeLabel.backgroundColor = [UIColor colorWithRed:0.9 green:0.9 blue:0.9 alpha:1];
[button setFrame: newFrame];
[button setBackgroundImage:smallImage forState:UIControlStateNormal];
[scrollView setContentSize:CGSizeMake((maxRowWidth * 160) + 160,self.view.frame.size.height - 100)];
[self.scrollView addSubview:button];
[self.scrollView addSubview:codeLabel];
xcount++;
x = x + 170; // move across the page
if(xcount == maxRowWidth) {
y = y + 210; // move down the screen for the next row
x = 30; // reset x to left of screen
xcount = 0; // reset xcount;
}
[prod release];
}
//[displayPool release];
[self.view addSubview: scrollView];
[self.view addSubview: infoView];
[scrollView release];
[infoView release];
[pool release];
}
By the way, pool is an autoreleasePool defined in the h file for the class.
I would really appreciate any specific help regarding my code or general tips on what could be wrong.
I see a few things wrong:
As was mentioned in the comments, you're abusing -autorelease in ways which make grown men cry and that which will crash your app.
-processXML is returning an owned object. You're allocating products and returning it. This breaks convention, because the method name does not begin with new or alloc, and does not contain copy. You should return [products autorelease]; instead. However, even that is shady, because since products isn't declared locally, it's probably an instance variable. In that case, what happens if processXML gets invoked multiple times? You have an owned object referenced by the instance variable, and suddenly you overwrite that reference with a new one... = memory leak.
Every time someone does MyClass * object = [[MyClass alloc] init]; object = [something thatReturnsAMyClass];, a kitten dies. If you then do [object release];, a second dies for good measure. This is a terrible, terrible memory leak (and a likely crash). You're allocating a new object and then immediately throwing it away but never releasing it. That you do this suggests you don't really get what a pointer is. I suggest reading "Everything you need to know about pointers in C"
On a lighter note, you should check out -[NSString stringByAppendingPathComponent:]. NSString has a bunch of really nice methods for dealing with paths.
I hope I don't come off as too harsh. :)
Some while ago at another post somebody said that one should read about the memory management and I was actually thinking that this answer is not really right. What is wrong with some trial and error and learning by doing. But after I had my painful experiences with memory I must admit that this guy was right. Take the time. Go and read the chapter on memory management in the apple documentation.
As above stated already you should not autorelease an object you do not own. But this might not cause the trouble. You can next to the instruments use Build+Analyze in the Build menu. This will help you to find out more.
Basically you need to release objects you create which you own (which one you own is in the documentation, basically those created with "alloc" and some more). If you cannot release them you assign them to the autorelease pool. This is the case with the "products" you return from processXML. When is the autorelease pool drained? This is when the next time the framework of the application is back in control (I think it was called run-loop or something). This can be a while and so you should not open to much objects which are assigned to an autorelease pool.
So to help you really read that chapter: memory management programming guide
i want to set the name of an object like UIButton from a string.
NSString *buttonName = [[NSString alloc] initWithString:#"someString"];
My goal is:
UIButton *someString = [[UIButton buttonWithType:UIButtonTypeCustom]retain];
how can i solve this?
You can't - variable names are resolved by the compiler well before any Objective-C code is executed. What you can do is maintain a map of strings to objects like buttons etc. using NSMutableDictionary:
NSString *string = #"someString";
[buttonMap setObject: [UIButton buttonWithType:UIButtonTypeCustom] forKey: string];
//...time passes...
[[buttonMap objectForKey: #"someString"] setEnabled: YES];
I am trying to get the width of an NSString (ex. NSString *myString = #"hello"). Is there a way to do this?
Thanks.
Here's a relatively simple approach. Just create an NSAttributedString with the appropriate font and ask for its size:
- (CGFloat)widthOfString:(NSString *)string withFont:(NSFont *)font {
NSDictionary *attributes = [NSDictionary dictionaryWithObjectsAndKeys:font, NSFontAttributeName, nil];
return [[[NSAttributedString alloc] initWithString:string attributes:attributes] size].width;
}
UIFont * font = [UIFont systemFontOfSize:15];
CGSize stringSize = [aString sizeWithFont:font];
CGFloat width = stringSize.width;
Using the UILabel's attributed string:
- (CGSize)getStringSizeWithText:(NSString *)string font:(UIFont *)font{
UILabel *label = [[UILabel alloc] init];
label.text = string;
label.font = font;
return label.attributedText.size;
}
Send the string a sizeWithAttributes: message, passing a dictionary containing the attributes with which you want to measure the string.
i dont know if you are suppose to use this in cocoa touch. if it is, then:
- (CGFloat)widthOfString:(NSString *)string withFont:(NSFont *)font {
NSDictionary *attributes = [NSDictionary dictionaryWithObjectsAndKeys:font, NSFontAttributeName, nil];
return [[[NSAttributedString alloc] initWithString:string attributes:attributes] size].width;
}
wont work.
in cocoa touch, you gotta add coretext framework and import the header
and write your code like this:
UIFont *font = [UIFont fontWithName:#"HelveticaNeue-BoldItalic" size:DEFAULT_FONT_SIZE];
// NSLog(#"%#", NSFontAttributeName);
NSDictionary *attributes = [NSDictionary dictionaryWithObjectsAndKeys:font, (NSString *)kCTFontAttributeName, nil];
but, GEE!!!!!
NSMutableAttributedString *as = [[NSMutableAttributedString alloc] initWithString:self.caption attributes:attributes];
[as size].width;
there's no size this method in NSMutableAttributedString!
finally, this would work
[self.caption sizeWithFont:font].width
as for ios 7 and up this is the correct way:
NSString * buttonTitle = #"demo title";
CGSize stringSize = [buttonTitle sizeWithAttributes:#{NSFontAttributeName: [UIFont systemFontOfSize:17.0f]}];
This works with iOS 14.5
Objective-C
Define attributes:
NSDictionary *attributes = #{
NSFontAttributeName: [UIFont fontWithName:#"Helvetica" size:25],
NSStrokeWidthAttributeName: #(0),
NSStrokeColorAttributeName: [UIColor blackColor]
};
Get width and height:
- (CGFloat)widthOfString:(NSString *)string {
CGSize stringSize = [string sizeWithAttributes:attributes];
return stringSize.width;
}
- (CGFloat)heightOfString:(NSString *)string {
CGSize stringSize = [string sizeWithAttributes:attributes];
return stringSize.height;
}
Sorry my question was not detailed enough and is not exactly what I'm trying to do. I am using a text storage, layout manager and a text container. The solution is to use the layout manager to determine the rectangle that bounds the rect. Here is the code.
NSTextStorage *textStorage = [[NSTextStorage alloc] initWithString:#"hello"];
NSLayoutManager *layoutManager = [[NSLayoutManager alloc] init];
NSTextContainer *textContainer = [[NSTextContainer alloc] init];
[layoutManager addTextContainer:textContainer];
[textContainer release];
[textStorage addLayoutManager:layoutManager];
[layoutManager release];
//Figure out the bounding rectangle
NSRect stringRect = [layoutManager boundingRectForGlyphRange:NSMakeRange(0, [layoutManager numberOfGlyphs]) inTextContainer:textContainer];
UIKit has a nice addition to NSString, making sizeWithAttributes: a bit lighter:
CGSize titleSize = [title sizeWithFont:titleFont
constrainedToSize:contentCellSize
lineBreakMode:UILineBreakModeWordWrap];
Here's Stephen's solution in Clozure Common Lisp, when using the Objective C bridge. I came across this post when searching for a solution, and I just rewrote Stephen's version which worked fine for me. Others using Clozure might find this helpful:
(defun string-width (str font)
(let* ((dict (#/dictionaryWithObjectsAndKeys: ns:ns-mutable-dictionary
font #$NSFontAttributeName
ccl:+null-ptr+))
(attr (#/initWithString:attributes: (#/alloc ns:ns-attributed-string)
(ccl::%make-nsstring str)
dict))
(size (#/size attr)))
(ns:ns-size-width size)))
This will work. You can try it.
NSDictionary *attrDict = #{NSFontAttributeName : [GenericUtility getOpenSansRegularFontSize:12]};
CGSize stringBoundingBox = [selectedReservationModel.DateLabel sizeWithAttributes: attrDict];
lblDeliveryDateWidth = stringBoundingBox.width;
Just in case you are wondering how to check a label size, you should use the UIFont, instead of the NSFont (not even sure if exists)
I'm actually starting to loose the will to live, this piece of code is driving me nuts!
I'm trying to get the content of mathspractice.txt into *myLabel
I'm using an array which is:
-(void)loadText
{
NSArray *wordListArray = [[NSArray alloc] initWithArray:
[[NSString stringWithContentsOfFile:[[NSBundle mainBundle] pathForResource:#”mathspractice” ofType:#”txt”]
encoding:NSMacOSRomanStringEncoding error:NULL] componentsSeparatedByString:#”\n”]];
self.theMathsPractice = wordListArray;
[wordListArray release];
}
and then I'm trying to pass it into *myLabel
UILabel *myLabel = [[UILabel alloc] initWithFrame:CGRectMake(0,100,960,40)];
myLabel.text = *theMathsPractice;
[myScrollView addSubview:myLabel];
[myLabel release];
}
Can anyone help?
It looks on quick inspection that your theMathsPractice is an NSArray, not an NSString, which is what you'd want to assign to the label's text property. You should at least format that array back into a string of some sort before assigning it to the label.
(Also not sure why you're dereferencing it with the * in the assignment-- I would think that would throw a compiler error, since naked non-reference Objective-C objects are not really allowed.)
I would use the following:
myLable.text = [theMathsPractice componentsJoinedByString:#" "]);