Reputation: 1105
I've been wrestling with this for a few days - I have a custom protocol that is supposed to get the data from one of my models (via it's controller) to a view that graphs it.
Here's how I am doing this - step by step:
In graphing view, I declare the protocol as follows:
@class GraphView;
@protocol GraphViewDataSource <NSObject>
-(CGFloat)yValueForGraphView:(GraphView *)sender usingXval:(CGFloat)xVal;
@end
I then declare a property in view.h
@interface GraphView : UIView
@property (nonatomic, weak) IBOutlet id <GraphViewDataSource> dataSource;
@end
I synthesize the property in view.m:
@synthesize dataSource=_dataSource;
Then in my drawRect, I am calling this method to bring me back a CGFloat from another controller's model:
-(void) drawRect:(CGRect)rect
{
//context stuff, setting line width, etc
CGPoint startPoint=CGPointMake(5.0, 6.0);
NSLog(@"first, y value is: %f", startPoint.y);
startPoint.y=[self.dataSource yValueForGraphView:self usingXval:startPoint.x]; //accessing delegate via property
NSLog(@"now the y value now is: %f", startPoint.y);
//other code..
}
Now in my other view controller, I am importing the view.h file and declaring that it conforms to the protocol:
#import "GraphView.h"
@interface CalculatorViewController () <GraphViewDataSource>
Creating a property for the GraphView:
@property (nonatomic, strong) GraphView *theGraphView;
Synthesize:
@synthesize theGraphView=_theGraphView;
Now in the setter, I set the current controller as the dataSource (aka the delegate):
-(void) setTheGraphView:(GraphView *)theGraphView
{
_theGraphView=theGraphView;
self.theGraphView.dataSource=self;
}
I also set the controller as the delegate in prepareForSegue (one of the things I tried while looking for a fix):
-(void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:@"GraphViewController"])
{
self.theGraphView.dataSource=self;
}
}
Finally, I implement the required method:
-(CGFloat)yValueForGraphView:(GraphView *)sender usingXval:(CGFloat)xVal
{
CGFloat test=51.40; //just to test
return test;
}
And the output I'm getting from my test NSLog in graphView's drawRect is:
2012-10-25 20:56:36.352 ..[2494:c07] first, y value is: 6.000000
2012-10-25 20:56:36.354 ..[2494:c07] now the y value now is: 0.000000
This should be returning 51.40 via the dataSource, but it is not. I can't figure out why! Driving me crazy, it seems like I've done everything right. But the delegate method is not getting called.
Is there some silly thing I am missing?
Additional info - diagram of controllers & GraphView:
Upvotes: 1
Views: 601
Reputation: 1105
here is the final solution:
the GraphViewController had to be set the delegate of the GraphView first. That is because graphView is connected to the GraphViewController (as a custom UIView) and that particular instance of graphView is not available could not be set from other classes, I found.
The way to get information to a custom view (graphView in my case) is by setting up another protocol in the custom view's controller. Then setting another class as the delegate of that controller and passing the data to the controller first, and then to the custom view.
One very important thing is when working with storyboards, the delegate for the controller should be set in prepareForSegue (as mentioned by reckerbh). In my case, CalculatorViewController segued to the GraphViewController so my perpareForSegue looked like this:
-(void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:@"ShowGraph"])
{
GraphViewController *gvc=[segue destinationViewController];
gvc.yValueSource=self;
}
}
Note here that I am creating a new pointer to the existing instance of GraphViewController via [segue destinationViewController]. I am not creating a new one via alloc init because that would not be connected to the one I already had and would be completely irrelevant.
So I guess the main advice here is to make sure that you are using the same instance when you are setting the delegate. And if you can't access that instance, use double delegation (or even tripple I guess if you had to).
Upvotes: 0
Reputation: 5836
The way I solved this was chaining some delegates, my GraphView
declares a protocol
GraphViewDelegate
with the following methods:
-(float)ordinateForAbscissa:(float)abscissa forGraph:(GraphView *)requestor;
-(float)scaleForGraph(GraphView *)requestor;
This protocol is implemented by the GraphViewController
and he sets himself as a delegate
of the GraphView
when the controller loads in viewDidLoad
.
Now I have another delegate that is CalculatorControllerDelegate
that implements yCoordinateForX
that is called from ordinateForAbscissa
. CalculatorViewController
implements this protocol and when segueing sets itself up as the GraphViewController
's delegate.
Hope this helps.
Upvotes: 0
Reputation: 9911
The diagram helps!
What you need to do is have a method on your GraphViewController that either exposes your custom GraphView, or wraps setting the delegate in it. On the segue, you'll need to get access to the destination view controller (the GraphViewController), and then either set the delegate directly in your GraphView or pass self into the GraphViewController and have it set the delegate.
Setting the delegate in the GraphView owned by your CalculatorViewController won't impact the GraphView in your GraphViewController unless the GraphView is a singleton, or is passed into the GraphViewController.
Assuming your GraphViewController exposes a property that is the GraphView it contains, the segue in the CalculatorViewController should be something like...
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
// Ensure the identifier string matches the segue name from the Storyboard
if ([segue.identifier isEqualToString:@"GraphViewControllerSegue"])
{
// Change this to whatever class is the destination class of the segue
GraphViewController *graphViewController = (GraphViewController *)[segue destinationViewController];
self.graphView.delegate = self;
// Pass the GraphView with the delegate set into the GraphViewController
graphViewController.graphView = self.graphView;
}
}
The fact that it's a direct segue from the button vs an action is irrelevant. If you've given the segue an identifier in the Storyboard/XIB then prepareForSegue:sender:
is called, and we can determine what segue is being called.
And if you don't have a public property in your GraphViewController for your GraphView...
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:@"GraphViewControllerSegue"])
{
// Change this to whatever class is the destination class of the segue
GraphViewController *graphViewController = (GraphViewController *)[segue destinationViewController];
[graphViewController setGraphViewDelegate:self];
}
}
and you'll need the following in your GraphViewController implementation.
- (void)setGraphViewDelegate:(id)graphViewDelegate {
// Set the delegate directly in the ivar
_graphView.delegate = graphViewDelegate;
}
Upvotes: 1