How to keep a class alive like in Tabbed apps - objective-c

I currently have an app like this that has the drawer interface but when I go into another view everything resets in the current view and never stays the same ?
This is how my code looks like when I switch views.
[self.slidingViewController anchorTopViewOffScreenTo:ECRight animations:nil onComplete:^{
CGRect frame = self.slidingViewController.topViewController.view.frame;
self.slidingViewController.topViewController = [self.storyboard instantiateViewControllerWithIdentifier:#"homeChannel"];
self.slidingViewController.topViewController.view.frame = frame;
[self.slidingViewController resetTopView];
[tableView deselectRowAtIndexPath:indexPath animated:YES]; }];
So my question is "How would I keep the view alive while going into another view such as the tabbed apps "
Such as my tableView, when I go to another view and back to the original one it resets.

Create a NSMutableArray that will keep a pointer to your views. Save the view when you leave it, fetch it when you need it back. Beware! This will consume a lot of memory, but that's what you asked for.
- (void)initArray
{
myViews = [[NSMutableArray alloc] init];
for (int i=0; i<NUMBER_OF_VIEWS; i++)
{
[myViews addObject:[NSNull null]];
}
}
- (void)saveView:(UIView*)view atIndex:(int)index
{
myViews[index] = view;
}
- (UIView*)fetchViewAtIndex:(int)index
{
UIView *view = myViews[index];
if (view == [NSNull null])
return nil;
return view;
}

Related

UInavigationController Custom transition is not getting called

I'm trying to implement MAOFlipViewController in one of my application. Everything is working properly, but as per my requirement I want to move back to 0 viewcontroller whenever user come to that particular view.
For moving back to 0 view I added following code
NSUInteger targetIndex = self.flipNavigationController.viewControllers.count;
for (int i=(int)targetIndex; i>=0; i--) {
[self.flipNavigationController popViewControllerAnimated:YES];
}
It's working fine. But it is not showing the animation.
How can I use the animation that is defined with UIPanGestureRecognizer to my defauly pop navigation.
Any help will be appreciated. Thanks in advance.
I'm updating my answer, so it can be useful to someone else too.
I used another custom library for flipping the view Flip View. So to root view controller will work like :
-(void)popToRoot{
NSArray *viewControllers=[[self flipNavigationController] viewControllers];
NSUInteger cnt = viewControllers.count;
if(cnt >= 2){
UIViewController *lastVC = [viewControllers objectAtIndex:cnt-1];
UIViewController *secondVC = [viewControllers objectAtIndex:cnt-2];
[lastVC.view flipToView:secondVC.view duration:0.2 removeView:YES direction:JDFlipImageViewFlipDirectionDown completion:^(BOOL finished) {
[self.flipNavigationController popViewControllerAnimated:NO];
[self popToFirst];
}];
}
}

Stacking multiple UIViewControllers and presenting the last one in UINavigationController stack

Possibly simple request here but I can't find the solution and it is bugging me for days.
I'm building simple options page where users could jump to desired page and I'm using UINavigationController instance to manage hierarchy. My storyboard looks like this:
Viewcontrollers are connected with push segues fired on next button, while I use [self.navigationController popViewControllerAnimated:YES] for previous button. If I connect, for instance, button labeled 2 on 5VC with 2VC through push segue, I get to the second page, but if I want to use previous button I will land to options page or 5VC which is something I don't want. Instead, I would like to be able to use previous button to go to first page, while on second page.
The way I see it, if I am on third page (3VC) and I call options page (5VC) and select button 3, system should stack 1VC-2VC and present 3VC, so I would be able to go to 2VC through [self.navigationController popViewControllerAnimated:YES] request.
I think the solution is somehow connected with setViewControllers:(NSArray *)viewControllers animated:(BOOL)animated, but I don't know the syntax how to make things work.
You have 3 cases
Back to one of ancestors in the middle with push
case 5VC=>2VC, 5VC=>3VC:
NSArray *vcs = self.navigationController.viewControllers;
for(NSInteger i = vcs.count - 2; i > 0; i--) {
// find the target and its parent view controller
// i.e. class of 2VC is ViewController2
if([vcs[i] isKindOfClass:[ViewController2 class]]) {
UIViewController *target = vcs[i];
UIViewController *parent = vcs[i - 1];
// pop to its parent view controller with NO animation
[self.navigationController popToViewController:parent animated:NO];
// push the target from its parent
[self.navigationController pushViewController:target animated:YES];
return;
}
}
Back to the root view controller with push
case 5VC=>1VC:
UIViewController *root = self.navigationController.viewControllers.firstObject;
// reset view controllers stack with self as root.
[self.navigationController setViewControllers:#[self] animated:NO];
// push target from self
[self.navigationController pushViewController:root animated:YES];
// reset navigation stack with target as root.
[self.navigationController setViewControllers:#[root] animated:NO];
Push new VC from one of ancestors
case 5VC=>4VC
NSArray *vcs = self.navigationController.viewControllers;
for(NSInteger i = vcs.count - 1; i >= 0; i--) {
// find the parent view controller
if([vcs[i] isKindOfClass:[ViewController3 class]]) {
UIViewController *parent = vcs[i];
// pop to the parent with NO animation
[self.navigationController popToViewController:parent animated:NO];
// perform segue from the parent
[parent performSegueWithIdentifier:#"push4VC" sender:self];
return;
}
}
On the particular your case(5VC=>4VC), you know 3VC is the self's parent, you can get the parent directly:
NSArray *vcs = self.navigationController.viewControllers;
UIViewController *parent = vcs[vcs.count - 2]; // [vcs.count-1] is self.
[self.navigationController popToViewController:parent animated:NO];
[parent performSegueWithIdentifier:#"push4VC" sender:self];

objective c - resident and dirty memory not getting released after dismiss of collection view controller

I am loading a 2X2 collection view which has multiple cells and each cell has imageview.But when I am dismissing this controller the resident memory and the dirty memory keeps on increasing.
I have multiple views in my controller and collection view is one of them and I do addsubview and removefromsuperview to show different views in the controller. Before I dismiss the controller I remove all the subviews including the collectionview from the controller's subviews.
But this does not release the resident memory and after it exceeds 500MB the app crashes after throwing a memory warning.
Here is my code to remove the subviews before dismissviewcontroller -
-(void)removeSubViewsOfView:(UIView *)view{
NSArray *viewsToRemove = [view subviews];
for (int i = 0; i<[viewsToRemove count]; i++) {
UIView *v = [viewsToRemove objectAtIndex:i];
[v removeFromSuperview];
v=nil;
}
}
Also I am using ARC.
Some suggestions:
Make sure you set all your IBOutlets to nil on your views -dealloc method
If you're using view controller containment, call -removeFromParentViewController:
- (void)removeSubViewsOfView:(UIView *)view{
NSArray *viewsToRemove = [view subviews];
for (int i = 0; i<[viewsToRemove count]; i++) {
UIView *v = [viewsToRemove objectAtIndex:i];
[v removeFromSuperview];
NSViewController* vController = nil;//retrieve your view controller
[vController removeFromParentViewController];
v=nil;
}
}
It won't generate dirty memory, but it may leak objects if you keep mutual strong references between objects, aka. A retains B and B retains A
Hope it helps :)

ipad objective c using removeFromSuperview to remove UICollectionViewController throws an error

So I'm customizing this control I found since I think it works very well except with this issue of mine:
http://www.cocoacontrols.com/controls/fsverticaltabbarcontroller
I wanted to load a UICOllectionViewCOntroller instead of a regular ViewController whenever an item is tapped on the sidebar. So I did this modification when selecting an item:
- (void)setSelectedIndex:(NSUInteger)selectedIndex
{
NSLog(#"selected Index is %#", [NSNumber numberWithInt:selectedIndex]);
NSLog(#"_selected Index is %#", [NSNumber numberWithInt:_selectedIndex]);
NSLog(#"vc counts is %i", [self.viewControllers count]);
if (selectedIndex != _selectedIndex && selectedIndex < [self.viewControllers count])
{
// add new view controller to hierarchy
UIViewController *selectedViewController = [self getSelectedVCWithSelectedIndex:selectedIndex];
[self addChildViewController:selectedViewController];
selectedViewController.view.frame = CGRectMake(self.tabBarWidth,
0,
self.view.bounds.size.width-self.tabBarWidth,
self.view.bounds.size.height);
selectedViewController.view.autoresizingMask = UIViewAutoresizingFlexibleHeight|UIViewAutoresizingFlexibleWidth;
[self.view addSubview:selectedViewController.view];
// remove previously selected view controller (if any)
if (_selectedIndex != NSNotFound)
{
UIViewController *previousViewController = [self.viewControllers objectAtIndex:_selectedIndex];
NSLog(#"ERROR HERE: remove previous: previousVC = %#", previousViewController);
[previousViewController.view removeFromSuperview];
[previousViewController removeFromParentViewController];
}
// set new selected index
_selectedIndex = selectedIndex;
// update tab bar
if (selectedIndex < [self.tabBar.items count])
{
self.tabBar.selectedItem = [self.tabBar.items objectAtIndex:selectedIndex];
}
// inform delegate
if ([self.delegate respondsToSelector:#selector(tabBarController:didSelectViewController:)])
{
[self.delegate tabBarController:self didSelectViewController:selectedViewController];
}
}
}
So what I did is since it already handles the index number of the items on teh sidebar, I just made sure it instantiates the type of controller it needs to load using this line, I have 3 regular VC and 1 collection VC:
UIViewController *previousViewController = [self.viewControllers objectAtIndex:_selectedIndex];
This is how it looks like:
-(UIViewController *)getSelectedVCWithSelectedIndex:(NSUInteger)selectedIndex{
UIViewController *selectedVC = [[UIViewController alloc]init];
// do a switch case on this.
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"MainStoryboard" bundle:nil];
// need to instantiate each and every custom uinav
switch(selectedIndex){
case 1:
selectedVC = [storyboard instantiateViewControllerWithIdentifier:#"UINavAdminCategoryIndexViewControllerID"];
break;
case 2:
selectedVC = [storyboard instantiateViewControllerWithIdentifier:#"UINavAdminParticipantIndexViewControllerID"];
break;
case 3:
selectedVC = [storyboard instantiateViewControllerWithIdentifier:#"UINavAdminTablesIndexCollectionViewControllerID"];
break;
case 4:
selectedVC = [self.viewControllers objectAtIndex:selectedIndex];
break;
default:
break;
}
return selectedVC;
}
Now everything would load smoothly, but whenever I would go to the Collection VC tab and then move away from it by going to another tab it would throw this error:
* Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'UICollectionView must be
initialized with a non-nil layout parameter'
The application bombs on this part when I remove it from the superview:
[previousViewController.view removeFromSuperview];
Was wondering why it would instantiate the UIView again when all I'm doing is removing it from the stack (is that the right term?)
EDIT: Added some more codes
So I finally figured it out and hopefully someone else can find this useful. When generating a UiCollectionView you need to initiate the Layout for some reason. Idk why, I will try to find out. But this is what led me to the solution:
http://www.rqna.net/qna/ikvmhu-uicollectionview-must-be-initialized-with-a-non-nil-layout-parameter.html
Before when I instantiated the CollectionViewController on the main ViewController before the FSVerticalTabbar is called I just used the class connected to the ViewController on the storyboard eg. AdminMainController, AdminEventsCollectionController, etc.
I basically just added the Layout and used that for the UICollectionViewController when being initiated. It now removes the VC without any errors.
UICollectionViewFlowLayout *aFlowLayout = [[UICollectionViewFlowLayout alloc] init];
[aFlowLayout setItemSize:CGSizeMake(200, 140)];
[aFlowLayout setScrollDirection:UICollectionViewScrollDirectionHorizontal];
myCollectionViewController = [[MyCollectionViewController alloc]initWithCollectionViewLayout:flowLayout];

Setting Bool in different classes

I have the following code where after a bool is true I want to add a drawing to my rect. here is the code I have but for some reason it is either not setting the bool or calling the setNeedsDisplay. Am I referencing to the other class properly? thanks
//in AppController.m
-(IBAction)colorToggle:(id)sender
{
if ([colorFilter state] == NSOnState)
{
CutoutView *theView = [[CutoutView alloc] init];
[theView setFilterEnabled:YES];
}
}
//in cutoutView.m
- (void)drawRect:(NSRect)dirtyRect
{
[[[NSColor blackColor]colorWithAlphaComponent:0.9]set];
NSRectFill(dirtyRect);
//this is what i want to be drawn when my bool is true and update the drawRect
if (filterEnabled == YES) {
NSRectFillUsingOperation(NSMakeRect(100, 100, 300, 300), NSCompositeClear);
[self update];
}
}
-(void)update
{
[self setNeedsDisplay:YES];
}
OK, you know how not every UILabel is the same? Like, you can remove one UILabel from a view without all the others disappearing too? Well, your CutoutView is the same way. When you write CutoutView *theView = [[CutoutView alloc] init]; there, that creates a new CutoutView that isn't displayed anywhere. You need to talk to your existing CutoutView (probably by hooking up an outlet, but there are any number of perfectly valid designs that will accomplish this goal).
You are forgetting to call the drawRect: method, it should looks like this:
CutoutView *theView = [[CutoutView alloc] init];
[theView setFilterEnabled:YES];
[theView setNeedsDisplay];
From the docs:
When the actual content of your view changes, it is your
responsibility to notify the system that your view needs to be
redrawn. You do this by calling your view’s setNeedsDisplay or
setNeedsDisplayInRect: method of the view.