Obj-C / Using MapKitView and TableView together with delegation - objective-c

I am a very beginner of objective c and I wanted to create a scene about using 2 child view controller in a master view controller which includes MapKit and TableView. I ve searched it on internet for 1 day and but I couldn't make any of solutions since I failed in different steps of each suggestions. I know that there are a lot of way to pass data between View Controllers and I thought the best way is using delegation logic in this situation. (let me know if I am wrong please). By the way, I can update or move the cursor onto specific location WHEN I set a dummy button on MapKitViewController, so MapKit part is not where I am failing at. I am pretty sure that the problem is about communication between 2 View Controllers which are active at the same time.
Problem:
Updating MapView by clicking a table row of TableView which includes location coordinate detail. Both children view are set on a MasterViewController by 2 container views.
Tried so far:
Created a method "goToLocation:(Location*) location" in MapViewController and sent parameters to this method from TableViewController. =>(Lat and Long parameters received by goToLocation() but MapKitView doesn't get updated)
Tried to create a delegate logic between MapView and TableView.=>(Probably I couldn't create it properly. Please see below)
What I want :
I want to know what part I am doing wrong. Is there a easy or proper way to achieve that ?
TableViewController.h
#class TableViewController; //define class
#protocol TableViewDelegate <NSObject> //define delegate protocol
//define delegate method to be implemented within another class
- (void) locationSelected: (TableViewController *) sender object:(Location *) location;
#end
#property (nonatomic, weak) id <TableViewDelegate> delegate; //define TableViewVCDelegate as delegate
TableViewController.m
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
[self notifyNow:[_locations objectAtIndex:indexPath.row]];
}
- (void) notifyNow:(Location *) location {
//this will call the method implemented in your other class
[self.delegate locationSelected:self object:(Location *) location];
NSLog(#"Selected: %#",[location name]);
}
MapViewController.h
//make it a delegate for TableViewVCDelegate
#interface MapViewController : UIViewController <TableViewDelegate>
MapViewController.m
- (void)viewDidLoad {
_tableViewController = [[TableViewController alloc]init];
_tableViewController.delegate = self; //set its delegate to self somewhere
}
!!_ This Method Doesn't Get Triggered _!!
- (void)locationSelected:(TableViewController *)sender object:(Location *)location {
NSLog(#"%# is great!", [location name]);
}
MainViewController.m (Master/Root View Controller)
#import "MainViewController.h"
#import "MapViewController.h"
#import "TableViewController.h"
#interface MainViewController ()
#property (strong, nonatomic) IBOutlet UIView *topCont;
#property (strong, nonatomic) IBOutlet UIView *bottomCont;
#property (strong, nonatomic) IBOutlet UIView *safeArea;
#end
#implementation MainViewController
MapViewController *mapViewVC;
TableViewController *tableViewVC;
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
[self.navigationController setNavigationBarHidden:YES animated:YES];
tableViewVC = [self.storyboard instantiateViewControllerWithIdentifier:#"TableViewController"];
mapViewVC = [self.storyboard instantiateViewControllerWithIdentifier:#"TopChildVC"];
[self addChildViewController:tableViewVC];
[tableViewVC.view setFrame:CGRectMake(0.0f,
0.0f,
self.safeArea.frame.size.width,
self.safeArea.frame.size.height/2)];
[self.bottomCont addSubview:tableViewVC.view];
[tableViewVC didMoveToParentViewController:self];
[self addChildViewController:mapViewVC];
[mapViewVC.view setFrame:CGRectMake(0.0f,
0.0f,
self.safeArea.frame.size.width,
self.safeArea.frame.size.height/2)];
[self.topCont addSubview:mapViewVC.view];
[mapViewVC didMoveToParentViewController:self];
}
#end

Related

How can I send selectedSegmentIndex to containerView in Objective-c?

I have a containerView into my viewController. My viewController has a UISegmentedControl.
How can I send this selectedSegmentIndex selected at this moment and everytime when I change this?
I', trying with a property in the next class with prepareForSegue but this only send on first load..
Thanks!
Then Edit I'm getting: -[ViewController containerViewDidChangeSegmentIndex:]: unrecognized selector sent to instance 0x7f8db16286d0
FirstViewController (This contains A select and a containerView)
ViewController.h
#interface ViewController : UIViewController
#property (weak, nonatomic) IBOutlet UISegmentedControl *select;
#property (weak, nonatomic) IBOutlet UIView *container;
#property(nonatomic, assign) ContainerViewController * classLevelReference;
#end
ViewController.m
#interface ViewController ()
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
[_select addTarget:self action:#selector(segmentChanged:) forControlEvents:UIControlEventValueChanged];
}
- (void)segmentChanged:(UISegmentedControl *)segment{
//since you've reference to your container view here, you can directly call its method here:
[self.classLevelReference containerViewDidChangeSegmentIndex:segment.selectedSegmentIndex];
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
//don't forget to check the segue id, if you've multiple segues
ContainerViewController *containerView = [segue destinationViewController];
self.classLevelReference = self;
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
ContainerViewController.h
#interface ContainerViewController : UIViewController
- (void)containerViewDidChangeSegmentIndex:(NSInteger)updatedIndex;
#end
ContainerView.m
#implementation ContainerViewController
- (void)containerViewDidChangeSegmentIndex:(NSInteger)updatedIndex{
//Do whatever you want with your updated index
NSLog(#"changing");
}
#end
You might want to make a method in your ContainerViewController.h like this:
#interface ContainerViewController:UIViewController
//.....other implementation here.
- (void)containerViewDidChangeSegmentIndex:(NSInteger)updatedIndex;
#end
Now implement this method in your ContainerViewController.m like this:
- (void)containerViewDidChangeSegmentIndex:(NSInteger)updatedIndex{
//Do whatever you want with your updated index
}
Now in your prepare for segue, save the reference to your ContainerViewController in a class-level variable :
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
//don't forget to check the segue id, if you've multiple segues
ContainerViewController *containerView = [segue destinationViewController];
self.classLevelReference = containerView;
}
Lastly in the FirstViewController.m, tell the container view when segment index changes.
- (void)viewDidLoad{
//.....your other implementation here.....
//add a listener to your segment's value changed action
[youregements addTarget:self action:#selector(segmentChanged:) forControlEvents:UIControlEventValueChanged];
}
- (void)segmentChanged:(UISegmentedControl *)segment{
//since you've reference to your container view here, you can directly call its method here:
[self.classLevelReference containerViewDidChangeSegmentIndex:segment.selectedSegmentIndex];
}
Important: you might've different names for the ContainerViewController and FirstViewController in your case, just apply the changes carefully.
Happy coding!

delegation and passing data back from childViewController

I have been struggling with this for a few days and have received valuable help on the way from S.O. I have made the simplest possible project to reduce the possibilities of it being a typo.
All my project is, is a ViewController that holds a container view hooked to a childViewController. The "parent" ViewController is set as the delegate of the childViewController. In the viewDidLoad of the child I am passing a value which is just a string. This string should be passed on to the parent and printed on the console. Here are the files.
ViewController.h
#import <UIKit/UIKit.h>
#import "ChildViewController.h"
#interface ViewController : UIViewController <ChildViewControllerDelegate>
#end
ViewController.m
#import "ViewController.h"
#interface ViewController ()
#property NSString *myValueRetrieved;
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
ChildViewController *controller = [self.storyboard instantiateViewControllerWithIdentifier:#"ChildVC"];
controller.delegate = self;
NSLog(#"Here is my value: %#",self.myValueRetrieved);
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
}
- (void) passValue:(NSString *)theValue{
self.myValueRetrieved = theValue;
}
#end
ChildViewController.h
#import <UIKit/UIKit.h>
#protocol ChildViewControllerDelegate;
#interface ChildViewController : UIViewController
#property (weak)id <ChildViewControllerDelegate> delegate;
#end
#protocol ChildViewControllerDelegate <NSObject>
- (void) passValue:(NSString*) theValue;
#end
ChildViewController.m
#import "ChildViewController.h"
#interface ChildViewController ()
#property NSArray *colors;
#end
#implementation ChildViewController
- (void)viewDidLoad {
[super viewDidLoad];
[self.delegate passValue:#"Hello"];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
}
#end
Am I right to think that when the app is launched, the console should log the following message: "here is my value: hello". Am I doing something wrong in terms of logically not getting delegation or is it just a silly typo somewhere? tx
You're assuming that the view is loaded when the view controller is instantiated. That's now how it works. The view gets loaded when it's needed (like to add to the parent view).
But you can force the view to load and make this work. Call -loadViewIfNeeded on the child view controller right after setting the delegate. That will probably get you what you want:
controller.delegate = self;
[controller loadViewIfNeeded];
NSLog(#"Here is my value: %#",self.myValueRetrieved);
Or, if you do want to call back the delegate in viewDidLoad, then you'd need to move the NSLog to the -passValue: method, since the primary view controller's viewDidLoad method will have already finished running.
To do this make ParentController a delegate of ChildController. This allows ChildController to send a message back to ParentController enabling us to send data back.
For ParentController to be delegate of ChildController it must conform to ChildController's protocol which we have to specify. This tells ParentController which methods it must implement.
In ChildController.h, below the #import, but above #interface you specify the protocol.
#class ChildController;
#protocol ViewControllerBDelegate <NSObject>
- (void)addItemViewController:(ChildController *)controller didFinishEnteringItem:(NSString *)item;
#end
next still in the ChildController.h you need to setup a delegate property and synthesize in ChildController.h
#property (nonatomic, weak) id <ChildControllerDelegate> delegate;
In ChildController we call a message on the delegate when we pop the view controller.
NSString *itemToPassBack = #"Pass this value back to ParentController";
[self.delegate addItemViewController:self didFinishEnteringItem:itemToPassBack];
That's it for ChildController. Now in ParentController.h, tell ParentViewController to import Child and conform to its protocol.
import "ChildController.h"
#interface ParentController : UIViewController
In ParentController.m implement the following method from our protocol
- (void)addItemViewController:(ChildController *)controller didFinishEnteringItem:(NSString *)item
{
NSLog(#"This was returned from ChildController %#",item);
}
The last thing we need to do is tell ChildController that ParentController is its delegate before we push ChildController on to nav stack.
ChildController *ChildController = [[ChildController alloc] initWithNib:#"ChildController" bundle:nil];
ChildController.delegate = self
[[self navigationController] pushViewController:ChildController animated:YES];

variables returning null value upon switching View Controllers in Objective C

Im new to programming with objective C and am working on moving data between View controllers. I am wondering if Bi-directional flow of data (variables) between ViewControllers is possible.
I can move data backwards (to the presentingViewController / sourceViewController) however i cannot move data forward (to the presentedViewController / destinationViewController).
I have made a simple case scenario (involving strings to get a principle of the idea) of this and it involves updating a UItextField on the destinationViewController using a UILabel in the sourceViewController and vice-versa.
I CANNOT update the UITextField using the UILabel, but can update the UILabel using the UITextField.
I have made Logs of different statements to track the variable values however when I switch ViewControllers the variables Data returns to null even if they are marked as strong.
Can you please offer any guidance, its been tearing away at my mind, or am I missing something obvious? I don't get why I keep getting a (null) value (in my NSLog) when I switch ViewControllers.
My sourceViewController / presentingViewController is named "ViewController."
My destinationViewController / presentedViewController is named "Gears2ViewController".
I have attached my code files below:
ViewController.h:
#import <UIKit/UIKit.h>
#interface ViewController : UIViewController
#property (strong, nonatomic) IBOutlet UILabel *outputLabel;
- (IBAction)ExitToHere:(UIStoryboardSegue *)sender;
#end
ViewController.m:
#import "ViewController.h"
#import "Gear2ViewController.h"
#interface ViewController ()
- (IBAction)changeItem:(id)sender;
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (IBAction)changeItem:(id)sender {
Gear2ViewController *G2VC=[[Gear2ViewController alloc] init];
G2VC.peterSido=[NSString stringWithFormat:#"%#",self.outputLabel.text];
[self performSegueWithIdentifier:#"toGear2" sender:self];
NSLog(#"ViewController UILabel reads %#",G2VC.peterSido);
}
- (void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
}
- (IBAction)ExitToHere:(UIStoryboardSegue *)sender {
}
#end
Gears2ViewController.h:
#import <UIKit/UIKit.h>
#import "ViewController.h"
#interface Gear2ViewController : UIViewController
#property (strong, nonatomic) NSString *peterSido;
#end
Gears2ViewController.m:
#interface Gear2ViewController ()
#property (strong, nonatomic) IBOutlet UITextField *updatedOutput;
- (IBAction)updateOutput:(id)sender;
#end
#implementation Gear2ViewController
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
NSLog(#"Gears2ViewController ViewDidAppear reads %#",self.peterSido);
}
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(#"Gears2ViewController ViewDidLoad responds %#",self.peterSido);
// Do any additional setup after loading the view.
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (IBAction)updateOutput:(id)sender {
self.peterSido = self.updatedOutput.text;
((ViewController *)self.presentingViewController).outputLabel.text = self.peterSido;
NSLog(#"Gears2View Controller updating ViewController UILabel reads %#",self.peterSido);
}
#end
NSLog:
2015-06-29 18:52:58.798 testerBeta[21735:645772] Gears2ViewController ViewDidLoad responds (null)
2015-06-29 18:52:58.799 testerBeta[21735:645772] ViewController UILabel reads I like Pie
2015-06-29 18:52:59.317 testerBeta[21735:645772] Gears2ViewController ViewDidAppear reads (null)
2015-06-29 18:53:12.651 testerBeta[21735:645772] Gears2View Controller updating ViewController UILabel reads No I dont
Quite Lengthy but Thanks in Advance!!!
You want to pass the data in prepareForSegue:, like so:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)__unused sender
{
if ([[segue identifier] isEqualToString:#"toGear2"])
{
Gear2ViewController *controller = (Gear2ViewController *)segue.destinationViewController;
controller.peterSido = self.outputLabel.text;
}
}
The reason why is that the segue instantiates the presented view controller for you, and you then set the property of the instantiated view controller which the segue will present.
To pass the data back, you can use an unwind segue, which can get the value from the presented view controller's property.
- (IBAction)unwindFromGear2:(UIStoryboardSegue *)segue
{
Gear2ViewController *controller = (Gear2ViewController *)segue.sourceViewController;
self.outputLabel.text = controller.peterSido;
}
This is the proper way to pass data back and forth via segues. Gear2ViewController shouldn't be setting properties on its presentingViewController.
Update:
The preferred way to test that a property isn't nil is like this:
if (self.peterSido)
{
self.updatedOutput.text = self.peterSido;
}
else // No need for if test here
{
self.updatedOutput.text = #"";
}
That's the long form, but the assignment and if test can be more concisely written as:
self.updatedOutput.text = self.peterSido ?: #"";
When you declare any variable as #property then you need to synthesize it in .m file .
You have declared your outputLabel as #property but you missed to synthesize it in .m file.
When you synthesize any variable then it allows you to get and set the values to it .
Do it it will help you.
Thank you.

Using protocol to trigger a segue from a UIView inside a UIViewcontroller

CONFIGURATION:
-UIviewController embedded in Navigationcontroller.
-UIviewController has a UIscrollview as subview
-UIscrollview has some views where charts are created: each view containing a chart has its own .h and .m file and from this file I want trigger a segue to a tableview controller.
-A Tableviewcontroller was added in xcode and a segue from the UIviewController to the TableViewcontroller was created as well (Xcode)
-created a protocol in the UIView to have the segue pushed from there.
PROBLEM:
delegate always nil, segue method will never be called
UIVIEW .h file
#protocol UItoUIvcDelegate <NSObject>
-(void)triggerSegue;
#end
#interface CFfirstGraph : UIView <CPTPlotDataSource , CPTPieChartDelegate,UIActionSheetDelegate>
#property(weak, nonatomic) id <UItoUIvcDelegate> delegate;
#end
UIVIEW .m file (snippet)
-(void)pieChart:(CPTPieChart *)pieChart sliceWasSelectedAtRecordIndex:(NSUInteger)index
{
if (self.delegate == nil)
{
NSLog(#"nil");
}
[self.delegate triggerSegue];
}
UIVIEWCONTROLLER .h file
#import "CFfirstGraph.h"
#interface CFMainViewController : UIViewController <UItoUIvcDelegate, UITableViewDelegate, UITableViewDataSource>
#end
UIVIEWCONTROLLER .m file (snippet)
- (void)viewDidLoad
{
[super viewDidLoad];
self.scrollView.contentSize = CGSizeMake(320, 1000);
[self.view addSubview:self.scrollView];
CFfirstGraph *click =[[CFfirstGraph alloc]init];
click.delegate = self ;
}
-(void)triggerSegue
{
[self performSegueWithIdentifier:#"detailedData" sender:self];
NSLog(#"estoy aqui");
}
What am I doing wrong ? why the delegate is always nil ? I tried to add the method setDelegate but still no luck.
Thanks,
dom
Make CFfirstGraph as a strong property.
#property (strong, nonatomic) CFfirstGraph * click;
- (void)viewDidLoad
{
[super viewDidLoad];
self.click =[[CFfirstGraph alloc]init];
self.click.delegate = self ;
self.scrollView.contentSize = CGSizeMake(320, 1000);
[self.view addSubview:self.scrollView];
}
ok, after many hours of sweat I found the issue.
First...delegate = nil was not the main problem.
The real issue was the protocol method triggering the segue was never called.
If i create and initialize a CFfirstGraph object (or even property) it won't be related to the view created already in x-code, and this is the main issue.
On the other hand...if I "CTRL-drag" an outlet from the UIview to the CFMainViewController i will have a property that is exactly the one i need:
#interface CFMainViewController () <UIScrollViewDelegate>
#property (weak, nonatomic) IBOutlet CFfirstGraph *FirstGraph;
Then i assign the delegate to self (CFMainViewController) in the viewDidLoad:
- (void)viewDidLoad
{
[super viewDidLoad];
self.FirstGraph.delegate = self ;
}
and the delegate method "triggerSegue" will be executed being called from the UIVIEW.
Best Regards,
dom

objective C: Using a Delegate to call a function in parent class

I'm creating a 3 layer navigation popup controller and on the 3rd popup controller I have a delegate method to access dismissPopup method that is in the parent class. I can't seem to call it, my NSLog messages in the function in the parent class isn't even showing so I must be either using delegation wrong or I'm calling it incorrectly.
The 3 classes ParentViewController has a toolbar with a button that brings up the table view --> RegionViewController is the First table view controller with items --> ConusViewController is the 2nd table view controller that is pushed onto the navigation stack. I'm trying to call the method dismissPopover that is in the parent method with a delegation after the selection is clicked on so the whole popover goes away.
In the ConusViewController if the delegation had worked I would have seen "Method Accessed" from the function in the parent class. It doesn't show so I must be using delegation wrong.
Sorry for being so wordy on my post, I wanted to be complete on what I'm trying to do here. Thanks.
ParentViewController.h
#import <UIKit/UIKit.h>
#import "ConusViewController.h"
#interface EnscoWXViewController : UIViewController <ConusViewControllerDelegate> {
UIPopoverController *popoverController;
IBOutlet UIWebView *webImageDisplay;
ConusViewController *cViewController;
}
#property (nonatomic, retain) UIPopoverController *popoverController;
#property (nonatomic, retain) UIWebView *webImageDisplay;
#property (nonatomic, retain) ConusViewController *cViewController;
-(IBAction) buttonShowRegion:(id) sender;
#end
ParentViewController.m
#import "ParentViewController.h"
#import "RegionViewController.h"
#implementation ParentViewController
#synthesize cViewController;
-(IBAction) buttonShowRegion:(id) sender {
...
}
-(void)dismissPopover {
[popoverController dismissPopoverAnimated:YES];
printf("Method Accessed\n");
}
- (void)viewDidLoad {
cViewController = [[ConusViewController alloc] init];
cViewController.delegate = self;
[super viewDidLoad];
}
RegionViewController.m
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
switch (indexPath.row) {
case 0: {
ConusViewController *conusViewController = [[ConusViewController alloc] initWithNibName:#"ConusViewController" bundle:nil];
conusViewController.contentSizeForViewInPopover = CGSizeMake(320, 350);
[self.navigationController pushViewController:conusViewController animated:YES];
[conusViewController release];
break;
}
case 1: {
break;
}
}
}
ConusViewController.h
#import <UIKit/UIKit.h>
#protocol ConusViewControllerDelegate <NSObject>
#required
- (void)dismissPopover;
#end
#interface ConusViewController : UITableViewController {
NSMutableArray *conusItems;
id delegate;
}
#property (nonatomic, assign) id <ConusViewControllerDelegate> delegate ;
#end
ConusViewController.m
#import "ConusViewController.h"
#import "ParentWXViewController.h"
#implementation ConusViewController
#synthesize delegate;
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
NSString *itemRequested = [conusItems objectAtIndex:indexPath.row];
NSLog(#"logging: %#", itemRequested);
[delegate dismissPopover];
[itemRequested release];
}
Just before calling [delegate dismissPopover], check if delegate is actually set. It probably isn't.
I see in ParentViewController.m you create an instance of ConusViewController and set its delegate, but never display it. In RegionViewController.m you create another instance of ConusViewController without setting its delegate and that is the one that seems to be being displayed.
Not sure if I missed it, but I never see you set the delegate property in ConusViewController. That needs to be set to an instance of the object that is to be delegated to (the object that has dismissPopover implemented in it).