When I programmatically allocated a UILabel in my custom initWithNibName method, and later in viewDidLoad, tried to assign a string to it, the label was not pointing to anything. I didn't release it; the label shows on the screen. If I create the label in IB, and assign text to it in viewDidLoad, it works.
Is it against a rule to set up manually allocated objects in viewDidLoad?
Why is it not pointing to anything, even though viewDidLoad is called after my init?
From the doc of viewDidLoad:
This method is called after the view controller has loaded its associated views into memory. This method is called regardless of whether the views were stored in a nib file or created programmatically in the loadView method. This method is most commonly used to perform additional initialization steps on views that are loaded from nib files.
In my init:
_descriptionLabel = [[UILabel alloc] initWithFrame:CGRectMake(20, 218, 280, 10)];
_descriptionLabel.numberOfLines = 0;
_descriptionLabel.lineBreakMode = UILineBreakModeWordWrap;
_descriptionLabel.font = [UIFont systemFontOfSize:12.0];
_descriptionLabel.adjustsFontSizeToFitWidth = NO;
_descriptionLabel.text = #"Description not found.";
_descriptionLabel.backgroundColor = [UIColor clearColor];
In viewDidLoad, the variable's value is 0x0.
It's the same with my manually allocated UIButton, which is fully working once the view loads.
If you want to create the UILabel programatically you can, but you still do it in viewDidLoad (as opposed to initWithNibName).
Don't be afraid to do UI setup in viewDidLoad. It is provided to add any static UI elements BEFORE the view appears on screen.
The view will not appear until just before viewDidAppear:(BOOL)animated is called.
If you have dynamic content configure it in viewWillAppear:(BOOL)animated. (This looks like your situation)
Then make sure you add it to the view:
[self.view addSubview:myLabel];
If you need future access to your new label, you will need to create an ivar to hold a pointer to it.
In the code posted, the _descriptionLabel UILabel is not retained and will be released before the view is drawn.
Try
_descriptionLabel = [[[UILabel alloc] initWithFrame:CGRectMake(20, 218, 280, 10)] retain];
Make sure you put [_descriptionLabel release] in dealloc. This assumes _descriptionLabel is an instance variable.
This basically applies to any object you create with alloc, copy, or new.
Related
This question already has an answer here:
Method calling via performSelectorOnMainThread Vs Normal method calling
(1 answer)
Closed 9 years ago.
In my viewDidLoad, I call a function:
[common startActivityIndicatorOnView:self.view];
This method adds a view with Activity indicator, in the center of self.view.
My current view is pushed on a Navigation Stack. This is how the view looks after this method returns (the activity indicator view is not in center):
However, if I call the same method this way:
[common performSelectorOnMainThread:#selector(startActivityIndicatorOnView:) withObject:self.view waitUntilDone:NO];
The view looks like the following image (the activity indicator view is in center):
I do not get, How does it make a difference if the calling line is written in viewDidLoad.
If any one can help me get this, thanks in advance.
Just for reference,
the method looks like this:
-(void) startActivityIndicatorOnView:(UIView *)view {
if ([NSRunLoop currentRunLoop] != [NSRunLoop mainRunLoop]) {
[self performSelectorOnMainThread:#selector(startActivityIndicatorOnView:) withObject:view waitUntilDone:NO];
return;
}
view.userInteractionEnabled = NO;
activityBgView = [[UIView alloc] initWithFrame:CGRectMake((view.frame.size.width/2) - 50, (view.frame.size.width/2) - 50, 100, 100)];
activityBgView.center = view.center;
activityBgView.backgroundColor = [UIColor grayColor];
activityBgView.alpha = 0.8;
spinner = [[UIActivityIndicatorView alloc] initWithFrame:CGRectMake((activityBgView.frame.size.width/2)-10, (activityBgView.frame.size.width/2)-10, 20, 20)];
spinner.activityIndicatorViewStyle = UIActivityIndicatorViewStyleWhiteLarge;
spinner.center = view.center;
[view addSubview:activityBgView];
[view addSubview:spinner];
[spinner startAnimating];
}
ViewDidLoad is called when you load viewControllers (via nib/xib or created programmatically in the loadView method), meaning xcode create all views and instantiate and alloc all objects in xib...
but, your viewController view (and subViews) are still not added in any view...
yourViewController.view.superView = nil;
so, its view has got the frame that you set in xib, but if you tell it to resize inside its superview, when you add it (e.g. with a push or an addsubview), its frame changes, but your
spinner won't change its position.
calling a performSelectorOnMainThread just will call your method later, when your current thread step ahead and may have pushed your viewController.view, so, when executed, yourViewController.view.superView exists, and so view.frame has already changed.
try to move your call to
[common startActivityIndicatorOnView:self.view];
in a viewWillAppear method: at that point yourViewController.view should been already resized to fit its superView
EDIT:
# pavel question:
after what moment yourViewController.view.superView will be not nil?
banally: when you add its view to a view. that is, firts you allocate and init it (init with a nib or via code)
something like:
yourViewControllerInstance = [[YourViewController alloc]initWithNibName:#"yourViewControllerNib" bundle:nil];
at this point the method viewDidLoad in your class is called (but yourViewController.view.superview 0 nil)
later, you usually use your new viewController, so you "push" it in the stack of a navigationController, or you just add its view to the current viewController.view... something like:
[currentViewController.view addSubview:yourViewController.view];
after this line, as you may imagine, yourViewController.view.superView = currentViewController.view, and the method viewWillAppear of yourViewController is called, so you can use superView inside it.
Notice that at this point your viewController.view is still not visible on screen, so you can adjust sizes, move or add views here without users see any changes.
after this, yourViewController will show, and at the end, and the method viewDidAppear of yourViewController is called (for any other code, in case)
I am defining a uiwebview programmatically and its delegate and requests are done but still no visible! why? Thank you
//do some loads on webviewSocial defined on IB and then set it to nil in order to init it after.
self.webViewSocial = nil;
self.webViewSocial = [[UIWebView alloc] initWithFrame:CGRectMake(0, 44, 320, 389)];
self.webViewSocial.autoresizingMask=(UIViewAutoresizingFlexibleHeight |
UIViewAutoresizingFlexibleWidth);
[self.webViewSocial setDelegate:self];
[self.vistaSocial addSubview:self.webViewSocial];
If I set a new webview it works but problem seems that webviewsocial is set on IB and after this coding is not visible anymore.
Some general notes:
make sure that self.vistaSocial is not nil
make sure that self.vistaSocial is added to self.view
Also dont release the webView and then reinitialize it
Just change the properties of the existing webView
For example if you want to change the frame of the existing webView and moving it to the new super view, you would do the following
self.webViewSocial = CGRectMake(0, 44, 320, 389);
[self.vistaSocial addSubview:self.webViewSocial];
You dont need to release it and reinitialize a new one
I suggest removing webViewSocial as an IB object since you're actually making it nil then re-initializing it programmatically, it's redundant.
If clearing your browser history is the ultimate goal, try this instead:
[webViewSocial stringByEvaluatingJavaScriptFromString:#"document.body.innerHTML = \"\";"];
I have some confusion. Not really a bug or issue I am facing. I want to know something and I am not sure this is right place for this kind of question but here is my question:
In reference to my Question Here I was trying to map single gesture on two views.
Now I am keen to know that why this is not working? I went on basics that in case I have any NSObject subclass lets say if NSString and I add this in an NSMutableArray as follows:
NSString *strTest = #"Test String";
[aryTest1 addObject:strTest];
[aryTest2 addObject:strTest];
String gets added in both of array. Now what I am doing is I created a label, set tag for the label and added that in two views as follows:
UILabel *lblTemp = [[UILabel alloc] initWithFrame:CGRectMake(10, 10, 100, 20)];
[lblTemp setTag:1];
[lblTemp setText:#"Label"];
[vw1 addSubview:lblTemp];
[vw2 addSubview:lblTemp];
Now every view has an array of subview. When we try to add an object in that view's subview why this is not being added in both of view?
I am getting a result that my second view is showing the label but first view is not having the label.
If I comment the last line of my code and don't add label in vw2 the label will be added in vw1. Than I tried a different thing.
UILabel *lblTemp = [[UILabel alloc] initWithFrame:CGRectMake(10, 10, 100, 20)];
[lblTemp setTag:1];
NSMutableArray ary1 = [[NSMutableArray alloc] init];
NSMutableArray ary2 = [[NSMutableArray alloc] init];
[ary1 addObject:lblTemp];
[ary2 addObject:lblTemp];
both array has count 1. Thats mean that I have label added in my array. after that I tried to add array's object in view than even ame problem. Label is only being added in second view:
[vw1 addSubview:[ary1 objectAtIndex:0]];
[vw2 addSubview:[ary2 objectAtIndex:0]];
When I printed retainCount of Label after initialize, adding in array1 and array2 everytime the retaincount was 1. So I think the array are not keeping the reference of same object. Its a new object copied in array. Than why label is not being added in same view?
What is actual internal process for a object to add on any view's subview. As of I know subview is an Array type than what happening? Can anyone explain me?
Thanks in Advance
The difference is that addSubview automatically removes a view from it's super view before adding it to the new view (See the class reference: http://developer.apple.com/library/ios/ipad/#documentation/uikit/reference/uiview_class/uiview/uiview.html) while an array will happily accept an object even if it is in another array.
You can create a copy of the label and place it in the other view though if this is what you need to do.
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 have one main view where I display an image, in the method viewDidLoad:
ballRect = CGRectMake(posBallX, 144, 32.0f, 32.0f);
theBall = [[UIImageView alloc] initWithFrame:ballRect];
[theBall setImage:[UIImage imageNamed:#"ball.png"]];
[self.view addSubview:theBall];
[laPalla release];
Obviously, the value of posBallX is defined and then update via a custom method call many times in the same class.
theBall.frame = CGRectMake(posBallX, 144, 32, 32);
Everything works, but when I go to another view with
[self presentModalViewController:viewTwo animated:YES];
and come back with
[self presentModalViewController:viewOne animated:YES];
the image is displayed correctly after the method viewDidLoad is called (I retrieve the values with NSUserDefaults) but no more in the second method. In the NSLog I can even see the new posBallX updating correctly, but the Image is simply no more shown...
The same happens with a Label as well, which should print the value of posBallX.
So, things are just not working if I come back to the viewOne from the viewTwo... Any idea???????
Thanks so much!
You should use dismissModalViewControllerAnimated: to switch back to viewOne from viewTwo instead of trying to present viewOne modally.
Also note that viewDidLoad is called only once - after the view controller's view is loaded into memory. If you want to perform an action once a view comes back on screen, you should do so in viewWillAppear:.
Both of these points are discussed in the UIViewController class reference and in the View Controller Programming Guide.