Hide and add a Tab to a UITabbar - ios

I'm working on a Tabbed Application with one tab showing information about the current neighbourhood if the user is within this specific region. If the user leaves the area the tab should be removed, if he enters the region the tab should be added again to the TabBar.
I achieved to calculate if the user is inside or outside the area using CLLocation. But I fail to remove and add the Tab again afterwards:
UITabBarController.m:
- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation
{
currentposition = [[CLLocation alloc] initWithLatitude:newLocation.coordinate.latitude longitude:newLocation.coordinate.longitude];
[self userisincity];
}
- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error
{
[self userisincity];
}
- (void)userisincity
{
if ((currentposition.coordinate.longitude > 17.50) && (currentposition.coordinate.longitude < 17.70) && (currentposition.coordinate.latitude > 37.45) && (currentposition.coordinate.latitude < 37.65)){
NSLog(#"inside city");
//add tab
} else {
NSLog(#"outside city");
//remove tab
NSUInteger indexToRemove = 0;
NSMutableArray *controllersToKeep = [NSMutableArray arrayWithArray:self.viewControllers];
UIViewController *removedViewController = [controllersToKeep objectAtIndex:indexToRemove];
[controllersToKeep removeObjectAtIndex:indexToRemove];
NSLog(#"%#", controllersToKeep);
[self.tabBarController setViewControllers:controllersToKeep animated:YES];
}
}
The log shows if the user is inside or outside so the location part works properly. controllersToKeep has 4 entries first and one gets removed. But setViewControllers has no effect.
How can I add the Tab afterwards again? It's a ViewController done and linked using Storyboards right now.

Change the last line of code from:
[self.tabBarController setViewControllers:controllersToKeep animated:YES];
to:
[self setViewControllers:controllersToKeep animated:YES];
since self is the UITabBarController.

this solution works so far:
i also had to update the name of the tabs after adding/removing one.
if ((currentposition.coordinate.longitude > XY) && (currentposition.coordinate.longitude < XY) && (currentposition.coordinate.latitude > XY) && (currentposition.coordinate.latitude < XY)){
//add view
NSMutableArray *ViewControllers = [NSMutableArray arrayWithArray:self.viewControllers];
if (ViewControllers.count == 3) {
UINavigationController *nextomeNavigationController = [[UIStoryboard storyboardWithName:#"MainStoryboard" bundle:nil] instantiateViewControllerWithIdentifier:#"ViewControllerID"];
//set the name of your Storyboard containing the ViewController and the ID you gave to the ViewController here
[ViewControllers insertObject:nextomeNavigationController atIndex:0];
[self setViewControllers:ViewControllers animated:YES];
}
} else {
//remove view
NSMutableArray *ViewControllers = [NSMutableArray arrayWithArray:self.viewControllers];
if (ViewControllers.count == 4) {
[ViewControllers removeObjectAtIndex:0];
[self setViewControllers:ViewControllers animated:YES];
}
}

Related

SWRevealViewController Different Content based on Current View

I'm using SWRevealViewController to get the side menu to show different content based on which tab the user is currently on. I've created a global variable in called location in AppDelegate to find out where the user is. For my sidebar, I'm using this condition to change the content:
- (void)viewWillAppear:(BOOL)animated {
[super viewDidAppear:animated];
AppDelegate *del=(AppDelegate*)[[UIApplication sharedApplication] delegate];
if ([del.location isEqualToString: #"Secure"]){
self.menu = [NSMutableArray
arrayWithObjects:#"secure1",#"secure2",#"secure3",#"secure4",#"secure5", nil];
self.menu2 = [NSMutableArray
arrayWithObjects:#"s1",#"s2",#"s3",nil];
NSLog(#"THIS IS %# menu",del.location);
}
else if ([del.location isEqualToString: #"Employee"]){
self.menu = [NSMutableArray
arrayWithObjects:#"emp1",#"emp2",#"emp3",#"emp4",#"emp5",#"emp6", nil];
self.menu2 = [NSMutableArray
arrayWithObjects:#"e1",#"e2",#"e3",nil];
NSLog(#"THIS IS %# menu",del.location);
}
else if ([del.location isEqualToString: #"Patient"]){
self.menu = [NSMutableArray
arrayWithObjects:#"patient1",#"patient2",#"patient3",#"patient4",#"patient5",#"patient6", nil];
self.menu2 = [NSMutableArray
arrayWithObjects:#"p1",nil];
NSLog(#"THIS IS %# menu",del.location);
}
}
It seems to be working as the NSLog statements all come out correctly, but the content for menu and menu2 still do not change. Where am I going wrong?
I wasn't adding the
[self.tableView reload];
after closing the if-statement, and instead added it under cellForRowAtIndexPath (which lead the cells to constantly reload, but not the table -- if my understanding is correct.)
Added it in the viewWillAppear after closing my if-statement and problem solved.
Thanks to Anbu.Karthik for pointing it out!

How can I alternate view controller and not lose data

I have two UIViewControllers, one is a UIPickerViewController, the Other a UITableViewController. Ideally the Picker should get a request from the user to add x amount of some item to the tableView. The Picker gets user inputs and assigns them to variables val1, val2, val3, where val1 is the number of items (number of rows) and val2 is the name or label for the item.
PickerViewController.m
- (IBAction)add:(id)sender
{
TableViewController *tvc = [[TableViewController alloc] init];
[tvc setValues:self.val1 :self.val2 :self.val3];
[self presentViewController:tvc animated:YES completion:nil];
}
TableViewController.m
-(void)setValues:(NSString *)newVal1 :(NSString *)newVal2 :(NSString *)newVal3
{
self.val1 = newVal1;
self.val2 = newVal2;
self.val3 = newVal3;
}
- (void)viewDidLoad
{
[super viewDidLoad];
[self.tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:#"UITableViewCell"];
// This is just a header which holds my "Add" button
UIView *header = self.headerView;
[self.tableView setTableHeaderView:header];
[self addNew:self.val1 :self.val2 :self.val3];
}
- (void)addNew:(NSString *)newVal1 :(NSString *)newVal2 :(NSString *)newVal3
{
if(!self.numberOfRows){
NSLog(#"Initially no of rows = %d", self.numberOfRows);
self.numberOfRows = [self.val1 intValue];
NSLog(#"Then no of rows = %d", self.numberOfRows);
}
else
{
self.numberOfRows = self.numberOfRows + [newVal1 intValue];
NSLog(#"New no rows = %d", self.numberOfRows);
}
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:self.numberOfRows inSection:0];
// Only run when called again .. not initially
if(self.run != 0){
[self.tableView beginUpdates];
[self.tableView insertRowsAtIndexPaths:#[indexPath]withRowAnimation:UITableViewRowAnimationBottom];
self.run ++;
[self.tableView endUpdates];
}
}
// "ADD" button which should go back to the picker and get new items to add to the table
- (IBAction)testAdd:(id)sender
{
PickerViewController *pvc = [[PickerViewController alloc] init];
[self presentViewController:pvc animated:YES completion:nil];
}
Now, I realize every time I call the next view controller I am creating a new instance of it, but I don't know how else to do it, I figure this is the main problem. As of right now, I expect when I leave the tableview for the picker view and return the console should log "New no of rows = x" but that doesn't happen.
I know val3 isn't used and my addNew: may not be the best, but I just need it to handle the basic logging mentioned above and I should be able to take it from there.
Been stuck on this for days
Create a property for TableViewController, and only create it the first time you present it,
- (IBAction)add:(id)sender {
if (! self.tvc) {
self.tvc = [[TableViewController alloc] init];
}
[self.tvc setValues:self.val1 :self.val2 :self.val3];
[self presentViewController:self.tvc animated:YES completion:nil];
}
It's not entirely clear from you question, whether it's this presentation or the one you have in the table view class that you're talking about. It also looks like you're doing something wrong in terms of presentation -- you're presenting the picker view from the table view controller, and also presenting the table view controller from the picker. That's not correct, you should present which ever controller you want to appear second, and that controller should use dismissViewControllerAnimated to go back, not present another controller.
In testAdd you don't need to create a new instance and present it. If you want to go back to the presentingViewController, just use dismissViewControllerAnimated .
And you will go one controller up in the stack.

IOS login required for certain tabs with storyboard

My question is based on the following question, but my case is reversed.
Example for login screen modally based on storyboard
I would like to have a few tabs private for users only so required login, my storyboard is below:
Below is my code in custom tabBarController
#interface PNTabBarController ()
#end
#implementation PNTabBarController
- (void)tabBar:(UITabBar *)tabBar didSelectItem:(UITabBarItem *)item
{
NSUInteger indexOfTab = [tabBar.items indexOfObject:item];
NSLog(#"%lu", (unsigned long)indexOfTab);
if (indexOfTab == 2 || indexOfTab == 3) {
PFUser *currentUser = [PFUser currentUser];
NSLog(#"%#", currentUser.username);
if(currentUser == nil){
PNLoginViewController *obj = [[PNLoginViewController alloc]init];
[self presentViewController:obj animated:YES completion:NULL];
}
else{
NSLog(#"%#", currentUser.username);
}
}
}
But what happens is a dark screen when I click the 2 or 3 tab. What should be the correct way to achieve this?

iOS navigation issue: after pushing a ViewController, it results in a navigation bar showing the navigation items of the previous ViewController

This situation happens very rarely and I don't know how to reproduce it, but it does happen sometimes and I would like to fix it.
In ViewController A, if I push ViewController Bin, sometimes(not always) when ViewController B did appear, the navigation bar is showing the navigation bar items of ViewController A, not those of ViewController B. If I click on the back button, I cannot go back to ViewController A, getting stuck in ViewController B.
A UIBarButtonItem is added to the navigation items of ViewController A, and the navigation items of ViewController A will be updated in response to some events. Is it the reason that causes this issue?
Codes for pushing ViewController B
ViewControllerB* viewControllerB = [ViewControllerB new];
[self.navigationController pushViewController:viewControllerB animated:YES];
Codes for updating the navigation items in ViewController A
- (void)viewDidAppear:(BOOL)animated{
[super viewDidAppear:animated];
if(NumberOfCalendarOperations == 0){
[self showNavigatorBarButtons];
}
else{
[self startRefreshAnimationOnUpdateButton];
}
}
//This method is triggered through notification when the number of operations in calendarOperationQueue is changed
-(void)calendarOperationQueueStateDidChange:(NSNotification*)notification{
if(NumberOfCalendarOperations == 0){
if (self.isShowActivityIndicator) {
dispatch_async(dispatch_get_main_queue(), ^{
[self showNavigatorBarButtons];
});
}
}
else{
if (!self.isShowActivityIndicator) {
dispatch_async(dispatch_get_main_queue(), ^{
[self startRefreshAnimationOnUpdateButton];
});
}
}
}
/**
* Show the update button on the right of navigation bar
*/
-(void)showNavigatorBarButtons{
self.isShowActivityIndicator = NO;
UIBarButtonItem *updateButton = [[UIBarButtonItem alloc] initWithImage:[UIImage imageNamed:#"sync.png"] style:UIBarButtonItemStylePlain target:self action:#selector(updateButtonDidPress:)];
[self.navigationItem setRightBarButtonItems:[NSArray arrayWithObjects:updateButton, nil]];}
/**
* Start refresh animation on the update button
*/
-(void)startRefreshAnimationOnUpdateButton{
if (self.navigationController.topViewController != self) {
return;
}
self.isShowActivityIndicator = YES;
UIView* updateButtonView = nil;
for (UIView *subViewInNavigationBar in self.navigationController.navigationBar.subviews){
NSString *subViewClassAsString = NSStringFromClass([subViewInNavigationBar class]);
if ([subViewClassAsString compare:#"UINavigationButton" /* TODO: be careful with this */
options:NSCaseInsensitiveSearch] == NSOrderedSame){
if ([subViewInNavigationBar isKindOfClass:[UIView class]] == YES){
if(updateButtonView == nil){
updateButtonView = subViewInNavigationBar;
}
else if(subViewInNavigationBar.center.x < updateButtonView.center.x){
updateButtonView = subViewInNavigationBar;
}
}
}
}
for (UIView *subViewsInButton in updateButtonView.subviews){
if ([subViewsInButton isKindOfClass:[UIImageView class]] == YES &&
subViewsInButton.frame.origin.x != 0.0f &&
subViewsInButton.frame.origin.y != 0.0f){
[subViewsInButton removeFromSuperview];
CGRect activityIndicatorFrame = self.updateButtonActivityIndicator.frame;
activityIndicatorFrame.origin.x = (CGRectGetWidth(updateButtonView.frame) / 2.0f) - (CGRectGetWidth(activityIndicatorFrame) / 2.0f);
activityIndicatorFrame.origin.y = (CGRectGetHeight(updateButtonView.frame) / 2.0f) - (CGRectGetHeight(activityIndicatorFrame) / 2.0f);
self.updateButtonActivityIndicator.frame = activityIndicatorFrame;
[self.updateButtonActivityIndicator startAnimating];
[updateButtonView addSubview:self.updateButtonActivityIndicator];
return;
}
}
}
Anyone has got a clue? Thank you.
Finally I found the reason of this issue. It is that I try to hide the navigation bar in viewWillDisappear. Now iOS allows me to back to the previous view by panning from the left edge of the screen. But while panning from the left edge, if I cancel this action, and pan back to the left edge, the navigation bar will enter this strange state.

How to reset UISearch from Appdelegate method applicationWillEnterForeground

I want to reset my UISearch when app is entering background, or entering foreground again. It would be enough when the tableview from UISearch gets hidden.
But when I try to hide it from AppDelegate.m it doesn't work. I have also logged the UIElements, there are also (null).
Here is how I try to access it:
- (void)applicationWillEnterForeground:(UIApplication *)application
{
XLog(#"");
/*
Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
*/
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
XLog(#"");
searchViewController = [[SearchViewController alloc] initWithNibName:#"SearchViewController_iPad" bundle:[NSBundle mainBundle]];
} else {
XLog(#"");
searchViewController = [[SearchViewController alloc] initWithNibName:#"SearchViewController_iPhone" bundle:[NSBundle mainBundle]];
}
XLog(#"searchViewController.searchBar.text: %#", searchViewController.searchBar.text);
searchViewController.tableViewSearch.hidden = YES; // is (null)
XLog(#"searchViewController.tableViewSearch: %#", searchViewController.tableViewSearch); // is (null)
}
How can I access that? Do I something wrong here, or is it not allowed to access elements from other classes thru appdelegate.m?
NSArray *ary_navigationControllerViews = [[NSArray alloc] initWithArray:[self.navigationController viewControllers]];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"self.class.description == %#", [[SearchViewController class] description]];
NSArray *ary_viewController = [[NSArray alloc] initWithArray:[ary_navigationControllerViews filteredArrayUsingPredicate:predicate]];
if ([ary_viewController count] > 0)
{
SearchViewController *sVw = (SearchViewController*) [ary_viewController objectAtIndex:0] ;
sVw.tableViewSearch.hidden = YES;
}
You are initializing new instance of your view controller, instead you need to get existing instance of your view controller and hide your view. Hope this helps.

Resources