Can I detect UITableView delegates in parent class? - objective-c

I have a ViewController that imports two TableViewControllers.
I have the delegate/datasource methods executing in these subclasses, but is there anyway that the ViewController can tell when the each TableView has executed methods such as the delegate method didSelectRowAtIndexPath or do I have to add methods in each tableviewcontroller for polling if cells had been selected?
Thanks!

You can define your own notifications, e.g. "MyDidSelectRowAtIndexPathNotification", and make your main view controller an observer of this notification:
#define kMyDidSelectNotification #"MyDidSelectRowAtIndexPathNotification"
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(myDidSelectAction:) name:kMyDidSelectNotification object:nil];
Then, in your implementation of tableView:didSelectRowAtIndexPath, you can simply trigger this notification for all observers:
[[NSNotificationCenter defaultCenter]
postNotificationName:kMyDidSelectNotification object:nil];
You can see here that you could pass the UITableViewCell or some other object in the notification, if you need it. Your handler for the notification (myDidSelectAction: in this case) receives an NSNotification object which would contain your object passed in the postNotification operation.
While a protocol would also work, it is more complicated to setup, in my view.
Have a look at the docs for Notification Center.

well you can create a #protocol like this:
#protocol MyChildViewControllerDelegate<NSObject>
#optional
- (void)tablView:(UITableView *)tv didSelectRowInChildViewControllerAtIndexPath:(NSIndexPath*)indexPath;
#end
now make a property of MyChildViewControllerDelegate in class ChildViewController and #synthesize it, like this:
#property(assign, nonatomic) id<MyChildViewControllerDelegate> delegate;
Now when making an instance of ChildViewController in parent classes assign the delegate by self like this:
ChildViewController *ch = [[ChildViewController alloc] initWithBlahBlah];
ch.delegate = self;
and implement the MyChildViewControllerDelegate methods.
now when you receive any UITableViewDelegate callback in ChildViewController tell your parent classes through delegates.
- (void)tableView:(UITableView *)tView didSelectRowAtIndexPath:(NSIndexPath *)iPath
{
[delegate tablView:tView didSelectRowInChildViewControllerAtIndexPath:iPath];
}
instead of making your own MyChildViewControllerDelegate you can use apple provided UITableViewDelegate too (as you find comfort with).
Hope it helps :)

Related

where can we write windowDidBecomeKey method for MainMenu.xib

i think this is simple question, but i m new to Cocoa. Where can we write -windowDidBecomeKey method for MainMenu.xib, so that when main window becomes the key the method should be invoked. Thanks.
First, go to your app delegate header file, and change something that looks like this:
#interface AppDelegate : NSObject <NSApplicationDelegate>
to this:
#interface AppDelegate : NSObject <NSApplicationDelegate, NSWindowDelegate>
Then go to the implementation file, and implement the method:
- (void)windowDidBecomeKey:(NSNotification *)notification {
// do something
}
Then right click on the window's title bar in interface builder and drag the dot next to Delegate onto your app delegate.
Alternatively, you can listen to the notification. Add this to the applicationDidFinishLaunching method:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(windowDidBecomeKey:)
name:NSWindowDidBecomeKeyNotification
object:_window];
The advantage of this approach is that you can name the listener method whatever you want.

How to Access a UIPickerView Value from Outside the View Controller that Owns the UIPickerView?

I have a picker View declared in the ViewControllerA (in Xib file). This Xib is loaded as a custom cell in ViewControllerB's tableView. If I change the value of Picker View, How can I access this changed value in ViewControllerB.
You can access the value of the UIPickerView by exposing a property on ViewControllerA... If there are set circumstances when viewControllerB needs to know the value, under the control of viewControllerB, then viewControllerB can check the property on viewControllerA at those times...
However, perhaps you're asking a more general question about communication - with the specific example of ViewControllerB needing to know about a change to the UIPickerView that's inside ViewControllerA...
This article on Communication Patterns is worth reading. It covers KVO, Notifications, Delegation, Blocks and Target/Action.
There's a flow-chart near the middle of the article that can help to evaluate what communication strategy to use in a given situation.
From what you've written, it sounds like you could use either KVO (key-value observing) or delegation.
I tend to use delegation in scenarios when one UIViewContoller wants to know about changes made in another UIViewController, e.g. when viewControllerC presents viewControllerD - and wants to know about changes made in viewControllerD.
In your case, you might use a delegate method along the lines of:
- (void)pickerViewValueDidChange:(NSString*)newValue;
That delegate method would be part of an #protocol. Something like:
#protocol ABCPickerViewDelegate
- (void)pickerViewValueDidChange:(NSString*)newValue;
#end
See Working with Protocols if this is new to you...
viewControllerB would conform to that protocol. viewControllerA would have a property that conforms to the protocol, something like:
#property (weak, nonatomic) id <ABCPickerViewDelegate> pickerDelegate;
Then, when the UIPickerView value changes within viewControllerA - it can call the delegate method... And, viewControllerB would then know that the change has occurred and what the value is.
you can get picker value from viewController A on ViewController B using various ways like static getter setter , App delegate class as I mentioned here using appDelegate class
In AppDelegate.h class declare properties with data type on your choice as i take it as NSString
#property (nonatomic, strong) NSString* pickerValue;
Get instance on AppDelegate class in ViewController A and B in viewDidLoad
self.appDelegate = (AppDelegate*)[UIApplication sharedApplication].delegate;
in picker delegate method set value as
-(void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row
inComponent:(NSInteger)component
{
self.appDelegate.pickerValue = self.pickerData[row];
}
In viewController B you can user value from
NSLog(#"ViewControllerB::picker valuer::%#",self.appDelegate.pickerValue);
More adaptable way:
Use NSNotification.
Implement the UIPickerViewDelegate method in ViewControllerA:
- (void)pickerView:(UIPickerView * _Nonnull)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component{
//get the data of the picker and save it in the dictionary
NSMutableDictionary *pickerData =[NSMutableDictionary new]
[pickerData setValue:[yourArrayForPicker objectAtIndex:row] forKey:#"value"];
//create and send the notification with the dictionary
[[NSNotificationCenter defaultCenter]
postNotificationName:#"XYYourNotification"
object:pickerData];
}
than in ViewControllerB register the listener for the notification:
//put this code where you want (maybe in view did load)
NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
[center addObserverForName:#"XYYourNotification"
object:nil
queue:nil
usingBlock:^(NSNotification *notification)
{
//this code will be called when the notification is received
NSDictionary* info = [notification userInfo];
NSLog(#"selected :%#", [pickerData valueForKey:#"value"]);
}];

Getting Key down event in an NSView controller

I'm trying to find a solution that allows me to get keydown events in a view controller.
I do not believe a view controller is part of the responder chain by default.
I would appreciate a sample of how to go about this. I have had trouble finding documentation I can understand on how to add the VC to the responder chain and get the events.
Thanks.
Miek
You can implement something like this:
-(void) globalKeyDown: (NSNotification *) notification
method in your controller class, and then just add the observer in awakeFromNib...or loadView method of your controller
- (void)awakeFromNib
{
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(globalKeyDown:)
name:#"my_keyEvent"
object:nil];
}
in your view class
-(void)keyDown:(NSEvent *)theEvent
{
[[NSNotificationCenter defaultCenter] postNotificationName:#"my_keyEvent"
object:theEvent
userInfo:#{#"sender":self}];
}
NSViewController doesn't have a default way to do this. However, you can achieve this through subclassing NSView. Here is the basic idea:
If you create a view subclass, you can set your view controller as a delegate and create a delegate method that handles events.
You can declare a delegate protocol at the start of your view header.
Import your view header in the view controller header. Declare the view controller as implementing the protocol.
In your view keyDown send the event to the delegate.
Another way is to post NSNotifications in your keyDown and observe and handle the notifications in your view controller. Other ways also exist.
NSView Subclass with Delegate method explained
Here is the delegation example with an NSView subclass which declares a protocol in its header with one required method, an IBOutlet id property that conforms to the protocol. The NSView subclass calls this method to its delegate whenever it wants to. If the delegate is nil, that's fine in Cocoa. Also note, tangentially, I have added IB_Designable and IBInspectable to the view's color properties. This allows setting them in IB and requires the 10.10 SDK.
The app delegate has imported the NSView subclass in the AppDelegate.m implementation file and adopted the protocol in the AppDelegate class extension at the top of the .m file. In the #implementation section it also implements the method.
Also note in IB, I added an NSView to the window, then set its class to the custom NSView subclass in the inspector. Finally, I set its eventDelegate IBOutlet to the AppDelegate proxy in IB.
Custom NSView subclass interface
#import <Cocoa/Cocoa.h>
#protocol EventDelegatingViewDelegate <NSObject>
- (void)view:(NSView *)aView didHandleEvent:(NSEvent *)anEvent;
#end
IB_DESIGNABLE
#interface EventDelegatingView : NSView
#property IBOutlet id<EventDelegatingViewDelegate> eventDelegate;
#property IBInspectable NSColor *fillColor;
#property IBInspectable NSColor *strokeColor;
#end
Custom NSView subclass implementation
#import "EventDelegatingView.h"
#implementation EventDelegatingView
- (BOOL)acceptsFirstMouse:(NSEvent *)theEvent {return YES;}
// The following two methods allow a view to accept key input events. (literally they say, YES, please send me those events if I'm the center of attention.)
- (BOOL)acceptsFirstResponder {return YES;}
- (BOOL)canBecomeKeyView {return YES;}
- (void)drawRect:(NSRect)dirtyRect {
[super drawRect:dirtyRect];
[self.fillColor set];
NSRectFill(self.bounds);
[self.strokeColor set];
NSFrameRect(self.bounds);
}
// Notice these don't do anything but call the eventDelegate. I could do whatever here, but I didn't.
// The NICE thing about delgation is, the originating object stays in control of it sends to its delegate.
// However, true to the meaning of the word 'delegate', once you pass something to the delegate, you have delegated some decision making power to that delegate object and no longer have any control (if you did, you might have a bad code smell in terms of the delegation design pattern.)
- (void)mouseDown:(NSEvent *)theEvent
{
[self.eventDelegate view:self didHandleEvent:theEvent];
}
- (void)keyDown:(NSEvent *)theEvent
{
[self.eventDelegate view:self didHandleEvent:theEvent];
}
#end
App Delegate (and eventDelegate!) implementation
#import "AppDelegate.h"
// Import the view class and if there were other files that implement any protocol
#import "EventDelegatingView.h"
// Declare protocol conformance (or more accurately, not only import that protocol interface, but say you're going to implement it so the compiler can nag you if you don't)
#interface AppDelegate ()<EventDelegatingViewDelegate>
#property (weak) IBOutlet NSWindow *window;
// For the simplest demo app we don't even need this property.
#property IBOutlet EventDelegatingView *eventDelegatingView;
#end
#implementation AppDelegate
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
// Insert code here to initialize your application
}
- (void)applicationWillTerminate:(NSNotification *)aNotification {
// Insert code here to tear down your application
}
// It's all right here. Receive a reference to a view and a reference to an event, then do as you like with them.
#pragma mark - EventDelegatingViewDelegate
- (void)view:(NSView *)aView didHandleEvent:(NSEvent *)anEvent
{
NSString *interestingEventNote;
switch (anEvent.type) {
case NSKeyDown:
case NSKeyUp:
{
// For simplicity we won't try to figure out the modifier keys here.
interestingEventNote = [NSString stringWithFormat:#"%# key was pressed.", anEvent.charactersIgnoringModifiers];
}
break;
case NSLeftMouseDown:
{
interestingEventNote = [NSString stringWithFormat:#"Left mouse down at point %# in window", NSStringFromPoint(anEvent.locationInWindow)];
}
break;
default:
break;
}
NSLog(#"%# %# aView=%#\n note=%#", self, NSStringFromSelector(_cmd), aView, interestingEventNote?interestingEventNote:#"Nothing worth noting");
}
#end
And that's it for the power of delegation. Basically it's callbacks of sorts and is a great way to build a class to enable it to defer something elsewhere as wanted. Moving some business logic to the right place in a fairly lazy and open and loosely coupled way.
NOTE: My code example shows using the app delegate. But the principal is the same. A view controller is little more than a delegate and you can add as much or as little as you like.
In your NSWidow (or NSWindowController) class implementation set your view controller as the first responder:
[self makeFirstResponder:yourViewControllerInstance];
You must, of course, make your NSViewController class return YES to the acceptsFirstResponder message.

How to Call a Controller Class (delegate) method from View Class Event in Objective C/Cocoa

lets say I have an NSWindow Class, that has several events for mouse and trackpad movements
for example this code
IMHO, I think this should be good programming, it is similar to pointing an action of a button to its method in controller.
in MyWindow.m Class I have (which in IB I have set the window class to it)
#implementation MyWindow
- (void)swipeWithEvent:(NSEvent *)event {
NSLog(#"Track Pad Swipe Triggered");
/* I want to add something like this
Note that, Appdelegate should be existing delegate,
not a new instance, since I have variables that should be preserved*/
[AppDelegate setLabel];
}
#end
and in My AppDelegate.h I have
#interface AppDelegate : NSObject <NSApplicationDelegate>
{
IBOutlet NSTextField *label;
}
-(void)setLabel;
#end
and in my AppDelegate.m I have
-(void)setLabel
{
[label setStringValue:#"swipe is triggered"];
}
I have tried #import #class, [[... alloc] init], delegate referencing in IB (I made an object class of MyWindow - thanks to the answer of my previous question )
that latter seems the closest one, it works if both of the classes are delegates, so I could successfully call the "setLabel" action from a button in a second controller (delegate class)'s IBAction method,
but this View Events seem not communicating with the delegate's action although their code is executing.
You are sending a message to the AppDelegate class, not the instance of your AppDelegate class which is accessible from the NSApplication singleton ([NSApplication sharedApplication])
[AppDelegate setLabel];
This is wrong, to get the delegate do this:
AppDelegate* appDelegate = (AppDelegate*)[[NSApplication sharedApplication] delegate];
then send the message to that instance:
[appDelegate setLabel];

Objective-C – Message a view controller from application delegate

I want to download some data in the application delegate in - application:didFinishLaunchingWithOptions:
After I have downloaded some data I want to set this data to an NSArray property in a view controller. If I have a synthesized property of NSArray (nonatomic, retain) called data I would like to do [viewController setData:downloadedData];
How would I call the active viewController instance from the application delegate?
My application structure is a Tab Bar Controller as root controller.
You'll want to use NSNotificationCenter which will essentially broadcast a message to all objects that have subscribed to that particular message.
In your view controller subscribe to the notification:
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:#selector(downloadedData:)
notificationName:#"DownloadedData"
object:data];
- downloadedData:(NSNotification *)notification {
self.data = notification.object;
}
And in your app delegate send the notification to the subscribers:
[[NSNotificationCenter defaultCenter]
postNotificationName:#"DownloadedData"
object:data];
Add delegateComplete property in your app delegate class:
//define ivar
id delegateComplete;
//define property
#property (nonatomic, retain) id delegateComplete;
//synthesize
#synthesize delegateComplete;
In init method or viewDidLoad of your viewController do following:
MainClass *appDelegate = (MainClass *)[[UIApplication sharedApplication] delegate];
appDelegate.delegateComplete = self;
replace MainClass with your app class. After downloading is complete, do following in your app delegate:
[delegateComplete loadingCompletedWithData:data];
Dont forget to add this method in your viewController:
- (void)loadingCompletedWithData:(NSData *)data
What happens is that your view controller registers to your app delegate. When loading completes, if your view controller has registered, call loadingCompletedWithData. Proper way of doing this would be through a protocol.