我已经和它搏斗了几天 - 我有一个自定义协议,应该从我的一个模型(通过它的控制器)获取数据到一个图形的视图。
这是我如何做到这一步 - 一步一步:
在图表视图中,我声明协议如下:
@class GraphView;
@protocol GraphViewDataSource <NSObject>
-(CGFloat)yValueForGraphView:(GraphView *)sender usingXval:(CGFloat)xVal;
@end
然后我在view.h中声明了一个属性
@interface GraphView : UIView
@property (nonatomic, weak) IBOutlet id <GraphViewDataSource> dataSource;
@end
我在view.m中合成了属性:
@synthesize dataSource=_dataSource;
然后在我的drawRect中,我调用这个方法让我从另一个控制器的模型中恢复一个CGFloat:
-(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..
}
现在在我的其他视图控制器中,我正在导入view.h文件并声明它符合协议:
#import "GraphView.h"
@interface CalculatorViewController () <GraphViewDataSource>
为GraphView创建属性:
@property (nonatomic, strong) GraphView *theGraphView;
合成
@synthesize theGraphView=_theGraphView;
现在在setter中,我将当前控制器设置为dataSource(也就是委托):
-(void) setTheGraphView:(GraphView *)theGraphView
{
_theGraphView=theGraphView;
self.theGraphView.dataSource=self;
}
我还将控制器设置为prepareForSegue中的委托(我在寻找修复时尝试过的一件事):
-(void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:@"GraphViewController"])
{
self.theGraphView.dataSource=self;
}
}
最后,我实现了所需的方法:
-(CGFloat)yValueForGraphView:(GraphView *)sender usingXval:(CGFloat)xVal
{
CGFloat test=51.40; //just to test
return test;
}
我在graphView的drawRect中测试NSLog的输出是:
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
这应该通过dataSource返回51.40,但事实并非如此。我无法弄清楚原因!让我发疯,看起来我已经做好了一切。但委托方法没有被调用。
我遗失了一些愚蠢的事情吗?
附加信息 - 控制器图表&amp; GraphView:
答案 0 :(得分:1)
图表有帮助!
你需要做的是在GraphViewController上有一个方法,它暴露你的自定义GraphView,或者包装设置委托。在segue上,您需要访问目标视图控制器(GraphViewController),然后直接在GraphView中设置委托或将self传递给GraphViewController并让它设置委托。
在CalculatorViewController拥有的GraphView中设置委托不会影响GraphViewController中的GraphView,除非GraphView是单例,或者传递给GraphViewController。
假设您的GraphViewController公开了一个属于它包含的GraphView的属性,那么CalculatorViewController中的segue应该类似......
- (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;
}
}
事实上,它与按钮直接相对于动作是无关紧要的。如果你在Storyboard / XIB中给segue一个标识符,那么调用prepareForSegue:sender:
,我们就可以确定调用了什么segue。
如果GraphView的GraphViewController中没有公共属性...
- (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];
}
}
并且您需要在GraphViewController实现中使用以下内容。
- (void)setGraphViewDelegate:(id)graphViewDelegate {
// Set the delegate directly in the ivar
_graphView.delegate = graphViewDelegate;
}
答案 1 :(得分:0)
我解决这个问题的方法是链接一些代表,我的GraphView
使用以下方法声明protocol
GraphViewDelegate
:
-(float)ordinateForAbscissa:(float)abscissa forGraph:(GraphView *)requestor;
-(float)scaleForGraph(GraphView *)requestor;
此协议由GraphViewController
实施,当控制器加载delegate
时,他将自己设置为GraphView
的{{1}}。
现在我有另一个viewDidLoad
委托来实现从CalculatorControllerDelegate
调用的yCoordinateForX
。 ordinateForAbscissa
实现此协议,并且当segueing将自己设置为CalculatorViewController
的委托时。
希望这有帮助。
答案 2 :(得分:0)
这是最终解决方案:
必须先将GraphViewController设置为GraphView的委托。这是因为graphView连接到GraphViewController(作为自定义UIView),并且我发现无法从其他类设置graphView的特定实例。
获取信息到自定义视图(在我的情况下是graphView)的方法是在自定义视图的控制器中设置另一个协议。然后将另一个类设置为该控制器的委托,并首先将数据传递给控制器,然后再传递给自定义视图。
一个非常重要的事情是在使用故事板时,控制器的委托应该在prepareForSegue中设置(如reckerbh所述)。在我的例子中,CalculatorViewController被segued到GraphViewController,所以我的perpareForSegue看起来像这样:
-(void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:@"ShowGraph"])
{
GraphViewController *gvc=[segue destinationViewController];
gvc.yValueSource=self;
}
}
请注意,我正在通过[segue destinationViewController]创建一个指向GraphViewController现有实例的新指针。我不是通过alloc init创建一个新的,因为它不会连接到我已经拥有的那个并且完全不相关。
所以我猜这里的主要建议是确保在设置委托时使用相同的实例。如果您无法访问该实例,请使用双重委派(如果必须,甚至可以使用双重委托)。