I am adding many UIView subviews to my UIViewController
A user can swipe (left or right) the subview off the UIViewController.
I would like to keep track of the array count so that when it hits 0 and all the subviews are gone I can display something.
I create my subviews in a for loop on the `UIView`` controller and check the count there.
example:
for (int i = 0; i < objects.count; i++)
{
self.subView.counter = objects.count;
// Some More Code Here ...
[self.items addObject:self.subView];
[self.view addSubview:self.subView];
}
Here I can NSLog the counter and get the number of subViews that are in the Array.
In my subView class ...
If I do this in my completion block for the animation,
self.counter --;
The counter will decrease by 1, but only 1.
So when I swipe away the second card, the count does not change.
What is the best way to keep track of an array count and see it decreasing each time a subView is moved out of the UIViewController?
Update:
I did what was suggested and now I'm getting a new issue. Here's a break down of what's going on:
As my subviews are being added I see the counter go up, for example - 6 items are added (0 - 5).
When subviews are removed the counter hits -1 at the end. (Since 5 is the current subview, removing it decrements the counter to 4, etc.)
I check to see if the counter is < 0, set a UIButton hidden to NO, but the button does not show.
Any thoughts?
Keep track of the count on the UIViewController that contains all of the subviews, rather than on the subviews themselves. Really, each subview shouldn't know about the other subviews... all it should know about is its parent view, the containing UIView.
Make the UIViewController a delegate to a protocol on the subviews. When the subviews disappear, have them call a delegate method on the UIViewController to decrement the count. And in that delegate method, put logic to handle self.count == 0.
Related
I use custom cells with buttons. On editing mode I need buttons to disappear.
I call this method when editing status changes:
-(void)setButtonsVisibility:(BOOL)visibility {
UIButton *currButton;
for (int i = 0; i <= [array count] - 1; i++) {
currButton = (UIButton *)[_tableView viewWithTag:10000+i];
if (currButton) {
[currButton setAlpha:visibility];
}
}
}
It works great. But this method don't work for cells that not visible now. For example, when there are more than 10 cells in table.
If you're reusing the cells properly, then the only UITableViewCells that exist in memory will be the 10 that are visible.
Easiest solution would be to just add a BOOL flag to the UITableViewDataSource instance that indicates whether to show or hide the buttons. Then the ~10 that are visible will be updated properly by your existing method, and in tableView:cellForRowAtIndexPath:, which will be called after you scroll, you can set the alpha property on the button to the flag's value.
I'm working on an app that has a Main view that wants to spawn a child view when a button is touched. So when I receive the button event, the MainViewController spawns the child view by calling initWithNibName and storing the ChildViewController in an ivar. I then show the ChildView by attaching an animation and setting childVC.view.hidden = NO.
This works, but I noticed that the ChildViewController was never getting released after closing the ChildView. I realized that the ChildVC's retain count went from 1 to 2 when I first access the child view. So something in the nib loading guts appears to be retaining my ChildVC again (in addition to the initial retain I expect during object initialization).
Can somebody help me figure out why the ChildVC is getting retained the extra time, and how can I make sure that it gets fully released when I want to close the child view?
Edit: here's some code, only slightly simplified. These are methods on the parent view controller.
-(IBAction)onLaunchChildButtonTouched:(id)sender
{
m_childViewController = [[ChildViewController alloc] initWithNibName:#"ChildViewController" bundle:nil];
[m_childViewController setParentDelegate:self]; // this is a weak reference
// m_childViewController retain count here is 1, as expected
m_childViewController.view.hidden = YES;
// m_childViewController retain count is now 2, not expected
[self.view addSubview:m_childViewController.view];
[self addTransitionEntrDir:YES]; // code omitted
m_childViewController.view.hidden = NO;
}
-(void)onChildWantsToClose:(id)child
{
NSAssert( child == m_childViewController, #"unexpected childVC" );
// if child view is now hidden, we should remove it.
if( m_childViewController != nil && m_childViewController.view.hidden )
{
[m_childViewController.view removeFromSuperview];
[m_childViewController release]; m_childViewController = nil;
// BUG: m_childViewController retain count is still 1 here, so it never gets released
}
}
Without code it is difficult to say exactly, but are you sure you are not assigning your ChildVC to a retain property of some other object? This would explain the unexpected retain you see.
Sorry for the previous answer, where I tried to convey this same message but I mixed everything up.
OLD ANSWER:
keep in mind that the view property of a UIViewController is retained:
view
The view that the controller manages.
#property(nonatomic, retain) UIView *view
so, if you assign to it like this:
childVC.view = [[xxxxx alloc] initWithNibName:...];
this explains what you are seeing.
Use instead:
childVC.view = [[[xxxxx alloc] initWithNibName:...] autorelease];
I found the problem, the leaky ChildViewController was instantiating an object that retained a ref back to it.
The interesting part is that I wasn't simply forgetting to release this reference. I did have a call to release it, but that code was never running because it assumed that viewDidUnload would run and give me a chance to release everything, but it didn't. I put me deinit code inside dealloc instead, and it works now.
In my app i had to draw certain checkboxes at a same time and i used a single function to add all of them. Now when a user clicks one of them all of those checkboxes should get removed from the superview and currently its just removing the last one. Also i have issue to recognize those checkboxes like which one is clicked. i know it should be done through Tag property but don't know how exactly it should be implemented.
Any suggestions.
Removing all subviews
int numberOfSubviews = [[yourView subviews] count];
for(int i=0;i<numberOfSubviews-1;i++
{
[[youView subviews]objectAtIndex:i]removeFromSuperView];
}
//this will leave check box that you added at last.... for first one to remain loop from 1 to numberOfSubviews....
Using tag property...
when you are creating checkbox objects use
checkBoxObject.tag = i;
//I am considering i as looop count which you are using in a loop
to add checkboxes.
then whenever you need a object of checkbox
[yourViewonwhichYouAddedCheckBox viewWithTag:<your tag >];
Thanks
For identifying a "checkbox" or better said any view within an action-method:
- (void)someActionHandler:(id)sender
{
UIView *actionOriginView = (UIView *)sender;
NSLog(#"this action came from view:%d", actionOriginView.tag);
}
For assigning the tag, you may use the IB or within your code, while instantiating;
UIView *myFunkyView = [[UIView alloc] initWithFrame:CGRectZero];
myFunkyView.tag = 1337;
For removing a bunch of views from your superview - lets assume their tag is set to 10 - 15;
for (int i=10;i <= 15;i++)
{
UIView *childView = [superview viewWithTag:i];
[childView removeFromSuperview];
}
I have 5 different views and when I tap the button, I want to push one of 5 views randomly. However, I do not want to make 5 different controllers for each views. Do I have a chance to put them in one controller? If so, how?
You can have as many views as you want in a single UIViewController subclass. You can create them all in Interface Builder as well, in the one .xib file for your UIViewController class (it might get a bit hard to see, though, better to lay out each UIView in its own .xib). You can present them in any combination you want. Assuming you want to just show one view at a time, you can do this:
In your viewDidLoad method, start out showing the initial view, and in your class keep track of which is the current view:
...
#property(nonatomic, retain) UIView *currentView;
...
- (void)viewDidLoad
{
[self.view addSubview:self.defaultView];
self.currentView = self.defaultView;
}
Then to switch to a particular other view do this:
- (void) switchToView:(UIView *)newView
{
[self.currentView removeFromSuperView];
[self.view addSubview:newView];
self.currentView = newView;
}
}
Or you can show them all at one time: just [self.view addSubview:theView];
You have to set the tags of all the views, and then use
int r = arc4random() % 5;
method to find the random number. Use this newly generated number to check the tag, in your selector
Just change the view outlet of the controller on the action of the button. Use random numbers to select which view. Ouh and - (void)setNeedsDisplay :)
I'm running into a retain count issue that I do not understand. (I've added what I believe to be the retain count of vertex in [] at the end of each line of code).
CBVertex *vertex = nil;
for(int i=0; i<10; i++) {
vertex = [[CBVertex alloc] initWithFrame:CGRectMake(minX, y, 10.0, 10.0)]; // retain count [1]
[vertex setTag:i];
[vertex setAnimationDelegate:self];
[gameboard addSubview:vertex]; // retain count [2]
[tripGraph addVertex:vertex]; // retain count [3]
[vertex release]; vertex=nil; // retain count [2]
}
CBVertex is a subclass of UIView, gameboard is a UIView and tripGraph is a class that, among other things, has an NSMutableArray (privateVerticies) to which vertex is added to in its addVertex method.
After the above is executed, Instruments shows that there are 10 instances of CBVertex living.
Later in the code execution (I've confirmed that this code executes):
[[tripGraph verticies] makeObjectsPerformSelector:#selector(removeFromSuperview)];
// gameboard should have no references to any of the CBVertex's (correct??)
[tripGraph removeAllVerticies];
// tripGraph privateVerticies is empty and no references to any of
// the CBVertex's (correct?)
Relevant tripGraph methods:
-(NSArray *) verticies {
return [NSArray arrayWithArray:privateVerticies];
}
-(void) tripGraph removeAllVerticies {
[privateVerticies removeAllObjects];
}
- (void) addVertex:(CBVertex *)vertex {
[privateVerticies addObject:vertex];
}
The issue arises when the second set of CBVertex's are created. Instruments shows that the first set of CBVertex's is still live (i.e. the number of instances of CBVertexs is now 20).
I'm (obviously?) missing a release somewhere, but don't understand where . . .
Help/pointers are appreciated!!
thanks
tom
If gameboard and tripGraph are still retaining that CBVertex object, then even if you release it in your loop, it will continue to exist until you remove the CBVertex object from gameboard and tripGraph as well.
OK, I'm closing this out as the code above is correct. The problem lies in my overriden removeFromSuperview method in which there is an animation going on. I'm going to investigate further and if I don't have it figured out, I'll repost a new question. (and link to it from here).
Thanks for the comments, answer and several views.
For those interested, here's what was going on and how it was resolved.
In CBVertex, I've overridden removeFromSuperview. In that overridden method, I am animating the view's layer and setting the view as the CAAnimations delegate (which is retained by the CAAnimation) and calling super removeFromSuperview. The animation is not removed on completion.
As the animation retains the delegate and the animation is not removed, the view's retain count remains at +1.
My resolution was to create an intermediate method to perform the animation. When the animation is complete it calls the overriden removeFromSuperview, which now only removes all animations and calls super. Removing the animation releases it, which in turn releases it's reference to it's delegate (the CBVertex) and the CBVertex's retain count goes to +0.
One final thought for anyone chasing retain counts: don't think of them as absolute values. The inner workings of the objects you may be using might be retaining your instances more than you'd expect -- think of retain counts as a delta.
SomeClass *myObject = [[SomeClass alloc] init]; // retain count +1
[someMutableSet addObject:myObject]; // retain count +2