I created a class to take care of my UILabels in 1 line instead of taking 4-5 by doing...
+(UILabel*)BeautifyLabel:(UILabel *)label withText:(NSString *)message withFont:(NSString *)font andSize:(float)size andColor:(UIColor *)theColor{
label.backgroundColor = [UIColor clearColor];
label.textColor = theColor;
label.font = [UIFont fontWithName:font size:size];
label.text = message;
return label;
}
And to call it, i do
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake....];
label = [CommonMethods BeautifyLabel:label withText:#"hi" withFont:#"Helvetica" andSize:13 andColor:[UIColor whiteColor]];
[self.view addSubview label];
[label release];
The analyzer probably doesn't like the part where I pass the label to my CommomMethods class, but since i'm initializing and releases the label in the current controller and the CommonMethods class doesn't do anything memory related, this is safe, right?
Also, would this be cause for Apple to reject my app?
Thanks
Your BeautifyLabel method should not return the label pointer. That is probably what the analyzer is complaining about (but it would be nice to see the text of the analyzer error).
The analyzer is assuming that BeautifyLabel method is returning a new instance of the label which then overwrites the one you had in label variable thus causing a memory leak of the overwritten instance (and overreleasing of the returned instance).
In the code:
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake....];
label = [CommonMethods BeautifyLabel:label withText:#"hi" withFont:#"Helvetica" andSize:13 andColor:[UIColor whiteColor]];
label is allocated on the first line, on the second line label is replaed by the call to BeautifyLabel or so the analyzer thinks, not knowing what is done in BeautifyLabel. It can't assume you are returning the same object.
Either do not make the assignment:
[CommonMethods BeautifyLabel:label withText:#"hi" withFont:#"Helvetica" andSize:13 andColor:[UIColor whiteColor]];
or use different label pointer names:
UILabel *labelTemp = [[UILabel alloc] initWithFrame:CGRectMake....];
label = [CommonMethods BeautifyLabel:labelTemp withText:#"hi" withFont:#"Helvetica" andSize:13 andColor:[UIColor whiteColor]];
Related
In my project i have created textfield using actionsheet button.how can i disable the userinterface after first creation.
-(void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex
{
if (buttonIndex==0)
{
text1 = [[UITextField alloc] initWithFrame:CGRectMake(x2,y2+3, 300, 25)];
text1.backgroundColor = [UIColor whiteColor];
text1.clearButtonMode = UITextFieldViewModeWhileEditing;
text1.font = [UIFont systemFontOfSize:15.0f];
text1.placeholder=#"ENTER HOME LOAN INTEREST";
[text1 setKeyboardType:UIKeyboardTypeNumberPad];
text1.textAlignment=NSTextAlignmentCenter;
text1.userInteractionEnabled=YES;
[scrollview addSubview:text1];
scrollview.contentSize=CGSizeMake(self.view.frame.size.width,self.view.frame.size.height+to);
to+=to;
y2+=30;
img6.frame=CGRectMake(0, y2+4, 320, 60);
y2=y2+62;
img7.frame=CGRectMake(0, y2+5, 320, 60);
y2=y2-62;
}
else if (buttonIndex==1)
{
text2 = [[UITextField alloc] initWithFrame:CGRectMake(x2, y2+3, 300, 25)];
text2.backgroundColor = [UIColor whiteColor];
text2.clearButtonMode = UITextFieldViewModeWhileEditing;
text2.font = [UIFont systemFontOfSize:15.0f];
text2.placeholder=#"ENTER EDUCATION EXPENSE";
[text2 setKeyboardType:UIKeyboardTypeNumberPad];
text2.userInteractionEnabled=YES;
text2.textAlignment=NSTextAlignmentCenter;
[scrollview addSubview:text2];
scrollview.contentSize=CGSizeMake(self.view.frame.size.width,self.view.frame.size.height+to);
to+=to;
y2+=30;
img6.frame=CGRectMake(0, y2+4, 320, 60);
y2=y2+60;
img7.frame=CGRectMake(0, y2+5, 320, 60);
y2=y2-60;
}
}
how can i prevent user from creating same textfield many time
It looks to me as if the two textfields are the same, with the exception of the placeholder, and presumably what you do with the value later. You could easily use a single textfield that is accessible as a property in your class. If you are using storyboards, it's possible that you can place the textfield in your storyboard and perhaps set it to 'hidden'.
Once you have a common text field, you can simply set the placeholder text and make the field visible inside the actionsheet delegate method. If there are other differences in the layout, then those too can be handled at this point.
This approach would need a way to hold 'state' so that you know the context of the field later on. You could manage this by examining the placeholder text, but this would be fragile because the text may change on a whim. A better approach would be to set a tag value e.g. text2.tag = 1 for home load interest and text2.tag = 2 for education expense. You can later access the tag value to interpret the context.
Because there is only one field, and because it is accessible by a property, you can later remove the textfield from the superview, or hide it again and use another flag to know that it has been displayed to the user. Most likely you can check that you have a value for either home loan interest or education expense, in which case, don't show it again.
You can hide/remove/disable the button that invokes the actionsheet from within the actionsheet delegate callback method too, again you will need a property in order to access it.
I have several UIView subclasses (buttons, labels, etc.) that follow the following setup pattern. My question is, why are messages still able to be sent to the UILabel after release?
myLabel = [[UILabel alloc] initWithFrame:someFrame];
[someUIView addSubview:myLabel];
[myLabel release];
myLabel.textAlignment = UITextAlignmentCenter;
// other property changes to myLabel
They are "owned" by a new UIView, I suppose, but I don't understand why release doesn't destroy the original object and thereby all messages to it. I'm not making property changes through someUIView's subViews. I'm not complaining. I'm just trying to understand why.
EDIT: I should add that these are instance variables, if that makes a difference.
The object is not destroyed as long as the retain count is greater than 0. In this case someUIView has retained the object.
It is really best not to access an object after releasing it. a better pattern might be:
myLabel = [[[UILabel alloc] initWithFrame:someFrame] autorelease];
myLabel.textAlignment = UITextAlignmentCenter;
[someUIView addSubview:myLabel];
myLabel = nil;
Second example:
myLabel = [[UILabel alloc] initWithFrame:someFrame];
[someUIView addSubview:myLabel];
myLabel.textAlignment = UITextAlignmentCenter;
// other property changes to myLabel
[myLabel release];
myLabel = nil;
Your call to -addSubview: calls -retain on the label when it receives it. At this point, you relinquish ownership (by calling -release) and only the view owns it. But it still exists until the containing view also releases it.
You can still send messages to the label because the label hasn't been released yet. -addSubview: retains the objects passed in, so the object remains in memory since the view is still holding a reference and you didn't nil the myLabel pointer.
Because they are probably retained before...
I am currently create a UITableViewCell with a UITextField in it.
On click of the text field, I want to bring up a number keyboard that I created. And as I type, the textfield should check the input for me; on click of other place, the keypad should be dismissed.
Code:
UITableViewCell *sizeCell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:#"sizeCell"];
sizeCell.textLabel.text = #"Size";
UITextField* sizeField = [[UITextField alloc] initWithFrame:CGRectMake(185, 10, 100, 28)];
sizeField.text = #"0";
sizeField.textAlignment = UITextAlignmentRight;
sizeField.textColor = [UIColor colorWithRed:50.0/255.0 green:79.0/255.0 blue:133.0/255.0 alpha:1.0f];
sizeField.backgroundColor = [UIColor clearColor];
sizeField.keyboardType = UIKeyboardTypeDecimalPad;
[sizeCell.contentView addSubview:sizeField];
rows = [[NSArray arrayWithObjects:switchCell, typeCell, sizeCell, nil] retain];
I tried to implement UITextFieldDelegate like:
-(BOOL)textFieldShouldReturn:(UITextField *)textField{
[sizeField resignFirstResponder];
return YES;
}
but the keyboard doesn't go away...
How do I validate the input and dismiss the keyboard?
You never set the delegate on your textfield so that textFieldShouldReturn: gets called. Make sure your class conforms to UITextFieldDelegate and then do the following:
...
UITextField* sizeField = [[UITextField alloc] initWithFrame:CGRectMake(185, 10, 100, 28)];
sizeField.delegate = self; //This is important!
sizeField.text = #"0";
...
A few observations:
As another poster suggested, make sure you set the keyboard's delegate correctly.
If you want to dismiss the keyboard on keyboard return, make sure you have one on your custom keyboard and it's correctly set up to call the ...ShouldReturn method.
If you want to dismiss on taps outside, you'll have to do that on your own.
You are declaring sizeField inside the method where you are setting it up, then calling it from another method outside that scope. I assume you have a class variable called sizeField or you'd be getting a compiler error. However, declaring it again when you're setting it up like you do shadows the class variable declaration so it never gets set up. Incidentally, that's a memory leak.
This shouldn't affect the actual running of the program if all else is correct (but it will if, e.g. 4 is the problem and not fixed), but I think it's better form to call [textField resign...] instead of [sizeField resign...]. At the least you should assert(textField == sizeField).
In my iPhone app, views will often load slowly when transitioning, like if a user clicks a button on the Tab Bar Controller. This happens more if the phone is low on memory. It doesn't really come up on 3GS phones, but it's a big problem on 3G phones.
I suspect that I'm not following some best practices for creating UIViewControllers. I think I might be doing too much in the init functions, not using the viewDidLoad function, or something. It seems to affect all my views, so I think it's a problem with my style in general, not some particular snippet.
Can anyone tell me what i might be doing wrong? Here is some sample code, from a UIViewController subclass:
EDIT: In response to the question: "where is this being called?"
This function gets called in this case when the user clicks a marker on the map:
if(marker.label.tag == SavedBookmarkTag) {
SavedDetailScreen *savedBookmark = [[[SavedDetailScreen alloc] initBookmarkView:
[(NSDictionary *)marker.data objectForKey:#"bookmark"]]autorelease];
[savedBookmark showMap];
[self.navBar pushViewControllerWithBackBar:savedBookmark];
return;
}
END EDIT
-(id)initBookmarkView: (Bookmark *)bm {
self = [self initView];
self.bookmark = bm;
primaryLabel.text = [bm title];
secondaryLabel.text = [self getLineWithLat:[bm lat] AndLon:[bm lon] AndDate:[bm timeCreated]];
return self;
}
- (id)initView {
self = [super init];
self.isWaypoint = NO;
UIImageView *bg = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"238-beveled-background.png"]];
bg.frame = CGRectMake(0, 0, 320, 376);
[self.view addSubview:bg];
bg = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"small-label.png"]];
[self.view addSubview:bg];
[bg release];
self.primaryLabel = [[UILabel alloc]init];
primaryLabel.font = TITLE_FONT;
primaryLabel.backgroundColor = [UIColor clearColor];
primaryLabel.textColor = LIGHT_BLUE;
self.secondaryLabel = [[UILabel alloc]init];
secondaryLabel.font = TEXT_FONT;
secondaryLabel.backgroundColor = [UIColor clearColor];
secondaryLabel.textColor = LIGHT_BLUE;
secondaryLabel.lineBreakMode = UILineBreakModeClip;
self.thirdLabel = [[UILabel alloc]init];
thirdLabel.font = TEXT_FONT;
thirdLabel.backgroundColor = [UIColor clearColor];
thirdLabel.textColor = LIGHT_BLUE;
thirdLabel.lineBreakMode = UILineBreakModeCharacterWrap;
[self.view addSubview:primaryLabel];
[self.view addSubview:secondaryLabel];
[self.view addSubview:thirdLabel];
self.loadingBackground = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"stats-box.png"]];
loadingBackground.frame = CGRectMake(0, 115, loadingBackground.frame.size.width, loadingBackground.frame.size.height);
[self.view addSubview:loadingBackground];
[self.view sendSubviewToBack:loadingBackground];
AnimatedGif *animatedGif = [[[AnimatedGif alloc] init] autorelease];
NSData *data = [NSData dataWithContentsOfFile:[[NSBundle mainBundle] pathForResource:#"35" ofType:#"gif"]];
[animatedGif decodeGIF: data];
UIImageView *loadingImage = [animatedGif getAnimation];
loadingImage.frame = CGRectMake(150,150,loadingImage.frame.size.width,loadingImage.frame.size.height);
[loadingImage startAnimating];
[loadingBackground addSubview:loadingImage];
[loadingImage release];
[self layoutSubviews];
return self;
}
- (void) layoutSubviews {
self.view.frame = CGRectMake(0,0,320,372);
primaryLabel.frame = CGRectMake(30, 30, 260, 18);
secondaryLabel.frame = CGRectMake(30 ,52, 260, 16);
thirdLabel.frame = CGRectMake(30, 72, 260, 16);
}
The slowness that you're seeing can probably be attributed to the fact that constructing objects and reading data from the flash are both expensive processes. Look for opportunities to reuse existing objects rather than constructing them multiple times, and consider deferring especially expensive operations until after the view gets displayed.
In this case, I would start with a couple changes:
Make savedBookmark a member variable so that you can construct it once and reuse it. Replace your initBookmarkView: method with a setBookmarkView: method that you can call after this member variable is constructed to reconfigure your labels for the specific bookmark being displayed.
Take the subview creation code out of initView and put it in loadView. This is the most appropriate place to construct your own view hierarchy programmatically. UIViewController implements lazy loading on its view property to defer construction as long as possible. The view property is nil until the first time it's requested. At that point UIViewController calls loadView to set the property. The default implementation loads the view from a nib file if one is defined. Otherwise it just constructs an empty UIView and makes that the main view. Note that you'll have to construct the container view and set the view property yourself.
In other apps you may get some improvement by moving some initialization code into viewDidLoad:, which gets called after the view property is loaded, whether programmatically or from a nib. If you ever have an especially slow operation like loading images from a remote URL, you might want to start loading the data asynchronously in viewDidLoad:, and then update your subviews once the data finishes loading. In some cases you may also want to defer some code until viewDidAppear:. Just be aware that this method gets called every time the view appears, unlike loadView and viewDidLoad:, which only get called once.
This:
UILable *myLabel = [[UILabel alloc] init];
UILable *myLabel = [[UILabel alloc] init];
gives me a redefinition error.
But this:
for(i=0;i<5;i++)
{
UILable *myLabel = [[UILabel alloc] init];
// some label code here
[self.view addSubview:myLabel];
[myLabel release];
}
doesn't. So is the second one false? Should I define it before and just reuse it?
Is that right:
UIIMageView *Sign;
//Some Sign Stuff
Sign = [[UIImageView alloc]init];
Sign.image = [UIImage imageNamed:#"Minus.png"];
frame = CGRectMake(160 ,80, 64, 64);
Sign.frame = frame;
[scrollView addSubview:Sign];
Sign = nil;
[Sign release];
//Some other Sign stuff
Sign = [[UIImageView alloc]init];
Sign.image = [UIImage imageNamed:#"Plus.png"];
frame = CGRectMake(200 ,80, 64, 64);
Sign.frame = frame;
[scrollView addSubview:Sign];
Sign = nil;
[Sign release];
is that correct? That doesnt work without the Sign = nil. So it seems a little wobbly too.
You cannot have identical variable names used in the same block level scope. So in your first example you cannot have a variable definition with the same name, you have to name them differently.
- (void) method {
UIImageView* image1;
// here we define a new block scope. This can be a block of any kind (while, for, if)
{
// All reference in this block to this variable will see this definition.
UIImageView* image1;
// Using image1 here
}
// Here we see again the image1 defined at the beginning of the method.
}
In your loop example you are in a new scope that it's reinitialize after each iteration.
Your third example is correct in that it define the variable only one time. You reuse this variable after that to assign a new object. The third one is less elegant in that your variable name does not describe well for each case what are their purpose.
For your case of 'Sign = nil' this effectively make the line that follows useless since in Objective-C a message sent to a nil object is ignored.
I would suggest to define a method that you can call to create your images that look the same. Something like:
- (void) createImage:(NSString*) image frame:(CGRect) frame {
UIImageView *Sign;
Sign = [[UIImageView alloc]init];
Sign.image = [UIImage imageNamed:image];
Sign.frame = frame;
[self.scrollView addSubview:Sign];
[Sign release];
}
Your for-loop is perfectly fine. The scope of myLabel is limited to one run of your for-loop. So each run a new variable to hold the reference to your UILabel is created.
The second code you posted has leaks.
Sign = nil
[Sign release]
This will release the object at address nil and not the object you created. I can't see what else is wrong with your code, but your fix is definitely not fixing the root cause. Maybe it will help to post what error/warning you get when removing Sign = nil.
Also note that starting your variable names with a capital letter is not a good naming convention, because usually class names start with one.