I've got a problem when I define a UILabel add it to a UIView
UIView *dwView=[[UIView alloc] initWithFrame:CGRectMake(20, 50, 975.0, 620)];
UILabel *label1 = [[UILabel alloc]initWithFrame:CGRectMake(10.0, 160.0, 950.0,
170.0)];
# i add many UILabel in dwView
...
UILabel *label1
UILabel *label2
UILabel *label3
...
dwView.addView(lable1);
dwView.addView(lable2);
dwView.addView(lable3);
dwView.addView(...);
[lable1 release];
[lable2 release];
[lable3 release];
[... release];
No matter where I define the label, I release it with the method:
[lable1 release];
lable1 = nil;
I log the retainCount, its all 0, but I checked the memory with the profile->allocations it's still not reducing.
I want to know why it is like this, and how I can reduce the memory.
edit 1: I built my project with ARC
edit 2:
now I define variable in .h
{
UIView *dwView;
UILabel *label1,lable2;
}
Init in .m
{
dwView=[[[UIView alloc] initWithFrame:CGRectMake(20, 50, 975.0, 620)] autorelease];
label1 = [[[UILabel alloc]initWithFrame:CGRectMake(10.0, 160.0, 950.0, 170.0)] autorelease];
label1.text = wordString;
dwView.addView(lable1);
}
-(void)dealloc {
[super dealloc];
label1 = nil;
dwView = nil;
}
I try the code above, it doesn’t work.
So: how can i release the variable dwView and lable1
edit
The short answer: Use ARC.
If you're determined to use manual reference counting, a few things:
Looking at retainCount is not useful. It will just confuse you. (You can't see pending autoRelease calls)
In your code, you are creating label1 as a local variable. Then you show ..., meaning you have code somewhere else. If you define a new local variable label1 (and you switch to "lable1" (different spelling) it will be nil. If you want to be able to get to label1 to add it to a superview and/or release it, you either need to do it in the same method/scope, or make it an instance variable.
Another option would be to autoRelease your label after creating it, then add it to the view you want to add it to. That will only work if you add it to your view before the current autoRelease pool is drained.
Your issue is using Allocations. It doesn't accurately tell you how much live memory you're using. For that you should be using Activity Monitor.
It's better to use ARC. Otherwise use autoRelease at the end of your declaration. For example,
UILabel *label1 = [[[UILabel alloc]initWithFrame:CGRectMake(10.0, 160.0,
950.0,170.0)]autorelease];
otherwise release those with
dealloc() label = nil;
Related
I have an UILabel pinter in my UIView (using ARC).
I dynamically create a lot of text and override the the same pointer every time.
I thought, that if I use the same pointer all the time, and override it with the new objects, they still be in my View, but the pointer of them will be deallocated. But as I see, my memory increase all the time, if the text was changed and the drawRect executed. Maybe someone know the better way to do that, or to fix this memory issue.
UPDATE: Code
#interface Bars : UIView{
NSMutableDictionary *dictCopy;
UILabel *pivotLabel;
}
for (a lot of times) {
pivotLabel = [[UILabel alloc] initWithFrame:frame];
pivotLabel.text = pivotText;
pivotLabel.backgroundColor = [UIColor clearColor];
pivotLabel.textColor = self.color;
[self addSubview:pivotLabel];
}
When you add a new label as a subview the parent view retains it. Nilling a pointer is not enough to remove it. To remove a label, do this:
[self.myLabel removeFromSuperview];
self.myLabel = nil;
For reasons of reusability, I'm interested in finding a way to programmatically add text and custom UITextFields to a view in an efficient manner, similar to how you can combine strings using NSString's stringWithFormat: and then assign the result to a UILabel's text attribute. Ideally, with 2-3 statements I could write text and include my UITextField Objects in a string, and get an automatically text-wrapped, nicely formatted UIView that I can embed directly into my view. Basically, it would function like a UILabel with the ability to add UIView objects. For an example of the output this image would be a combination of both text and underlined UITextFields:
If this exists, it would allow me to reuse a single UITableViewCell subclass rather than having 5-6 xibs and 3-4 subclasses. I've searched about 2 hours with no real luck for a pre-existing solution, so has anyone ever encountered this problem before and used or released a library to handle this, or is there a simple solution I'm overlooking?
Thank you!
you can use CSLinearLayoutView (https://github.com/scalessec/CSLinearLayoutView)
and create a class
#implementation LabledView
+ (UIView*)create :(CGRect) frame view:(UIView*) view labelTitle:(NSString*)labelTitle viewLinearLayoutMakePadding :(CSLinearLayoutItemPadding)viewLinearLayoutMakePadding labelLinearLayoutMakePadding :(CSLinearLayoutItemPadding)labelLinearLayoutMakePadding font:(UIFont*)font textColor:(UIColor*)textColor
{
CSLinearLayoutView *container = [[CSLinearLayoutView alloc] initWithFrame:frame];
container.orientation = CSLinearLayoutViewOrientationHorizontal;
UILabel *label = [[UILabel alloc] init];
label.textColor = textColor;
[label setText:labelTitle];
[label setFont:font];
[label sizeToFit];
CSLinearLayoutItem *itemLabel = [CSLinearLayoutItem layoutItemForView:label];
itemLabel.padding = labelLinearLayoutMakePadding;
CSLinearLayoutItem *itemView = [CSLinearLayoutItem layoutItemForView:view];
itemView.padding = viewLinearLayoutMakePadding;
[container addItem:itemLabel];
[container addItem:itemView];
return container;
}
example :
UITextField *textField = [[UITextField alloc] initWithFrame:CGRectMake(0, 0, 260, 40)];
UIView *customView = [LabledView create:CGRectMake(0, 0, 280, 40) view:textField
labelTitle:#"your label" viewLinearLayoutMakePadding:CSLinearLayoutMakePadding(0, 10, 0, 0)
labelLinearLayoutMakePadding:CSLinearLayoutMakePadding(10, 0, 0, 0)
font:[UIFont fontWithName:#"Helvetica" size:12] textColor:[UIColor blackColor]];
You can underline specific ranges of a string with NSAtttibutedString. You can setAttributedString to UILabel in ios6... So that's the way I'd do it, then it can indeed be in a single label with the desired parts underlined (or in a different font/colour/etc) only. Be careful when you look into attributed string, it's attributes dictionary uses different keys for working with UIKit (these are the ones you need here) to what it uses with CoreText
I am trying to programatically generate two UILabels in my application for each UIImageView on my storyboard. The code runs and works correctly, however, on first load the two UILabels form in the (0.0) coordinate of the main view, as opposed to the UIImageView frame origin.x,origin.y. I can't understand why this is happening.
If I then click on a different tab and return to the page, the labels generate in the correct location.
Why is this? How can I get it to initially generate the labels in the correct location?
-(void) viewWillAppear:(BOOL)animated
{
//removed unneccessary code above...
int i = 0;
for (UIImageView *plantScreen in self.view.subviews)
{
if ([plantScreen isMemberOfClass:[Plant class]])
{
#try
{
//the label which will hold the name
UILabel *plantName = [[UILabel alloc] initWithFrame:CGRectMake((plantScreen.frame.origin.x), (plantScreen.frame.origin.y+ plantScreen.frame.size.height), 160, 30.0)];
plantName.numberOfLines = 1;
plantName.minimumScaleFactor = .5;
plantName.adjustsFontSizeToFitWidth = YES;
[plantName setTextAlignment:NSTextAlignmentCenter];
[plantName setBackgroundColor:[UIColor clearColor]];
[self.view addSubview:plantName];
plantName.hidden = false;
[self.view bringSubviewToFront:plantName];
//create the label which will hold the quantity
UILabel *quantity = [[UILabel alloc] initWithFrame:CGRectMake((plantScreen.frame.origin.x), (plantScreen.frame.origin.y+ plantScreen.frame.size.height + 20), 160, 30.0)];
[quantity setTextAlignment:NSTextAlignmentCenter];
[quantity setBackgroundColor:[UIColor clearColor]];
quantity.text = [NSString stringWithFormat:#"%d",plant.quantity];
[self.view addSubview:quantity];
quantity.hidden = false;
[self.view bringSubviewToFront:quantity];
i++;
}
#catch (NSException *exception)
{
NSLog(#"An exception occured: %#", [exception reason]);
}
#finally
{
}
}
}
}
Frame of the UIImageView depends on the image being drawn and its contentMode property. You can try setting the contentMode to UIViewContentModeScaleAspectFill to see if it forces to keep its assigned frame.
First things first, you're missing a call to [super viewWillAppear:animated]. You need to give the superclass (including the UIViewController base class) a chance to "do its magic".
Never forget about giving the parent class a chance to do its magic, unless you know really really well what you're doing.
Second, UI creation should be done in -loadView, not in viewWillAppear:.
Try these two things first.
Alright. Now I'm curious about how you moved things to -loadView. Did you add [super loadView];?
In fact, now that I think about it, moving to -loadView is wrong in this case; you obviously instantiate some views through a nib. UIViewController's implementation of -loadView typically just loads the nib file. Once that's done, UIViewController's -loadView calls -viewDidLoad.
So when you're not creating all UI programmatically but are instead allowing UIViewController to load it from nib, you actually probably want to move code into -viewDidLoad. (See template generated by Xcode when you tell it to create a new UIViewController subclass.)
Moving on, let's consider what the frame depends on. It depends on some view class you called Plant.
Please don't call it that way; it's confusing. Call it PlantView, so a casual reader of your code is aware of what the class is supposed to do. Similarly, you might want to call the variables plantView instead of plantScreen, and plantNameLabel instead of plantName. plantScreen implies a variable containing UIScreen, and plantName implies an NSString more than it implies a UILabel. Same applies to quantity; call this variable quantityLabel.
Next, let's consider what the variables are depending on -- their origin's x and y do not change based on the counter, variable i. Perhaps you meant to write:
UILabel *plantName = [[UILabel alloc] initWithFrame:CGRectMake((plantScreen.frame.origin.x), (plantScreen.frame.origin.y+ plantScreen.frame.size.height * i), 160, 30.0)];
and later on:
UILabel *quantity = [[UILabel alloc] initWithFrame:CGRectMake((plantScreen.frame.origin.x), (plantScreen.frame.origin.y+ plantScreen.frame.size.height*i + 20), 160, 30.0)];
Next, avoid exceptions and exception handling. Ensure that the exception does not occur via other forms of checking; Apple highly recommends you fix exceptions while writing the application and not handle them when they run:
Important: You should reserve the use of exceptions for programming or
unexpected runtime errors such as out-of-bounds collection access,
attempts to mutate immutable objects, sending an invalid message, and
losing the connection to the window server. You usually take care of
these sorts of errors with exceptions when an application is being
created rather than at runtime.
Next, a small stylistic remark (not very important): you're mixing calling setters via properties and calling setters directly. Nothing wrong (they end up doing exactly the same), but stylistically not very nice.
Next, unless you're using ARC (automatic reference counting), don't forget to release the views once they're added as subviews.
Next, plantScreen (which I named plantView below) can have type set to Plant (which I named PlantView below) when declared inside the loop.
Last but highly important and extremely easy to miss: you call the function isMemberOfClass: instead of isKindOfClass:.
Reworked version of your code (untested):
-(void)viewDidLoad
{
[super viewDidLoad];
int i = 0;
for (PlantView *plantView in self.view.subviews)
{
if ([plantView isKindOfClass:[PlantView class]])
{
//the label which will hold the name
CGRect plantNameLabelFrame = CGRectMake((plantScreenView.frame.origin.x),
(plantScreenView.frame.origin.y + plantScreen.frame.size.height*i),
160,
30.0);
UILabel *plantNameLabel = [[UILabel alloc] initWithFrame: plantNameLabelFrame];
plantNameLabel.numberOfLines = 1;
plantNameLabel.minimumScaleFactor = .5;
plantNameLabel.adjustsFontSizeToFitWidth = YES;
plantNameLabel.textAlignment = NSTextAlignmentCenter;
plantNameLabel.backgroundColor:[UIColor clearColor];
[self.view addSubview:plantNameLabel];
[plantNameLabel release];
//create the label which will hold the quantity
CGRect quantityLabelFrame = CGRectMake((plantScreenView.frame.origin.x),
(plantScreenView.frame.origin.y + plantScreen.frame.size.height*i + 20),
160,
30.0);
UILabel *quantityLabel = [[UILabel alloc] initWithFrame: quantityLabelFrame];
quantityLabel.textAlignment = NSTextAlignmentCenter;
quantityLabel.backgroundColor = [UIColor clearColor];
quantityLabel.text = [NSString stringWithFormat:#"%d", plant.quantity]; // NB: what's "plant"?
[self.view addSubview:quantityLabel];
i++;
}
}
}
I've also removed .hidden = false (which should actually read .hidden = NO; this is Objective-C, and not C++), and bringSubviewToFront: (it's already in front, having just been added by addSubview:).
I am facing a problem with updating my labels.. it doesn't remove the old values so new values go on top of old ones.. any help with this will be appreciated..
timer = [NSTimer scheduledTimerWithTimeInterval:1
target:self
selector:#selector(updateLabels)
userInfo:nil
repeats:YES];
-(void) updateLabels{
for (GraphView *graph in arr){
// retrieve the graph values
valLabel = [[UILabel alloc] initWithFrame:CGRectMake(i * 200, 0, 90, 100)];
valLabel.textColor = [UIColor whiteColor];
valLabel.backgroundColor = [UIColor clearColor];
valLabel.text = [NSString stringWithFormat:#"Value: %f", x];
i++;
}
}
If you set the text of your label you do not need to call setNeedsDisplay or clearContext etc.
In your code, I do not know what are your variables i and x?
The main problem is that you are creating and adding new labels on your view. When you call updateLabels method, may cause a Memory leak. Simply you have n times labels overlapped.
You need to remove the labels before you create and add new labels or you can reuse which you already have. To reuse your labels you need to save them to an array and update texts.
If you want to create new labels then you can do like this unless you have other labels in your view
-(void) updateLabels{
// remove all labels in your view
for (UIView *labelView in self.view.subviews) {
if ([labelView isKindOfClass:[UILabel class]]) {
[labelView removeFromSuperview];
}
for (GraphView *graph in arr){
// retrieve the graph values
valLabel = [[UILabel alloc] initWithFrame:CGRectMake(i * 200, 0, 90, 100)];
valLabel.textColor = [UIColor whiteColor];
valLabel.backgroundColor = [UIColor clearColor];
valLabel.text = [NSString stringWithFormat:#"Value: %f", x];
i++;
}
}
When you create new labels like this you need to add them to your view as subview
[self.view addSubview: valLabel];
if you have other labels in your view then you can save them in an array and remove just them
Your updateLabels method is actually creating new UILabel controls each time so they will simply appear "on top of" older ones. I'm guessing this is not what you want, although it's not perfectly clear so apologies if I've misunderstood what you're trying to do.
If I'm correct about that, create your UILabel controls just once maybe in your viewDidLoad or similar. Then just set their .text properties when your timer fires.
You need to call setNeedsDisplay so that the app knows it has changed and redraw it.
- (void)setNeedsDisplay
Set clearsContextBeforeDrawing property of your label to YES
you can set this from nib as well as code.
label.clearsContextBeforeDrawing = YES;
I am writing a fairly simple application which spawns a thread which ultimately calls the following method to put a UILabel at a certain location. I had expected ARC to clean up the labels as the method closed. I was wrong. :)
Is there a way to force these to be cleaned up or is there something obvious that I am missing? Thanks!
-(void) drawNumberLabel:(NSString *)labelText xloc:(float)xLocation yLoc:(float)yLocation {
UILabel *tempLabel;
tempLabel = [[UILabel alloc] initWithFrame:CGRectMake(xLocation, yLocation, 27.0, 59.0)];
tempLabel.font = [UIFont fontWithName:#"Helvetica" size:fontSize];
tempLabel.text = labelText;
tempLabel.backgroundColor = backgroundColor;
tempLabel.textColor = textColor;
[self addSubview:tempLabel];
}
What do you mean by "clean up the labels"? Are you expecting that tempLabel would be deallocated at the end of this method? It won't, because when you call [self addSubview:tempLabel], your view retains the label. When the superview is deallocated, the labels you've added will also be deallocated.
When you are using ARC (Automatic Reference Counting), you should never make any memory management calls because the compiler will insert these statements for you at compile-time.
The compiler should be injecting [tempLabel release]; to the end of your method at compile-time.
However, because you have added the label as a subview to a view, the containing view will retain the label, and the label will not be released until you remove the label from that view.