Subview remove and add in objective c/iphone - objective-c

I have a UIViewController class where I create a UIView instance.
And then I initialize (means fill) it with 25 subviews each containing an image (1, 2, ..., 25). Then after clicking 5 times in these image I called a function where I used
for(UIView *subview in [contentView subviews]) {
[subview removeFromSuperview];//ContentView name of my view
}
to remove the previously added subview. And then I use the same approch to
add 25 new subviews (image 1,2,3,....25). But this time no subview is added.
Can someone plz give me full code of adding & removing subview.
I have used the following code when I first add subview
//create main window
contentView = [[UIView alloc] initWithFrame:[[UIScreen mainScreen] applicationFrame]];
contentView.backgroundColor = [UIColor blackColor];
self.view = contentView;
[contentView release];
//adding 25 subview 1st time
int a=0;
int b=0;
for (int i = 0; i < 26; i++)
{
CGRect dragRect = CGRectMake(0.0f, 0.0f, x, y);
dragRect.origin = CGPointMake(a,b);
DragView *dragger = [[DragView alloc] initWithFrame:dragRect];
NSString *Flower = [[NSArray arrayWithObjects:#"1.png", #"2.png", #"3.png",#"4.png", #"5.png", #"6.png",#"7.png",#"8.png", #"9.png",#"10.png", #"11.png", #"12.png",#"13.png",#"14.png",#"15.png",#"16.png",#"17.png",#"18.png",#"19.png",#"20.png",#"21.png",#"22.png",#"23.png",#"24.png",#"25.png",#"26.png", nil] objectAtIndex:i];
[dragger setImage:[UIImage imageNamed:Flower]];
[dragger setUserInteractionEnabled:YES];
[self.view addSubview:dragger];
[dragger release];
a+=10;
b+=10;
}
//then removing 25 subview
//adding 25 subview 2nd times
I used the same approch to add the second time as first time, but the problem is that when I remove 25 subview and then add 25 subview, these subview are not added/shown, the view remain same. I am tired with these problem. plz someone help me.

The problem might be the way you remove the old views. You are modifying an array while iterating over it, which does not work. Try this code to remove the old views:
UIView* subview;
while ((subview = [[contentView subviews] lastObject]) != nil)
[subview removeFromSuperview];

A nice one liner is:
[view.subviews makeObjectsPerformSelector:#selector(removeFromSuperview)];

Looking at your code, I can suggest the following changes. There's one line in your for loop which is terribly inefficient:
NSString *Flower = [[NSArray arrayWithObjects:#"1.png", #"2.png", #"3.png",#"4.png", #"5.png", #"6.png",#"7.png",#"8.png", #"9.png",#"10.png", #"11.png", #"12.png",#"13.png",#"14.png",#"15.png",#"16.png",#"17.png",#"18.png",#"19.png",#"20.png",#"21.png",#"22.png",#"23.png",#"24.png",#"25.png",#"26.png", nil] objectAtIndex:i];
[dragger setImage:[UIImage imageNamed:Flower]];
Either take the Flower initialisation out of the the for loop (to only create the array once) or do the following:
[dragger setImage:[UIImage imageNamed:[NSString stringWithFormat:#"%d.png", i]]];
The code itself looks like it should work though. If you add 26 subviews then remove the 26 subviews then add the 26 subviews in exactly the same way then it should display as you'd expect.

Related

Subclass of UIPageControl refresh only after uiscrollview move, not before

the problem I've met today is with my subclass of UIPageControl. When I initialize it, the frame (specifically the origin) and image of dots stays default, which is the problem, since I want it to change right after initialization. However, when I move with scrollView (as in "touch and move") after initialization, they (the dots) somehow jump to the right position with correct images.
What could be the problem?
Code:
CustomPageControl.m
- (id) initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
activeImage = [UIImage imageNamed:#"doton.png"];
inactiveImage = [UIImage imageNamed:#"dotoff.png"];
return self;
}
- (void) updateDots
{
for (int i = 0; i < [self.subviews count]; i++)
{
UIImageView *dot = [self.subviews objectAtIndex:i];
if (i == self.currentPage) dot.image = activeImage;
else dot.image = inactiveImage;
[dot setFrame:CGRectMake(i * 13.5, 1.5, 17, 17)];
}
}
- (void)setCurrentPage:(NSInteger)currentPage
{
[super setCurrentPage:currentPage];
[self updateDots];
}
#end
ChoosingView.m - init part
scrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, 160, 300)];
[scrollView setBackgroundColor:[UIColor clearColor]];
[scrollView setDelaysContentTouches:NO];
[scrollView setCanCancelContentTouches:YES];
[scrollView setClipsToBounds:NO];
[scrollView setScrollEnabled:YES];
[scrollView setPagingEnabled:YES];
[scrollView setShowsHorizontalScrollIndicator:NO];
[scrollView setShowsVerticalScrollIndicator:NO];
pageControl = [[CustomPageControl alloc] initWithFrame:CGRectMake(200, 300, 80, 20)];
[pageControl setBackgroundColor:[UIColor clearColor]];
pageControl.numberOfPages = 6;
[pageControl setCurrentPage:0];
the last line is when I would expect the UIPageControl to refresh, however that does not happen.
Does this happen with the standard UIPageControl implementation?
Your problem states that your objects subViews (eg the UIImageViews) rects/size are not initialising to your desired size/position.
I implemented this code in my project with a nib rather than programmatically and I needed to call -(void)updateDots to set it as its initial condition was the standard dots..
I dont see how the UIScrollView has any bearing impact on this unless somehow its linked to your -(void)updateDots function (E.g. your setting the currentIndex of your custom page control). You state, "However, when I move with scrollView (as in "touch and move") after initialization, they (the dots) somehow jump to the right position with correct images."
Because they "jump to the right position with correct images" it means that your -(void)updateDots function must be getting called. I dont see any other explanation.
Also your iteration loop assumes that all the UIViews in your .subViews array are UIImageViews, although fairly safe to assume this, I would check to see if the UIView is a UIImageView with reflection.

Subview of TableView removed - Tableview not visible

For the first time working with CorePlot (after a couple of hours trying to set it up :P )
on my view, i have a tableview. when a certain IBAction is called i want to show another view (a graph) instead of the tableview.
my approach was to add a subview with the same size to the tableview. it works fine to display the graph, but when i remove the graphs view from [table subviews] the tableview does not reappear.
note:
expenseTable: my tableView
hasSubView: (BOOL) that indicates if a graph is shown right now or not
code
-(IBAction)displayDayBalanceGraph:(id)sender{
if (hasSubView) {
[[expenseTable subviews] makeObjectsPerformSelector: #selector(removeFromSuperview)];
NSLog(#"%#",expenseTable.subviews);
}
else{
[self initializeMonthArray];
CPTGraphHostingView *host = [self buildGraphView];
[expenseTable addSubview:host];
CPTXYGraph *graph = [[CPTXYGraph alloc ]initWithFrame:host.frame];
host.hostedGraph = graph;
CPTScatterPlot *plot = [[CPTScatterPlot alloc]init ];
plot.dataSource = self;
[graph addPlot:plot];
[expenseTable reloadData];
hasSubView = !hasSubView;
}
}
-(CPTGraphHostingView *)buildGraphView{
CPTGraphHostingView *view = [[CPTGraphHostingView alloc]initWithFrame:CGRectMake(0, 0, 312, 260)];
[view setBackgroundColor:[self grayColor]];
return view;
}
1st Screenshot: TableView displayed
2nd Screenshot: GraphView displayed
sidenote: this is a sampleplot =)
3rd Screenshot: GraphView dismissed
has anyone an idea what i missed? (or messed ;) )
It's not generally a good idea to add views as subviews of UITableView.
Instead, you could either remove the table view and replace it with the Core Plot view:
[tableView removeFromSuperview];
[containerView addSubview:corePlotView];
Make sure you have a reference to the table view somewhere or it will be released.

iOS Horizontal Scrolling Calendar

Some background. I'm helping a group of students at my son's school and they want to write a simple tracking app that managers student observations that will be used on iPod Touch.
I've only done very basic iOS development using the standard widgets, but I'm keen to help.
We worked through and designed the functionality and interface of the app and are now starting to program. Where I am out of my depth is that they want to have a strip running along the bottom of the screen with each day as a little block displaying the date and date and an indicator to show if there was an observation for that day.
I was hoping that you guys might be able to point me in the right direction, either an existing widget or explain in detail how to achieve this. I've tried a few approaches with no luck.
I'd appreciate any assistance with this as I know this must be a common requirement, but I'm struggling to get through this.
I thought I'd post my final solution in case others find it useful.
In view root view controller I added a custom UITableViewController which basically rotates the table 90 degrees and then rotates each table cell -90 degrees. It does what I want is was pretty straight forward.
Code for root view controller. This sets the size and position of the table :
scrollingDateViewController=[[ScrollingDateViewController alloc] initWithNibName:#"ScrollingDateView" bundle:[NSBundle mainBundle]];
CGRect rect=CGRectMake(0,330,320,100);
scrollingDateViewController.view.frame=rect;
scrollingDateViewController.view.backgroundColor=[UIColor clearColor];
scrollingDateViewController.delegate = self;
[self.view addSubview:scrollingDateViewController.view];
Key Code for ScrollingDateViewController. This rotates the table 90 degrees and the each cell -90 degress :
- (void)viewDidLoad {
today = [[NSDate alloc] init];
CGRect rect=self.view.frame;
myTableView.frame=rect;
myTableView.layer.transform = CATransform3DRotate(CATransform3DIdentity,1.57079633,0,0,1);
myTableView.backgroundColor=[UIColor clearColor];
myTableView.separatorColor=[UIColor clearColor];
myTableView.frame=rect;
myTableView.rowHeight=44; // cellWidth
myTableView.showsHorizontalScrollIndicator=NO;
myTableView.showsVerticalScrollIndicator=NO;
myTableView.separatorColor=nil;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
UITableViewCell *cell=[tableView dequeueReusableCellWithIdentifier:#"tableCell"];
if(!cell){
cell=[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"tableCell"];
cell.contentView.backgroundColor = [[UIColor alloc] initWithPatternImage:[UIImage imageNamed:#"cell_back.png"]];
cell.contentView.transform = CGAffineTransformMakeRotation(-1.57079633);
cell.selectionStyle=UITableViewCellSelectionStyleGray;
// Add background image view
CGRect rect=CGRectZero;
UIImageView *imgView=[[UIImageView alloc] initWithFrame:rect];
imgView.contentMode=UIViewContentModeScaleToFill;
imgView.layer.transform = CATransform3DRotate(CATransform3DIdentity,-1.57079633,0,0,1);
imgView.tag=101;
[cell addSubview:imgView];
rect.origin.x=0;
rect.origin.y=0;
rect.size.height=80;
rect.size.width=44;
imgView.frame=rect;
[imgView release];
// Add Day Label
UILabel *dayLabel=[[UILabel alloc] initWithFrame:rect];
dayLabel.layer.transform = CATransform3DRotate(CATransform3DIdentity,-1.57079633,0,0,1);
dayLabel.tag=102;
dayLabel.font = [UIFont fontWithName:#"Geogrotesque-Medium" size:14.0];
dayLabel.textAlignment=UITextAlignmentCenter;
dayLabel.backgroundColor=[UIColor clearColor];
dayLabel.textColor=[UIColor darkGrayColor];
[cell addSubview:dayLabel];
rect.origin.x=40;
rect.origin.y=0;
rect.size.height=44;
rect.size.width=20;
dayLabel.frame=rect;
[dayLabel release];
// Add Date Label
UILabel *dateLabel=[[UILabel alloc] initWithFrame:rect];
dateLabel.layer.transform = CATransform3DRotate(CATransform3DIdentity,-1.57079633,0,0,1);
dateLabel.tag=103;
dateLabel.font = [UIFont fontWithName:#"Geogrotesque-Bold" size:18.0 ];
dateLabel.textAlignment=UITextAlignmentCenter;
dateLabel.backgroundColor=[UIColor clearColor];
dateLabel.textColor=[UIColor darkGrayColor];
[cell addSubview:dateLabel];
rect.origin.x=55;
rect.origin.y=0;
rect.size.height=44;
rect.size.width=20;
dateLabel.frame=rect;
[dateLabel release];
}
UILabel *label=(UILabel*)[cell viewWithTag:102];
label.text= [self getDayString:displayDate];
label=(UILabel*)[cell viewWithTag:103];
label.text= [self getDateString:displayDate];
return cell;
}
IMHO, you should create a view which you want to be shown for each date. Create something like
#interface DayView : UIView {
You can implement it with nib or make everything programmatically in drawRect method.
After it go to your Xcode documentation and search for UIScrollView. Xcode will provide your with code samples, use project "Scrolling" to find out how to use scrollView. In this sample they scroll pics, and you replace them with our custom DayViews.
Good luck!

iOS (Re)Use a NIB as a Template

I have created a nib file with different views. I was planning to use this nib file as a template, much like in web (i.e. html). So create a new instance of this nib fill it with actual data, create another new instance of nib & fill with actual data etc...
But when I try to do this, nib instance is not behaving the same. Only one gets created. Am I missing something? Aren't nib files supposed to be used this way?
Here's how I tried to use my nib file -
int yCoord = 0;
for(int i=0; i<[resultSet count]; i++)
{
[[NSBundle mainBundle] loadNibNamed:#"msgView" owner:self options:nil];
UIView *tmpMsgView = [[UIView alloc] initWithFrame:CGRectMake(0, yCoord, msgView.view.frame.size.width, msgView.view.frame.size.height)];
tmpMsgView = msgView.view;
UILabel *senderLabel = (UILabel *)[tmpMsgView viewWithTag:1];
[senderLabel setText:#"trial"];
[self.view addSubview:tmpMsgView];
[tmpMsgView release];
yCoord += msg.view.frame.size.height;
}
this msgView is hooked up to the nib file (through IB) and the owner of that nib file too is defined as this viewController class.
Your code is adding the same instance over and over again, not a new instance each time.
UIView *testView = [[UIView alloc] init];
testView.backgroundColor = [UIColor blueColor];
for (int i=0; i<5; i++) {
testView.frame = CGRectMake(0.0, (i+1)*40.0, 200.0, 20.0);
[self.window addSubview:testView];
}
[testView release];
and
for (int i=0; i<5; i++) {
UIView *testView = [[UIView alloc] init];
testView.backgroundColor = [UIColor blueColor];
testView.frame = CGRectMake(0.0, (i+1)*40.0, 200.0, 20.0);
[self.window addSubview:testView];
[testView release];
}
are two very different things.
the first actually makes very little sense and results in a single blue bar,
the second makes much more sense and results in 5 blue bars -- I think this is what you want.
look at the following, it will create 5 rectangles with all different colors -- this is to illustrate that each is rectangle is its seperate instance loaded from a nib:
for (int i=0; i<5; i++) {
NSArray *nibObjects = [[NSBundle mainBundle] loadNibNamed:#"aView" owner:self options:nil];
UIView *nibView = [nibObjects objectAtIndex:0];
nibView.backgroundColor = [UIColor colorWithRed:(i+1)*0.14 green:(i+1)*0.11 blue:200.0 alpha:1.0];
nibView.frame = CGRectMake(0.0, (i+1)*40.0, 200.0, 20.0);
[self.window addSubview:nibView];
}
now, if we assign our nibObjects array only once before the loop, we again have the same issue in that we add the same instance over and over again, which will result in one rectangle only:
NSArray *nibObjects = [[NSBundle mainBundle] loadNibNamed:#"aView" owner:self options:nil];
for (int i=0; i<5; i++) {
UIView *nibView = [nibObjects objectAtIndex:0];
nibView.backgroundColor = [UIColor colorWithRed:(i+1)*0.14 green:(i+1)*0.11 blue:200.0 alpha:1.0];
nibView.frame = CGRectMake(0.0, (i+1)*40.0, 200.0, 20.0);
[self.window addSubview:nibView];
}
hope this helps
You have memory management issues in your code.
UIView *tmpMsgView = [[UIView alloc] initWithFrame:CGRectMake(0, yCoord, msgView.view.frame.size.width, msgView.view.frame.size.height)];
You alloc/init a UIView and on the next line you store some other view in the variable. You lose the pointer to the UIView and therefore you leak its memory.
Whether the code using senderLabel will work depends on what you defined in the nib file.
In [tmpMsgView release]; you're most probably releasing something you do not own (the pointer no longer points to the UIView you alloc/inited). The issue here depends on how you declare msgView. If you are releasing an object you do not own, it might get deallocated and that might be the reason you do not see it in your app.
What you are trying to do is fine - this is what is routinely done for loading table view cells, for example.
If I do this I have a single outlet from the view controller (which is set as files owner which you have done). This will be the topmost object in the nib, eg a UIView. All of your other Nib components are just subviews of this, you can identify with tags or, if your topmost view is a custom view, that can have outlets which are connected appropriately.
When you do loadNibNamed this will set your outlet ivar to the topmost object. You then assign this to a method level variable, and set your ivar back to nil. You can then do what you like with this before adding it as a subview to something else. Next time you load the nib, you get another fresh version.
Hope that makes sense, let me know if I can add anything else. The problem in your code is that you are doing things with msgView.view instead of the top level object.

Clear a CALayer

I use a CALayer and CATextLayers to lay out the numbers on a sudoku grid on the iPhone.
I have a tableView that lists some sudokus. When I tap one table cell it shows the sudoku in another viewController that is pushed on to the navigation controller.
In my - (void)viewWillAppear... method I call my - (void)loadSudoku method which I will show you below.
The problem is when you look at one sudoku, go back to the table view using the "back" button in the navigationBar and then tap another sudoku. Then the old sudoku is still there, and the new one is drawn on top of the old one.
I guess I need to clear the old one somehow. Any ideas?
I do have a background image set through the interface builder of the actual sudoku grid. I don't want to remove this.
The method that draws the sudoku looks like this:
- (void)loadSudoku
{
mainLayer = [[self view] layer];
[mainLayer setRasterizationScale:[[UIScreen mainScreen] scale]];
int col=0;
int row=0;
for(NSNumber *nr in [[self sudoku] sudoku])
{
if([nr intValue] != 0)
{
//Print numbers on grid
CATextLayer *messageLayer = [CATextLayer layer];
[messageLayer setForegroundColor:[[UIColor blackColor] CGColor]];
[messageLayer setContentsScale:[[UIScreen mainScreen] scale]];
[messageLayer setFrame:CGRectMake(col*36+5, row*42, 30, 30)];
[messageLayer setString:(id)[nr stringValue]];
[mainLayer addSublayer:messageLayer];
}
if(col==8)
{
col=0; row++;
}else
{
col++;
}
}
[mainLayer setShouldRasterize:YES];
}
To remove only text layers, you can do this –
NSIndexSet *indexSet = [mainLayer.sublayers indexesOfObjectsPassingTest:^(id obj, NSUInteger idx, BOOL *stop){
return [obj isMemberOfClass:[CATextLayer class]];
}];
NSArray *textLayers = [mainLayer.sublayers objectsAtIndexes:indexSet];
for (CATextLayer *textLayer in textLayers) {
[textLayer removeFromSuperlayer];
}
In a nutshell, the first statement gets all the indices of text layers which are a sublayer to over root layer. Then in the second statement we get all those layers in a separate array and then we remove them from their superlayer which is our root layer.
Original Answer
Try doing this,
mainLayer.sublayers = nil;