I am creating a view like this:
UILabel *qty = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 100, 20)];
qty.backgroundColor = [UIColor whiteColor];
qty.text =[NSString stringWithFormat:#" Qty: %#", currentQty];
qty.alpha = 0.5;
[qty setTag:999];
[self.view addSubview:qty];
[qty release];
This can happen multiple times in this view controller so before I create a new view like this I want to remove any that might exist with this tag, I am trying this:
UIView *removeView = [self.view viewWithTag:999];
[removeView removeFromSuperview];
This is not working for some reason, anyone see my problem here?
I guess i could loop through all the views and check the tag but would rather have a more elegant and direct solution.
Is the issue that you're possibly only removing one view of several? Try this:
UIView *removeView;
while((removeView = [self.view viewWithTag:999]) != nil) {
[removeView removeFromSuperview];
}
If there's only one view that's getting created/tagged/removed, you also might consider just adding a property to track that view, and writing:
[self.subView removeFromSuperview];
self.subView = qty;
Related
example if there is a method addLabel:
- (void)addLabel {
for (NSInteger i = 0; i < 5; i ++) {
UILabel *label = [[UILabel alloc] init];
[label setText:#"label"];
[[self view] addSubView:label];
[label release];
}
}
and the method is called whenever a button is fired.
Does it need to remove all the label from the subviews first (removeFromSuperView:) before addSubview again?
First, you have to give some coordinate to UILabel. So that, it can display at proper place.
You can use following line for that:
UILabel *lblTaskTitle = [[UILabel alloc] initWithFrame:CGRectMake(45.0, 5, 200.0, 35.0)];
Another thing is that, it will be better if you remove other label. (It's not necessary, but it's good practice).
You can do it in following way:
UILabel *lbl = nil;
NSArray *Arraylbl = [self.view subviews];
for (lbl in Arraylbl){
if ([lbl isKindOfClass:[UILabel class]]){
[lbl removeFromSuperview];
}
}
Hope it will be fine for you.
Let me know in case of any difficulty.
Yes you have to remove all the previous labels from super view otherwise they all will be added above the previous existing labels, so the new labels would not be understandable.
I'm creating a rate-a-fish application. When the user rates the fish, I want to display a little box which says "Average rating: * " with stars.
I have achieved something similar in cells for a table, but can't seem to get it to work in a normal view (I run the following code and nothing appears in the view):
-(void)displayAvg:(NSInteger)avg{
UILabel *text = [[UILabel alloc]initWithFrame:CGRectMake(10, 10, 90, 20)];
text.text = #"Average Rating:";
UIView *wrapper = [[UIView alloc]initWithFrame:CGRectMake(100, 200, 100, 30)];
wrapper.layer.cornerRadius = 8;
wrapper.backgroundColor = [UIColor whiteColor];
[wrapper addSubview:text];
[text release];
UIImageView *star;
NSInteger ratingI = avg;
for(int i = 0; i<ratingI; i++) {
star = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"little_star.png"]];
star.frame = CGRectMake(50 + (i * 16), 0, 16, 16);
[wrapper addSubview:star];
[star release];
}
[self.view addSubview:wrapper];
[self.view bringSubviewToFront:wrapper];
NSLog(#"Sleeping...");
[NSThread sleepForTimeInterval:3];
[wrapper removeFromSuperview];
[wrapper release];
}
I tried this code The problem is with the following line
[NSThread sleepForTimeInterval:3];
So instead of this line, you write a new method and call it with 'wrapper' as argument to remove the wrapper view.
[self performSelector:#selector(removeWrapper:) withObject:wrapper afterDelay:3];
implement the method
-(void)removeWrapper:(id)sender
{
UIView *wrapper = sender;
[wrapper removeFromSuperview];
[wrapper release];
}
This isn't answer to your question but I think it will be better to add timer that will fire in 3 seconds and then remove the window; you will not block the thread this way; Have you tried to add just simple sub view at first, i.e. wrapper with no other code? does it appear then? Or actually as I rethink the problem it might be the answer to your question - blocking thread this way might be the cause the window never get displayed. So far I know UI will be updated after thread returns to the run-loop.
I have a 320x460 view with a number of buttons, depending on the button pressed, a 280x280 view pops up over the 320x460 view (similar to the behavior of the UIAlertView) using code like this:
UIView *overlayView = [[UIView alloc] initWithFrame:CGRectMake(20, 200, 280, 280)];
overlayView.backgroundColor = [UIColor whiteColor];
[overlayView autorelease];
[overlayView addSubview:label]; // label declared elsewhere
[overlayView addSubview:backgroundImage]; // backgroundImage declared elsewhere
//... Add a bunch of other controls
[label release];
[backgroundImage release];
//... Release a bunch of other controls
[self.view addSubview:overlayView];
Everything works fine displaying the overlayView and all its controls.
The question I have is, how do I get rid of the overlayView once it's displayed? I want to make it not only not visible but to remove it completely, since the user will be popping up the overlayView repeatedly during use.
You need access to overlayView to remove it, I'd suggest adding this to the create side:
overlayView.tag = 5; // Or some other non-zero number
Then later you can use it like this:
-(void)removeOverlayView
{
UIView *overlayView = [self.view viewWithTag:5];
[overlayView removeFromSuperview];
}
I am wanting to show a simple loading dialog when certain things are happening in my app. I figured I would just create a new view, add a label to that, and then set that view to a subView of my current view.
When doing this, I don't see anything!
Here is how I am writing my method:
- (void)showLoading {
UIView *loading = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 200, 200)];
loading.backgroundColor = [UIColor blackColor];
UILabel *txt = [[UILabel alloc] initWithFrame:CGRectMake(198, 9, 94, 27)];
txt.text = #"Loading...";
txt.textColor = [UIColor whiteColor];
[loading addSubview:txt];
[super.view addSubview:loading];
[super.view bringSubviewToFront:loading];
[loading release];
[txt release];
}
Am I doing this completely wrong?
EDIT:
I added it to the viewDidLoad method, and it works how I want:
loading = [[UIView alloc] initWithFrame:CGRectMake(100, 100, 200, 200)];
loading.backgroundColor = [UIColor blackColor];
UILabel *txt = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 94, 27)];
txt.text = #"Loading...";
txt.textColor = [UIColor whiteColor];
[loading addSubview:txt];
[txt release];
[self.view addSubview:loading];
[self.view bringSubviewToFront:loading];
But when loading it from a method, it seems to lag, and not show up for a bit.
Although this doesn't directly answer your question, I'd recommend grabbing MBProgressHUD from GitHub and using that in place of a static label. Looks better, less code for you to directly maintain, etc. You can find it at http://github.com/matej/MBProgressHUD
The way I use it is by creating a subclass of UITableViewController and defining a handful of methods to show and hide the HUD view. From there, I call each relevant method when I'm loading or done loading.
Specifically, I have four methods: -hudView, -showLoadingUI, -showLoadingUIWithText:, and -hideLoadingUI.
-hudView creates a new MBProgressHUD object if one doesn't already exist, and adds it to the current view ([self.view addSubview:hudView]).
-showLoadingUI calls -showLoadingUIWithText: with a default title, -showLoadingUIWithText: just unhides the MBProgressHUD and sets a label value for it (self.hudView.labelText = #"foo";).
-hideLoadingUI hides the hudView ([self.hudView hide:YES]).
First, I don't think UIView has method called init. You may just call the super of it. The appropriate method you should call is - (id)initWithFrame:(CGRect)aRect . The frame is the position, the size of the View you want to display. More here
Another thing is why you call [super.view addSubView:], I think it should be self.view, isn't it?
I have an app where I create many uiviews and add them to the self.view of the UIViewController. My app is running really slowly. I am releasing all of my objects and have no memory leaks (I ran the performance tool). Can anyone tell me what could be making my app so slow? (code is below)
[EDIT] The array has around 30 items. [/EndEdit]
Thanks so much!
Here is the code for the loadView method of my UIViewController:
- (void)loadView {
UIView *contentView = [[UIView alloc] initWithFrame:[[UIScreen mainScreen] applicationFrame]];
contentView.backgroundColor = [UIColor whiteColor];
self.view = contentView;
[contentView release];
int length = 0;
for(NSString *item in arrayTips)
{
length++;
[item release];
}
int index = 0;
for(NSString *item in arrayTitles)
{
SingleFlipView *backView = [[SingleFlipView alloc] initWithFrame:[[UIScreen mainScreen] applicationFrame]];
backView.userInteractionEnabled = YES;
backView.backgroundColor = [UIColor whiteColor];
[backView setViewIndex:index];
[backView setLastViewIndex:length];
CGRect labelFrame = CGRectMake(10.0f, 0.0f, 300.0f, 30.0f);
UILabel *backLabel = [[UILabel alloc] initWithFrame:labelFrame];
backLabel.textAlignment = UITextAlignmentCenter;
backLabel.userInteractionEnabled = YES;
backLabel.text = item;
backLabel.font = [UIFont fontWithName:#"Georgia" size:24.0f];
backLabel.textColor = [UIColor blackColor];
backLabel.backgroundColor = [UIColor whiteColor];
CGRect textFrame = CGRectMake(10.0f, 30.0f, 300.0f, 110.0f);
UITextView *tbxView = [[UITextView alloc] initWithFrame:textFrame];
tbxView.textAlignment = UITextAlignmentCenter;
tbxView.userInteractionEnabled = YES;
tbxView.editable = FALSE;
tbxView.text = [arrayTips objectAtIndex:index];
tbxView.font = [UIFont fontWithName:#"Arial" size:14.0f];
tbxView.textColor = [UIColor blackColor];
tbxView.backgroundColor = [UIColor whiteColor];
//CGRect labelFrame = CGRectMake(10.0f, 0.0f, 84.0f, 30.0f);
UIImage *nextTip = [[UIImage imageNamed:#"NextTip.png"] retain];
UIImageView *nextTipView = [ [ UIImageView alloc ] initWithImage:nextTip];
nextTipView.frame = CGRectMake(230.0f, -10.0f, 84.0f, 30.0f);
nextTipView.userInteractionEnabled = YES;
UIImageView *view = [[ UIImageView alloc ] init];
view.userInteractionEnabled = YES;
if(self.sexString == #"Men")
{
UIImage *imgTip = [[UIImage imageNamed:#"feet_small.jpg"] retain];
view.image = imgTip;
view.frame = CGRectMake(0.0f, 110.0f, 416.0f, 228.0f); //59*161
[imgTip release];
}
[backView addSubview:view];
[backView addSubview:tbxView];
[backView addSubview:backLabel];
//[backView addSubview:nextTipView];
[self.view addSubview:backView];
[backView release];
[backLabel release];
[nextTip release];
[nextTipView release];
[tbxView release];
[view release];
index++;
[item release];
}
}
It's going to depend upon how many items are in arrayTitles. If you're just adding one or two of these, you shouldn't see a HUGE slowdown; more, and you will. You should probably take a look at the way UITableView handles its cells; only create these as they're actually needed/used, or, better yet, only create one of these, and set its contents on-the-fly.
A few other notes:
== is not a valid string comparison operator in Objective-C; use [string1 isEqualTo: string2]
It appears you're trying to place a lot of these on screen at the same time, which doesn't seem like it would make a lot of sense.
it looks like you've got a spurious [item release] at the end there (you're never retaining item, so there's no need to release it.
the whole first loop ( for(NSString *item in arrayTips)... frightens and confuses me; items in NSArrays are already retained by the array. You shouldn't have to explicitly retain/release them in this way.
Having deep view hierarchies can lead to slow downs that you can often fix through flattening them some with custom views, but if you are using simple views you can have dozens on the screen with no perceptible performance impact, so in general I recommend ignoring how many views you have when you are developing, and then reducing the view count if it proves to be a performance problem.
Having said that, you appear to be setting up something with an unboundedily large number of views which is not good. Without knowing how many entries there are in array titles I can't tell you what is going on exactly, but I suspect that while the actual visual heiarchy with each backView you are creating is fine, making a backView for each item in the array and using indices to have the front most one hide all the other ones behind it is causing you to have way too many views.
So, how to test it:
Add a break to the bottom of your for loop. Make the loop drop out after a single iteration and see if performance improves. If it does, then the huge view hierarchies are your issue. YOu may have to hack up the routine that changes the indexes to make sure it never swaps to an invalid index to test.
If that is the case you have a few options. You could implement a custom view and flatten every backview into a single view, but depending on how many you have that mat not be sufficient, and it is more work than simply building the back views the way you currently are, but on demand instead of at load time:
1) Refactor the code in your for loop into a separate method that makes a backView for a specific title and attaches it to the view at that time.
2) Where ever you are currently altering the indexes to make the backview visible, instead call the new method to actually build and attach the backview at that time
Don't forget to make as many of your views opaque as you can. Transparent views are a major source of performance issues.