自定义协议未在Xcode中调用,返回0

时间:2012-10-26 01:05:06

标签: objective-c xcode delegates protocols

我已经和它搏斗了几天 - 我有一个自定义协议,应该从我的一个模型(通过它的控制器)获取数据到一个图形的视图。

这是我如何做到这一步 - 一步一步:

在图表视图中,我声明协议如下:

@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:

GraphingCalc

3 个答案:

答案 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调用的yCoordinateForXordinateForAbscissa实现此协议,并且当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创建一个新的,因为它不会连接到我已经拥有的那个并且完全不相关。

所以我猜这里的主要建议是确保在设置委托时使用相同的实例。如果您无法访问该实例,请使用双重委派(如果必须,甚至可以使用双重委托)。