UIButton addTarget in different ViewController - ios

In my project, I have two ViewControllers - mapViewController and dataViewController.
In mapViewController, I have outlets for two buttons :
#property (weak, nonatomic) IBOutlet UIButton *previousButton;
#property (weak, nonatomic) IBOutlet UIButton *nextButton;
For fetching mapViewController in dataViewController,
self.MapViewController = ((OTPAppDelegate *)[[UIApplication sharedApplication] delegate]).mapViewController;
Using the above technique, I can manipulate the properties of mapViewController inside dataViewController by accessing self.MapViewController.property
However, if I wish to add a target for the two buttons inside dataViewController using the following code:
[self.MapViewController.previousButton addTarget:self action:#selector(doNothing:) forControlEvents:UIControlEventTouchDown];
It throws a BAD access error. I was wondering what needs to be fixed, in order to achieve the desired button click behavior.

Create a protocol in MapViewController
#Protocol prtocol_name <NSObject>{
-(void)method_name;
#end
create an object for protocol in MapViewController.
#property(nonatomic) id< prtocol_name> delegate;
in button methods implementation call protocol method like following
[self.delegate method_name];
And finally implement protocol method in DataViewController.
Thanks

If you want the target/selector in different view controller, then pass the delegate parameter as other view controller's instance. For eg:
[self.MapViewController.previousButton addTarget:otherControllerInstance action:#selector(doNothing:)
forControlEvents:UIControlEventTouchDown];
Detailed Explanation:-
You have two classes named FirstVC and SecondVC. A button is present in FirstVC, on which you want to add target in SecondVC.
[button addTarget:objSecondVC action:#selector(doSomething:)
forControlEvents:UIControlEventTouchDown];

I hope you have create property of mapViewController into dataViewController.
If you choose wrong property attributes then it can raise error you got.
Another possibility is, MapViewController property not assigned/initialized properly and it is nil while you are trying to add target of its subview.
The best way to get callback event is to use delegate.
Below are some information on how delegate works:
Delegate are function pointers. Using it, one can call another class' function easily.
To create delegate, common procedure is to, first create protocol and add relevant methods in it (in the class you want to initiate delegate method). This methods can be implemented by class that adopts protocol.
You also need to create generic property of protocol type called delegate property. This will be assigned to instance of class that conforms to protocol.
In your case, class mapViewController define some protocol in it. Here, dataViewController conforms class mapViewController's protocol.
Now, class dataViewController has object defined of class mapViewController in it. In class dataViewController, here we need to assign class mapViewController's delegate to instance of dataViewController(self). (now in class mapViewController, delegate property contains instance of dataViewController and one can easily call protocol method implemented in class dataViewController from class mapViewController).
I hope this will help you.

Related

Changing IBOutlet UIObject in ViewController from another class using Objective C (iOS)

I'm a beginner in iOS development and cannot get this part working. The objective is simple: I have a class named TCPComm which connects to a server and sends data periodically. In my Storyboard I have a view containing some buttons and a textfield. The idea is to change the state of the IBOutlet UIButtons and UITextField based on the received data from the server (from another class).
I have tried using properties in different ways but none of them worked.
Any help on the best way to do this please?
The best way to achieve this is using Delegates.
In your TCPComm header file declare protocol
#protocol tcpCommDelegate <NSObject>
-(void)callBackMethod;
#end
In the interface declare a public property
#property (nonatomic, weak) id<tcpCommDelegate> delegate;
Now, call the method declared in protocol whenever you received some data in your TCPComm class like below
if(delegate && [delegate respondsToSelector:#selector(callBackMethod)])
[delegate performSelector:#selector(callBackMethod)];
Now, in your viewcontroller class make sure you imported TCPComm class and it accepts the tcpCommDelegate protocol like below
#interface YourViewController : UIViewController <tcpCommDelegate>
In viewDidLoad method of ViewController create an instance of TCPComm class and assign its delegate property to self
TCPComm *tcpcomm = [[TCPComm alloc]init];
tcpcomm.delegate = self;
That's, it now you can implement the callBackMethod body in your viewcontroller and change the properties of whatever UIObjects you want. This method will be called whenever there is new data fetched by your TCPComm class, if you have called the 3rd code snippet at the right time i.e when data fetch completed

How do I use delegation in iOS for slide out menu?

I am trying to figure out delegation in iOS. Basically, I have classA which contains methodA. I also have classB which I would like to call methodA from.
To be specific, I have a class called ViewControllerRootHome and class called ViewControllerRootHomeLeftPanel. The ViewControllerRootHome has a method in it called, movePanelToOriginalPosition I would like to call this method from the ViewControllerRootHomeLeftPanel class.
Any help would be greatly appreciated. Ohh forgot to mention I'm still using Objective-C for the project.
I'll give this an attempt.
Let's say you've got a ViewController called ViewControllerA, and another ViewController called ViewControllerB. We want to call a method inside A from B. How are we going to achieve this?
Simple. We will define a protocol inside B that A will comply to. Let me do that right here.
#import ...
#protocol myProtocol; // Declare Protocol
#interface ViewControllerB : UIViewController
#property (nonatomic, weak)id <myProtocol> myDelegate; // Create Delegate property
#end // Notice this is AFTER the #end of the #interface declaration
#protocol myProtocol <NSObject> // Define Protocol
-(void)doSomething;
#end
Okay, now you have defined a protocol called myProtocol that you wish to use inside ViewControllerA
Let us use it there. We will have to do several things: One, comply to the protocol. And two, set our current VC as it's delegate!
#import ...
#import "ViewControllerB" // IMPORT the VC with the Protocol
#interface ViewControllerA : UIViewController <myProtocol> // Conform to Protocl
#property (nonatomic)ViewControllerB *viewControllerB;
#end
Notice I've defined a property of type ViewControllerB. You will need to have a reference to ViewControllerB in some shape or form. This is usually easy to achieve because you normally create an instance of ViewControllerB from ViewControllerA. Otherwise it will need to be set externally or passed to ViewControllerA upon initialization and you set it as a property there.
Inside ViewControllerA.m, set ViewControllerA as it's delegate:
self.ViewControllerB.myDelegate = self;
Now, all you have to do is define the method from the protocol inside ViewController A so it can be called:
-(void)doSomething
{
...
}
This is all you need to do. However, please note that if you have TWO ViewControllers complying to each other's protocols, you will likely have to declare the protocols inside their own header files.
Edit: How to call the method.
If you want to call the method defined inside the protocol. You will do so inside ViewControllerB, like so:
if ([self.myDelegate respondsToSelector:#selector(doSomething)])
{
[self.myDelegate doSomething];
}

Obj-C/iOS: Setting delegations programmatically versus through the NIB

Is there a difference between delegating in iOS like this:
#interface JOLoginHomeVC : MCViewController <UITextFieldDelegate>
versus in the NIB, delegating each individual UI element by dragging it to the objects owner?
Yes, this:
#interface JOLoginHomeVC : MCViewController <UITextFieldDelegate>
Sets JOLoginHomeVC to conform to UITextFieldDelegate protocol.
Setting a delegate in InterfaceBuilder actually assigns the delegate. It serves the same function as assigning it like:
someTextField.delegate = self;

Can't send a UIButton event to other view controller correctly

Can someone provide some examples and formal patterns for one-to-one event transfer between UIViewControllers? I think NSNotificationCenter is not applicable for this use case because it is based on event bus and broadcasting patterns for broad state changes and that's why should be used for one-to-many transfers. I know that KVO is not applicable in this case at all too, because it is usually used for communication between model and controller layers in classical MVC realm. So now I know only one way for one-to-one event transfers: delegate pattern. But may be there are even more elegant and simple not easy solutions.
For example:
In the view the action is sent to:
#protocol MapViewDelegate <NSObject>
#required
-(void)MapImageButtonClicked:(UIButton*)sender;
#end
#interface MapView : UIView
{
UIButton *mapButton;
id mapViewDelegate;
}
#property(nonatomic,retain) id mapViewDelegate;
#property(nonatomic,retain) UIButton *mapButton;
.m
[mapButton addTarget:self.delegate action:#selector(mapImageButtonClicked:) forControlEvents:UIControlEventTouchUpInside];
In the view the action will be sent from:
#import "MapView.h"
#interface MapViewController : UIViewController<MapViewDelegate>
{
}
.m
MapView *map = [[MapView alloc] init];
map.delegate = self;
-(void)MapImageButtonClicked:(UIButton*)sender
{
//implement the necessary functionality here
}
Hope you get the idea. Please implement it according to your situation.
You can put that method in a protocol in your view's interface:
#protocol MyViewWithButtonDelegateProtocol<NSObject>
-(void)myButtonAction:(id)sender; #end
you put a new property of NSObject or UIView type called delegate in your view
that has the button.
you make your view that has to handle the action comply to that
protocol and when it inits the view assign the delegate property to
self.
you implement myButtonAction in your view controller implementation.
and now you just do [myButton setTarget:delegate
action:#selector(myButtonAction:)
forControlEvents:UIControlEventTouchUpInside];.

Delegate changes unexpectedly

I have one delegate ExampleDelegate and I have one UITableViewController and detail UIViewController both implementing that delegate:
#interface ClassA : UITableViewController <ExampleDelegate>
and:
#interface ClassB : UIViewController <ExampleDelegate>
and delegate:
#protocol ExampleDelegate <NSObject>
-(void)notifyUser;
#end
#interface Example : NSObject
#property (nonatomic, retain) id delegate;
-(id)initWithDelegate:(id<ExampleDelegate>) delegate;
#end
At first I'm initializing the instance of delegate from ClassA and its working fine but once I navigate to the ClassB there also I'm creating one instance for the delegate when I came back to the ClassA the delegate instance get retained from ClassB, so the function inside the ClassB gets called always instead of ClassA's function.
Can anyone point me out what I'm doing wrong and how to get this working?
You need to make sure that you nil out your delegates once they're not needed - this will help you achieve 2 things:
Pass the correct class the proper delegation that it needs to handle when it is visible.
Prevent crashes so that when a class gets deallocated and is defined as a delegate, you might get a "message sent to deallocated instance" crash.
In your case, once ClassB goes back to ClassA, you must make sure that ClassA has that delegate defined and that ClassB's delegate property is nil.
Do you intend for Class B to be the delegate of Class A so that it can call back with the -notifyUser method? If that's the case, you don't need the initializer and you should have the delegate property on class B declared as...
#property (assign, nonatomic) id <ExampleDelegate> delegate;
Then, if I'm following you correctly, when you create the detail controller (Class B), you will set it's delegate property to self (Class A). Now when Class B needs to communicate back up to Class A it simply needs to call -notifyUser on it's delegate like so...
// Something happened that you want to communicate back up the chain
[self.delegate notifyUser];
When you use this pattern, the protocol is typically declared on the class that also implements the property for it to be set on, in this case that is Class B.
Edit:
In both classes I have declared delegates as strong variable which needs to be declared as weak as I came through some doc so by changing this it solved the problem.
And thanks for you all the answers which helped a lot.

Resources