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;
Related
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
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.
I have a superclass A that has a BaseModalViewControllerDelegate protocol and a retain property for the id<BaseModalViewControllerDelegate> delegate.
I also have class B, subclass of A, that has a ModalLoginDelegate protocol and a retain property for the id<ModalLoginDelegate> delegate
Now, I set as setter method for B class delegate this method:
-(void)setDelegate: (id<ModalLoginDelegate>)delegate
{
_delegate = delegate;
[super setDelegate: (id<BaseModalViewControllerDelegate>)delegate;
}
So, there is the RootViewController that implements both protocol, but it inits only B class and it set itself as delegate only for B class because it doesn't know that B class is a subclass of A.
Do you think that this is a correct way to set RootVC as delegate for both protocol? Thanks
ADDING
Setting the protocol of B class as inherited from A class:
#protocol ModalLoginDelegate <BaseModalDelegate>
// delegate method of subclass
#end
Now, my RootVC has not to set itself as delegate of BaseModalViewController. But now, when in my B class I want to call the delegate method of the superclass I'm doing this
if (self.loginDelegate)
{
[self.loginDelegate baseModalViewController: self willDismiss: YES];
}
I think that this is not a very clean way, so I created a public method in the superclass -(void)pressedCloseButton; that it will do this
-(void)pressedCloseButton
{
if (self.delegate)
{
[self.delegate baseModalViewController: self willDismiss: YES];
}
}
And in the subclass:
-(IBAction)closeBtnPressed: (id)sender
{
[super pressedCloseButton];
}
Do you think is right?
A better design is to implement a separate delegate property for your subclass, say loginDelegate. It isn't very good OO design to change the type of a property in a subclass. Most OO languages won't even allow it.
This also ensures that the consuming class is "aware" that there are two separate delegate protocols involved.
RootVC will need to set itself as both delegates if it needs to implement both protocols. You can't expect the class not to know which delegate protocols it needs to implement. If RootVC thinks it is only dealing with the base class then it won't set loginDelegate and won't implement the methods in that protocol.
I don't disagree with Paulw11's answer here at all, but it interesting to note that Apple themselves do this.
example. UIScrollView has a delegate property
#property (weak, nonatomic) id <UIScrollViewDelegate> delegate;
and a subclass, UITableView, has a delegate property
#property (weak, nonatomic) id <UITableViewDelegate> delegate;
When we declare a protocol in ObjC we usually have that protocol extend the < NSObject > protocol.
#protocol BaseModalViewControllerDelegate <NSObject>
-(void)doSomething;
-(NSString *)titleForThing;
#end
Now this protocol above has not only the methods prototyped here but also those in the < NSObject > protocol. Its is very much like this protocol is a 'subclass' of the other protocol, inherits all its stuff too.
If you did this with your second protocol
#protocol ModalLoginDelegate <BaseModalViewControllerDelegate>
-(void)doAnotherThing;
-(NSString *)titleForTheOtherThing;
#end
then what you've done here would be absolutely in line with what Apple have done with UITableView and UIScrollView, because a pointer of type id< ModalLoginDelegate > is always also an object of type id< BaseModalViewControllerDelegate > , just as a UIButton* will always be able to be passed in as a UIView* ...
But without doing this there is a fundamental problem in your
-(void)setDelegate:(id<ModalLoginDelegate>)delegate
method there, because you are assuming this object complies with the BaseModalViewControllerDelegate protocol when the only thing you know for sure is that it complies with the ModalLoginDelegate protocol. Some inherited method may call on self.delegate with a BaseModalViewControllerDelegate method which self.delegate does not respond to..
I hope this helps :)
I've got a ViewController that has a UITableView within it. When I'm watching tutorials people are using things like this:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return _Title.count;
}
How am I able to generate the stubs without firstly creating the class with them in. When I made the class I selected it as a UIViewController. I've been playing around trying to auto generate the stubs but all to no avail.
Simply add the UITableViewDataSource (and most likely the UITableViewDelegate) to your UIViewController declaration. Example:
// MyViewController.h
#interface MyViewController : UIViewController <UITableViewDataSource, UITableViewDelegate>
// ...
#end
After that your implementation file MyViewcontroller.m should help you with the code completion.
One note: don't forget to set yourself as dataSource:
_tableview.dataSource = self;
If you added the tableview by code, you need to create a property (weak) in order to have a reference to your table view after adding it to your view controller's subview. If you add it by using interface builder, you need to create a iboutlet property that will allow you to "bind" your table view property with the xib/storyboard file representing your view controller. Alternatively, you can use UITableViewController as the parent class of your view controller. This class already has a property to access the table view in your view controller.
Tell your controller that you need to conform to the table view protocols and they will start to auto-complete when you try to type them in. You can check the docs of a protocol to find the available methods. Checking the UITableView docs would tell you about the relevant data source and delegate:
The data source must adopt the UITableViewDataSource protocol and the delegate must adopt the UITableViewDelegate protocol.
In your header file:
#interface MyViewController : UIViewController <UITableViewDataSource, UITableViewDelegate>
You have a couple of options.
You could make your class inherit from UITableViewController instead of UIViewController. This will give you a tableView so you don't need to make one.
Or...
Your UIViewController could implement the protocols UITableViewDataSource and UITableViewDelegate. Then set the dataSource and delegate properties of your table view to self (your view controller containing the table).
-First of all you may need to add datasource and delegate of UITableViewController in your UIViewController header file
#interface MyViewController : UIViewController <UITableViewDataSource, UITableViewDelegate>
and then implement the required and optional methods to populate the data in your _tableView.
Sample Code for TableView demonstration by Apple:
https://developer.apple.com/library/ios/samplecode/TableViewSuite/Introduction/Intro.html#//apple_ref/doc/uid/DTS40007318
The title is what I think I need but i will go back one step. I want to create a class which handles certain things in an iOS app. This class might be called by multiple UIViewcontrollers in an iOS app. The class may need to show a UIView at some stage for user input. So my question is how can I show a UIView when I don't know which subclass of UIViewController is calling it? To what can I add the UIView from this class?
I suppose there are two possible answers either the class finds the current UIViewController or the calling subclass of UIViewController passes itself to the class so the class knows.
How is this supposed to be done.
Thanks guys for your help.
I'm going to expand on #ericleaf's comment regarding using a protocol and subclasses. It sounds like you are asking the following:
How can I create a resusable, generic class that presents a view
within a UIViewController subclass?
A great way to do this is to define a protocol in your generic class and have your view controller subclasses support this protocol. The protocol defines an interface for your custom class to comunicate with it's delegate, in this case a UIViewController subclass. Other than the protocol, the objects don't need to know anything else about the implementation of each other.
Any information your custom object needs to be able to present views within it's delegate would be passed via protocol methods. The specifics of the protocol are up to you based on your needs. You could have the custom object "ask" the delegate for information (e.g. what view should I put a subview in?) or you could have the protocol provide information to the delegate and let the delegate deal with it (e.g. here is a subview you can put wherever you want).
There is a lot of great documentation on protocols available on SO and elsewhere. This is long enough already so I kept the example fairly simple.
custom class .h file with protocol definition
// my custom class that adds adds a view to a view controller that supports it's protocol
// forward class definition for the protocol
#class MyAwesomeObject;
#protocol MyAweseomeObjectDelegate <NSObject>
- (UIView *)viewForMyAwesomeObject:(MyAwesomeObject *)awesomeObject;
#end
// this could be defined such that the delegate *must* be a UIViewController. I've left it generic.
#interface MyAwesomeClassObject : NSObject
#property (nonatomic, weak) id <MyAwesomeObjectDelegate> delegate;
#end
custom class .m file
// MyAwesomeObject.m
#import "MyAwesomeObject.h"
#implementation MyAwesomeObject
// this is a dumb example, but shows how to get the view from the delegate
// and add a subview to it
- (void)presentViewInDelegate
{
UIView *containingView = [self.delegate viewForMyAwesomeObject:self];
if (containingView) {
UIView *subview = [[UIView alloc] initWithFrame:containingView.bounds];
subview.backgroundColor = [UIColor redColor];
subview.autoresizingMask = UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleHeight;
[containingView addSubview:subview];
}
}
MyViewController .h using the custom object
// MyViewController.h
#import "MyAwesomeObject.h"
#interface MyViewController : UIViewController <MyAwesomeObjectDelegate>
#property (nonatomic, strong) MyAwesomeObject *awesomeObject;
#end
MyViewController .m using the custom object
// MyViewController.m
#import "MyViewController.h"
#implementation MyViewController
- (void)init
{
self = [super init];
if (self) {
_awesomeObject = [[MyAwesomeObject alloc] init];
_awesomeObject.delegate = self;
}
return self;
}
// MyAwesomeObjectDelegate
- (UIView *)viewForMyAwesomeObject:(MyAwesomeObject *)awesomeObject
{
return self.view;
}
You can get the class into a string and do a compare.
For example, lets assume your custom UIViewController subclass is CustomViewCon and the UIViewController object reference is myUnknownClassObject, then:
NSString *classString = NSStringFromClass([myUnknownClassObject class]);
Then you can:
if([classString isEqualToString:#"CustomViewCon"]){
//do something like maybe present a particular view
myUnknownClassObject.view = myCustomView; //or anything..
}
Similarly you can check for any class.
Edit: According to the suggestions from comments, you could also do the following(better way):
if([[myUnknownClassObject class] isKindOfClass:[CustomViewCon class]]){
//same as before
}
Why wont you use a block for this?
BaseViewController.h:
#property (copy) void (^addViewBlock)();
- (IBAction)showViewWhenNeeded;
BaseViewController.m:
- (IBAction)showViewWhenNeeded
{
if (self.addViewBlock)
self.addViewBlock();
}
And in your child class, set that block's actions, and call the method when you feel like you should put up a view.
ChildViewController.m
// within some method, propably init or smth
[self setAddViewBlock:^{
[self.vied addSubView:...];
}];
// when need to actually add the view
[self showViewWhenNeeded];