Communicating between 2 ViewControllers in Tab Bar Controller Project - ios

I'm using a Tab Bar Controller in my project where the FirstViewController has a Mapbox map view and the SecondViewController has buttons that when pressed add a tile layer to the map view. Here's what I've tried. It creates error ***Use of undeclared identifier '_mapView' in the SecondViewController.m
//FirstViewController.h
#import <UIKit/UIKit.h>
#import <MapBox/MapBox.h>
#import "SecondViewController.h"
#import "SecondViewController.m"
#interface FirstViewController : UIViewController
#property (strong, nonatomic) IBOutlet RMMapView *mapView;
#end
//SecondViewController.h
#import <UIKit/UIKit.h>
#import <MapBox/MapBox.h>
#import "FirstViewController.h"
#import "FirstViewController.m"
#interface SecondViewController : UIViewController
- (IBAction)stationingLayerButton:(id)sender;
#end
//SecondViewController.m
- (IBAction)stationingLayerButton:(id)sender {
RMMBTilesSource *stationingMap = [[RMMBTilesSource alloc] initWithTileSetURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:#"Stationing20" ofType:#"mbtiles"]]];
[_mapView addTileSource:stationingMap atIndex:2];
}
}
The map calls are correct because I've tested it on a project that only uses one view controller. Now that I'm trying it on a Tab Bar Controller I get this error.
My question is
1.how do I get mapView in the FirstViewController to respond to calls in the SecondViewController?
2.Can this be done? I've imported the class files thinking this would open communication between the two but I'm stuck with this error.

Using tabbar controller you can get array on all associated view controllers.
You can find more details here: UITabbarController - viewControllers property
For example :
In a tabbar, if we have two view controllers, let say VC1 and VC2, then we can have any of these reference by using below code snippet.
Accessing VC1 reference in VC2 class implementation (VC2.m):
VC1 *myVC1ref = (VC1 *)[self.tabBarController.viewControllers objectAtIndex:0];
Now we can use public properties and methods of VC1 class and it will give the same reference which tabbar has loaded.
Hope this helps.

You basically add view controller to UITabBarController. So, if you need to access a UIViewControler in a specific tab, you need to query the UITabBarController. The following answered SO question might help you
any code example of how access viewcontroller from uitabbarcontroller?
Once you get hold of the view controller, you can pass all the data you want.

Thanks to Mrunal and Naz Mir.
I added a UITabBarController Class file and assigned it to my TabBarController. I then NSLog the array description of view controllers of the TabBarController.
//TabBarController.h
#property (strong, nonatomic) NSArray *array;
//TabBarController.m
- (void)viewDidLoad
{
NSArray *array = self.viewControllers;
NSLog(#"View Controllers = %#", [array description]);
}
I then imported the FirstViewController.h to SecondViewController.h and in SecondViewController.m I wrote...
//SecondViewController.m
- (IBAction)stationingLayerButton:(id)sender {
FirstViewController *FVC = [self.tabBarController.viewControllers objectAtIndex:0];
[[FVC mapView] addTileSource:stationingMap atIndex:2];
}

Related

.delegate self unrecognized selector sent to instance

I've a TableViewController in which I'm saving the selected cells in an NSMUtableArray. After selecting these cells user clicks on a confirm button and in this button action I'm trying to pass that NSMUtableArray so that I can display it in another viewController tableView
#import <UIKit/UIKit.h>
#protocol SelectedDXDelegate;
#interface AddDXTableViewController : UITableViewController
#property (nonatomic, strong) NSMutableArray *favDXArray;
#property (strong, nonatomic) UISearchController *searchController;
#property (nonatomic, strong) DX *AddEditDX;
#property (weak) id<SelectedDXDelegate> delegate;
- (IBAction)confirmPressed:(id)sender;
#end
#protocol SelectedDXDelegate <NSObject>
#required
-(void)getSelectedDX:(NSMutableArray *)DXselected;
#end
So when confirm button is pressed
- (IBAction)confirmPressed:(id)sender {
[self.delegate getSelectedDX:selectedDX];
[self.navigationController popViewControllerAnimated:YES];
}
it gets me to the
-(void)getSelectedDX:(NSMutableArray *)DXselected
{
myDXSelected = DXselected;
}
But it crashes the app here at reloadData in
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
[self.DXTableView reloadData];
}
You seem to have mixed up which view controller should be the delegate and which is the delegator. Also, you are just allocating a new instance of the AddDXTableViewController and assigning this as the delegate. This won't work; you need to have the existing instance of your view controller set as the delegate.
From what I can tell from your question, it is actually an instance of DXViewController that is to be the delegate of AddDXTableViewController
Presumably in DXViewController you have some code something like:
AddDXViewController *newViewController = [self.storyboard instantiateViewControllerWithIdentifier:#"AddDXViewController"];
[self.navigationController pushViewController:newViewController animated:YES];
What you need to do is set your delegate at this point:
AddDXViewController *newViewController = [self.storyboard instantiateViewControllerWithIdentifier:#"AddDXViewController"];
newViewController.delegate = self;
[self.navigationController pushViewController:newViewController animated:YES];
Having said all of that, since you are using a storyboard, a delegate is probably an unnecessarily complicated way of achieving your requirement; You can use a segue to move between the first and second view controller and an unwind segue to return back to the first. You can then implement prepareForSegue in the second view controller and use that to provide the array back to the first view controller
You have to make the UIViewController added in storyboard to AddDXTableViewController type in the identity inspector tab in story board.
See here the image
Here you can see the class type is ViewController, click on the dropdown and select the type to AddDXTableViewController
then type cast the viewController to AddDXTableViewController. As per my guess the instantiateViewControllerWithIdentifier: returns UIViewController which does not have delegate may cause the crash
AddDXTableViewController *addDXTVC = (AddDXTableViewController *)[self.storyboard instantiateViewControllerWithIdentifier:#"DXViewController"];
addDXTVC.delegate = self;
Let me know If it works

How to pass the values from one view controller to previous view controller)

How do I pass a value from one view controller to its previous prant view controller?
Consider this case: I have two view controller. The first screen has one lable and a button and the second view controller has one EditText and a back button.
If I click the first button then it has to move to second view controller and here user has to type something in the text box. If he presses the button from the second screen then the values from the text box should move to the first view controller and that should be displayed in the first view controller.
in secondViewController create protocol - SecondViewController.h
#import <UIKit/UIKit.h>
#protocol MyDelegate
-(void)PassString : (NSString *)str;
#end
#interface SecondViewController : UIViewController
#property (nonatomic)id <MyDelegate> delegate;
-(IBAction)btnBack:(id)sender;
than after in SecondViewController.m file
-(IBAction)btnBack:(id)sender
{
[delegate PassString:txt.text];
[self.navigationController popViewControllerAnimated:YES];
}
than after in FirstViewController set protocol of SecondViewController
in ViewController.h file
#import <UIKit/UIKit.h>
#import "SecondViewController.h"
#interface ViewController : UIViewController<MyDelegate>
than after in ViewController.m file
-(void)PassString:(NSString *)str
{
lbl.text=str;
}
You need to use a delegate for passing data from secondViewController to firstViewController:
- (IBAction)backToViewController:(id)sender {
[self.delegate viewController:self sendMessageToFirstViewController:#"EditText"];
}
This is and example for delegate.
This may help :
yourFirstViewController *viewController=(yourFirstViewController *)[self presentingViewController];
viewController.lbl.text=#"yourValue";
If you are using navigation controller to navigate, then use this :
yourFirstViewController *viewController=[self.navigationController.viewControllers objectAtIndex:numberOfViewControllers - 2];
viewController.lbl.text=#"yourValue";
Basically you have to pass value from one view controller to another.Right ! You can use NSUserDefaults
Step 1. Save the text in an String
NSString *Text=#"Hello !"
[[NSUserDefaults standardUserDefaults] setObject:Text forKey:#"passingValue"];
[[NSUserDefaults standardUserDefaults]synchronize];
Step 2.
Now you have your textsaved in string "text".
Now lets pass this text into a view controller where you want.
NSString *savedtext =[[NSUserDefaults standardUserDefaults]
stringForKey:#"passingValue"];
Here you can display the text stored in "savedtext" into a textview or label.
All The Best
First you create one variable with property, ex.
#property (nonatomic, retain) NSString *strForUsername;
Then you
#import "SecondViewController".
Then you need to click event of the button.
Create Object of SecondViewController class, example
SecondViewController *mSecondViewController = [SecondViewController alloc] initWithNibName:#"SecondViewController" bundle:nil];
mSecondViewController.strForUsername = #"Hello I m from first view controller";
[self.navigationController pushViewController:mHomeViewController animated:YES];
You should use variable with value in SecondViewController.

How to show viewcontroller from the view?

I searched for answers like Get to UIViewController from UIView? and couple of other answers but was not successful.
My issue is that I have a button in UIView lets say class1 and when I click on that button I want to load another view class2 which is UIViewController, and as I don't get navigationController in class1 I am unable to load the class2 view.
Please help me with this.
Thanks,
In Advance.
In general UIViews should not contain any logic that triggers the flow of app. This is the job of UIViewControllers. It's just a way of making the design of your code better and more organized.
One way I often use is to use a delegate pattern in my custom UIViews. Here is s simple setup:
In your MyCustomView .h file:
#class MyCustomView;
#protocol MyCustomViewDelegate <NSObject>
#optional
- (void)myViewDidTapOnButton:(MyCustomView)myCustomView;
#end
#interface MyCustomView : UIView
#property (weak, nonatomic) id <MyCustomViewDelegate> delegate;
#end
In your MyCustomView .m file:
- (IBAction)didTapMyButton:(id)sender {
if ([self.delegate respondsToSelector:#selector(myViewDidTapOnButton:)]) {
[self.delegate myViewDidTapOnButton:self];
}
}
Then in your viewcontroller, which is presenting your view:
interface:
#interface MyViewController ()<MyCustomViewDelegate>
#property (weak, nonatomic) IBOutlet UIView *myCustomView;
and implementation:
- (void)viewDidLoad {
[super viewDidLoad];
self.myCustomView.delegate = self;
}
- (void)myViewDidTapOnButton:(MyCustomView)myCustomView {
... code for presenting viewcontroller ...
}
Note:
Even if you dont use the parameter myCustomView which is sent in this method, its a common pattern and good habit to always send the sender of the delegate as the first parameter.
This is also used a lot by Apple, e.g. in
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath;
Two cases :
If you are using storyboard then give your NavigationController a
storyboard id. And create an object of navigationController in your
custom UIView class.
If you have customized the app launching from AppDelegate create a
public property of your navigationController. From your UIView class create an object of appDelegate with [UIApplication sharedApplication].delegate. From this object access the navigationController property
When you have the navigationController object you can push your viewcontroller with:
[navigationController pushViewController:ViewController animated:YES];
First fill storyboard ID with "MyViewController", which is a String field that you can use to create a new ViewController based on that storyboard ViewController. And later access that view controller like this:
- (IBAction)buttonPressed:(id)sender{
MyCustomViewController *newvc = [self.storyboard instantiateViewControllerWithIdentifier:#"MyViewController"];
[self presentViewController:newvc animated:YES completion:nil];
}
When you click your button,you can do this:
YouViewController *yourViewController = [YouViewController new];
[self.view addSubView:yourViewController.view];
Hope to help you.

Pushing view controller is not working properly in ios

I am passing textfield data between two view controllers. i have two view controllers Firstviewcontroller, Secondviewcontroller. i have embed them in navigation but when i click the button on first view controller, second view controller is not showing only black screen is showing.below is my code.
First view controller.h
#import <UIKit/UIKit.h>
#import "secondViewController.h"
#interface FirstViewController : UIViewController<SecondViewControllerDelegate>
#property(nonatomic) secondViewController *secondview;
- (IBAction)btnclick:(id)sender;
#property (strong, nonatomic) IBOutlet UITextField *textfield1;
#end
First view controller.m
- (IBAction)btnclick:(id)sender {
secondViewController *SecondViewController= [[secondViewController alloc]init];
SecondViewController.data=self.textfield1.text;
[self.navigationController pushViewController:SecondViewController animated:YES];
}
- (void)dataFromController:(NSString *)data
{
[self.textfield1 setText:[NSString stringWithFormat:#"%#",data]];
}
Seconviewcontroller.h
#protocol SecondViewControllerDelegate <NSObject>
#required
- (void)dataFromController:(NSString *)data;
#end
#interface secondViewController : UIViewController
#property (nonatomic, retain) NSString *data;
#property(nonatomic,weak)id<SecondViewControllerDelegate> _delegate;
#property (strong, nonatomic) IBOutlet UITextField *textfield2;
#end
Seconviewcontroller.m
- (void)viewDidLoad {
[super viewDidLoad];
self.textfield2.text=[NSString stringWithFormat:#"%#",_data];
// Do any additional setup after loading the view.
}
whats the reason ??
You have to either use storyboard and let it intanciate the view controller for you:
[self.storyboard instantiateViewControllerWithIdentifier:#"SecondViewController"]
Or you can use xib files and tell the view controller which one to load:
[[SecondViewController alloc] initWithNibName:#"SecondViewController" bundle:nil]
second view controller is not showing only black screen is showing
Typically, this means that the view controller has no view. Your code does not explain where secondViewController is expected to get its view from. Is there a secondViewController.xib? If there is, is it correctly configured with a view outlet? If not, what does this view controller do in order to get its view?
Note that you have done a very odd thing with capitalization: you have given your class a small letter (secondViewController), while your instance has a capital latter (SecondViewController). This is backwards and a big mistake, and you should correct it immediately. Perhaps the problem is just a capitalization problem; if you call your class secondViewController and your xib file SecondViewController, that is not a match and they won't find each other.
I see "IBOutlet" in your code, it means you created the user interface of secondviewcontroller on storyboard. I guess [[secondViewController alloc]init] does not get UI element from storyboard then the black screen is shown. You have to create instance of secondViewController by loading from storyboard file.
Basically:
you can get storyboard via storyboard file name
on storyboard, Secondviewcontroller has an storyboard id which is set in Identity Inspector and you can use it to get instance of Secondviewcontroller
You can refer this link for more detail
loading iOS viewcontroller from storyboard
Hope this can help!

Setting variable in one view controller for another, and swapping to that view controller

Ok, so i have an interesting situation here:
I have a calendar View, this view does not have a navigation bar, so i created another View to contain the Calendar and added a navigation bar to that view.
So now i have 2 Views displaying a navigation bar and a calendar.
The navigation bars has a butten that is supposed to present a "Insert" controllers, but before it does that, it has to set a #property from the calendar to the "Insert" view controller.
So to sum it up:
Outer View Controller IBAction -> Inner Calendar Set Property on "Insert" -> Inner Calender Present "Insert".
Here is the code:
ViewControllerCalendarContainer.h
#import <UIKit/UIKit.h>
#interface ViewControllerCalendarContainer : UIViewController
- (IBAction)SeguqInsert:(id)sender;
#end
ViewControllerCalendarContainer.m
#import "ViewControllerCalendarContainer.h"
#import "CalendarMonthViewController.h"
...
- (IBAction)SeguqInsert:(id)sender {
CalendarMonthViewController *controller = [[CalendarMonthViewController alloc] initWithNibName:nil bundle:nil];
[controller SegueInsert];
}
CalendarMonthViewController.h
#property (nonatomic, strong) NSDate *dateSelected; // data to send to Insert View Controller
- (void)SegueInsert; // the present "Insert View Controller Method"
CalendarMonthViewController.m
#import "CalendarMonthViewController.h"
#import "ViewControllerInsert.h"
- (void)SegueInsert {
NSDate *dateUserSelected = self.dateSelected;
ViewControllerInsert *controller = [[ViewControllerInsert alloc] initWithNibName:#"ViewControllerInsert" bundle:nil];
controller.dateSelected = dateUserSelected; // set property in Insert
[self presentViewController:controller animated:YES completion:nil]; // present
}
Runtime error on click:
on whose view is not in the window hierarchy!
PS: I cannot Segue via Storyboard, since it uses another instance, and the property that is supposed to get set, does not get set.
Wain is right. The extra view controller is causing a problem. However, I don't think you will be able to just move the code over. You should keep a pointer to your calendar in your navigation controller and just set the property in SequqInsert. Something like this:
#import <UIKit/UIKit.h>
#interface ViewControllerCalendarContainer : UIViewController
#property (weak, nonatomic) CalendarMonthViewController *calendarViewController;
- (IBAction)SeguqInsert:(id)sender;
#end
#import "ViewControllerCalendarContainer.h"
#import "CalendarMonthViewController.h"
...
- (IBAction)SeguqInsert:(id)sender {
NSDate *dateUserSelected = self.dateSelected;
ViewControllerInsert *controller = [[ViewControllerInsert alloc] initWithNibName:#"ViewControllerInsert" bundle:nil];
controller.dateSelected = calendarViewController.dateUserSelected; // set property in Insert
[self presentViewController:controller animated:YES completion:nil]; // present
}
If you're worried about keeping a pointer to the calendar, you can always use protocols to acquire the information.
You seem to have added a view controller that you don't need. The error is because you never show that view controller and then try to present another view controller from it.
Take the code in SegueInsert and move it to SeguqInsert. Then delete the CalendarMonthViewController (which presumably isn't doing anything else and has no other code).

Resources