1

I'm trying to call an instance method which is in my UIViewController from within a UIView. In my UIViewController I have something like:

-(void) test {
    NSLog(@"test");
}

In my UIViewController, I create an instance of my UIView like so:

draggableView = [[DraggableView alloc]initWithFrame:CGRectMake(20, 190, 280, 280)];

In my draggableView, I then want to call the test instance method. How do I do this without creating a new instance of the UIViewController?

I have tried this, but it doesn't seem like a very elegant solution and I get an error "No visible @interface ..."

Community
  • 1
  • 1
Brett Gregson
  • 5,867
  • 3
  • 42
  • 60
  • Use a delegate (i.e. tell the view the viewcontroller-delegate to use and make the view call back on that delegate). – trojanfoe Jul 09 '14 at 09:48
  • Thanks @trojanfoe, do you have a good example anywhere for me? – Brett Gregson Jul 09 '14 at 09:54
  • 1
    Delegates are very common in Objective-C and there are tons of examples on the net and questions on here. For example: http://www.alexefish.com/post/522641eb31fa2a0015000002 – trojanfoe Jul 09 '14 at 09:55

1 Answers1

3

View does not have default method to access its view-controller object. You need to pass the view-controller object into the view object yourself. Typical way to do this is making a property.

@class ViewController;

@interface DraggableView : NSObject
@property (readwrite,nonatomic,assign) ViewController* theOwnerViewController;
@end

@implementation DraggableView
- (void)testCallingOwnerViewControllerMethod
{
    [self.theOwnerViewController test];
}
@end

You need to set the theOwnerViewController after you created the DraggableView object.

- (void)loadView
{
    draggableView = [[DraggableView alloc]initWithFrame:CGRectMake(20, 190, 280, 280)];
    draggableView.theOwnerViewController = self;
    //...extra view setup.
}

Use assign to avoid retain-cycle on the property.

Delegate pattern

You can do this by above pattern, but as you noticed, you need to know and forward-declare the owner view-controller class' name from its view (which is sub-node of the VC). Usually This is bad design choice because it's easily makes circular dependency (or backward dependency), which usually creates tight-coupling.

Instead, you can use delegate pattern to avoid circular dependency issue.

@protocol TestDelegate
- (void)test;
@end

@interface DraggableView : NSObject
@property(readwrite,nonatomic,assign) id<TestDelegate> testDelegate;
@end

@implementation DraggableView
- (void)test
{
    [self.testDelegate test];
}
@end

You need to set the testDelegate after you created the DraggableView object.

@interface ViewController<TestDelegate>
@end
@implementation
- (void)test
{
    // do something.
}
- (void)loadView
{
    draggableView = [[DraggableView alloc]initWithFrame:CGRectMake(20, 190, 280, 280)];
    draggableView.testDelegate = self;
    //...extra view setup.
}
@end

In this case, you don't have to know the class name of the view object before you create. Any class which conforms TestDelegate protocol can be used, and now the view and VC are loosely-coupled via the protocol.

Community
  • 1
  • 1
eonil
  • 83,476
  • 81
  • 317
  • 516
  • I did try this earlier, but I keep getting the error `No visible @interface declares the selector test` – Brett Gregson Jul 09 '14 at 09:52
  • @eskimo You can avoid the error by declaring the `@class` name before using it. Anyway delegate provides more flexibility with less dependency. – eonil Jul 09 '14 at 09:53
  • Thanks @Eonil. What do you mean by that (declaring the class name)? – Brett Gregson Jul 09 '14 at 09:55
  • The circular dependency problem is trivial to solve. The disadvantages of having the view know about the view controller are: increased coupling between the two classes; it becomes impossible to reuse the view in another context; and it also makes it possible for the view to control its controller, which will make the control flow hard to follow. – Douglas Hill Jul 09 '14 at 10:17
  • @DouglasHill I agree. Coupling is the problem rather then dependency. – eonil Jul 09 '14 at 10:19
  • 1
    @eskimo That specifically means `@class ViewController;` line. See http://stackoverflow.com/questions/16789252/forward-declaration-for-objective-c-interfaces for more details. – eonil Jul 09 '14 at 10:21
  • Thanks @Eonil, got it working using the delegate. Much appreciated! – Brett Gregson Jul 09 '14 at 10:21