OK, real quick question, if I want to pass a view controller in a method, which would be the correct way(apparently, both seem to work) -
-(void)fetchedDataN:(UIViewController *)response
or
-(void)fetchedDataN:(ViewController *)response
EDIT: Here is the Complete Code to clarify things
- (IBAction)nextButton:(id)sender {
activityN.hidden=NO;
[activityN startAnimating];
[self.view bringSubviewToFront:activityN];
[audioFile stop];
NSLog(#"HELL O");
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
Page04ViewController *viewControl=[[Page04ViewController alloc]initWithNibName:nil bundle:nil];
[self performSelectorOnMainThread:#selector(fetchedDataN:)
withObject:viewControl waitUntilDone:YES];
}); }
-(void)fetchedDataN:(UIViewController *)response{
if ([self interfaceOrientation] == UIDeviceOrientationLandscapeLeft) {
[self presentViewController:response withPushDirection:#"fromTop"];
// NSLog(#"landscape left");
}
else if([self interfaceOrientation] == UIDeviceOrientationLandscapeRight)
{
[self presentViewController:response withPushDirection:#"fromBottom"];
// NSLog(#"landscape right");
} }
Presumably ViewController is a subclass of UIViewController...
If you try to pass a UIViewController to the second method, that won't work (the compiler will complain) because it can't guarantee that the instance you're passing is of the correct class.
If you try to pass ViewController to the first method that will work because the compiler can make the guarantee.
The correctness of the method definition depends on your intentions - without knowing that, neither and both are correct, they are just different...
For your updated question, if the method is only presenting the view controller then using UIViewController can be considered correct as it is the most generic and flexible option. If you wanted to change the method in future to add other features which require response to be a specific subclass then the compiler will tell you that it can't guarantee the class is of the correct type.
This is right:
-(void)fetchedDataN:(UIViewController *)response.
Then you can
-(void)fetchedDataN:(UIViewController *)response
{
if ([controller isKindOfClass:[ViewControllerSubclass class]])
{
//The controller is a ViewControllerSubclass or a subclass of ViewControllerSubclass your code here
}
if ([controller isKindOfClass:[MyViewControllerSubclass class]])
{
//The controller is a MyViewControllerSubclass or a subclass of MyViewControllerSubclass your code here
}
}
If you want to pass a UIViewController; use the first method.
I never heard of the type 'ViewController'. It it your own type? If you always want to be sure the passed variable is of type 'ViewController', use the second method.
It depends if you want to pass only ViewController and/or it's descendants or UIViewController and all of it's descendants. UIViewController will be more generic way as (I assume) ViewController is subclass of UIViewController and probably you won't extend ViewController anymore. Everything depends on context and if you need to use any specific method/property defined in ViewController or you just want to receive any view controller subclassing UIViewController.
Related
Following is my code:
- (void)getAge:(NSString *)age getGender:(NSString *)gender getLocation:(NSString *)location getPrefAge1:(NSString *)age1 getPrefAge2:(NSString *)age2 getPrefGender:(NSString *)prefGender getPrefLocation:(NSString *)prefLocation getImage:(UIImage *)image
{
self.textFieldAge.text=age;
self.textFieldGender.text=gender;
self.textFieldLocation.text=location;
self.textFieldPrefGender.text=prefGender;
self.textFieldPrefAge1.text=age1;
self.textFieldPrefAge2.text=age2;
self.textFieldPrefLocation.text=prefLocation;
self.imageProfilePic.image=image;
}
I get these value from another viewController I want to set these textFields with the above values. Can anyone help?
I am new to iOS.
This the method where I am calling the above method :
-(IBAction)editpro:(id)sender
{
ProfileEditViewController *profileEdit= [[ProfileEditViewController alloc] initWithNibName:#"ProfileEditViewController" bundle:nil];
[profileEdit getAge:_labelAge.text getGender:_labelGender.text getLocation:_labelLocation.text getPrefAge1:_labelPrefAge1.text getPrefAge2:_labelPrefAge2.text getPrefGender:_labelPrefGender.text getPrefLocation:_labelPrefLocation.text getImage:self.profilepic.image ];
NSLog(#"Gender :%#",_labelGender.text);
[profileEdit.saveProfile setHidden:YES];
[self presentViewController:profileEdit animated:YES completion:nil];
}
Your code should work if:
Text fields are hooked up in Interface Builder or correctly initialized programmatically.
You set those properties in the main thread.
You should check the following things, your code will not work if one of these is the case or can be all too :
Text fields should be linked to their IBoutlets, that is self.textFieldAge should not be nil.
Also, the object or view controller you are calling this function of getAge, check if that object is not nil.
I am trying to do some debugging on a project I am working on, and would like to know the kind of ViewController a specific variable is being assigned. So I created an if statement like so:
if ([controller isSubclassOfClass:[UINavigationController class]]) {
NSLog(#"It's a navigation controller!");
}
'controller' is created just above using
DetailViewController *controller = (DetailViewController *)[[segue destinationViewController]topViewController];
DetailViewController is a simple class inheriting from UIViewController. However I'm getting a compiler error saying No visibile #interface for 'DetailViewController' declares the selector 'isSubclassOfClass:'
How is that possible? When I tripleTap the reference for isSubclassOfClass it says it's a class method defined in NSObject. How is it possible that DetailViewController doesn't know that selector since all objects inherit from NSObject??
Both other responders gave you a correct answer to your question, but I wanted to clarify something.
Methods who's declaration start with a + are CLASS methods. The class object implements those methods, not instances of that class. So this method:
+ (BOOL)isSubclassOfClass:(Class)aClass
Is a class method.
You'd use it like Jeffery Thomas demonstrated in the first part of his answer:
[[controller class] isSubclassOfClass:[UINavigationController class]]
The [controller class] bit fetches the class object for the controller object, and then sends that class object the isSubclassOfClass message.
In contrast, the method isKindOfClass:
- (BOOL)isKindOfClass:(Class)aClass
...is an instance method. You can tell because it's declaration starts with a "-" instead of a "+". Learn to look for the "+" or "-" at the beginning of every method declaration to see if it's a class method or an instance method. And if the compiler isn't letting you send a message that you see in the docs, go back and double-check. I've been programming in Objective-C for quite a few years now and this still bites me occasionally.
You send the isKindOfClass message to an instance of the object you want to test for class membership:
[controller isKindOfClass:[UINavigationController class]]
The standard way of doing this is -isKindOfClass:, not +isSubclassOfClass:, but it's possible to use +isSubclassOfClass:.
Using the instance method -isKindOfClass:
if ([controller isKindOfClass:[UINavigationController class]]) {
NSLog(#"It's a navigation controller!");
}
Using the class method +isSubclassOfClass:
if ([[controller class] isSubclassOfClass:[UINavigationController class]]) {
NSLog(#"It's a navigation controller!");
}
When I tripleTap the reference for isSubclassOfClass it says it's a class method defined in NSObject.
It's a class method, and you're using it on an instance. Try isKindOfClass.
Trying to get a better understand of iOS delegation. I'm using UIImagePickerController as a reference but what's a good code example to use a delegate to dismiss my view controller?
I have a TabBarViewController that calls AViewController and want to use delegation to dismiss AViewController.
In this case, the idea is that the class that presented the new viewcontroller, should also be the one that dismisses it. The AViewController may have no clue how it was presented, so it wants to let the presenter, the TabBarViewController, handle the dismissal in whatever form needed. So, we need to define a protocol, say AViewControllerProtocol, which allows there to be a standard definition of the dismissal call:
This goes in AViewControllerProtocol.h:
#protocol AViewControllerProtocol <NSObject>
#required
- (void) dismissWithDone:(UIViewController *)viewController;
- (void) dismissWithCancel:(UIViewController *) viewController;
#optional
- (void)dismissWithDone:(UIViewController *)viewController andPlaySoundFile:(NSString *)soundPath;
#end
This goes in a file called AViewControllerProtocol.h. Both TabBarViewController.m and AViewController.m will import it. Think of this as a pact that any user of AViewController must agree to before it can utilize it. Similarly, you can't use UITableView without making a pact to observe the UITableViewDelegate and UITableViewDataSource protocols (those are two separate protocols).
Any class which wants to present AViewController, can see from the protocol definition that AViewController expects two required methods in order to be properly dismissed. It also has an optional third method that requests the presenter to play a sound after dismissing.
In order for AViewController to do its piece of this pact, it needs to get and store one piece of information about who the presenter is. That is called the delegate. The delegate is a property defined in the #interface of AViewController, in AViewController.h:
This goes in AViewController.h:
#property (nonatomic, weak) id<AViewControllerProtocol> delegate;
Now, the presenter, TabBarViewController, needs to do its bit. It needs to define the two required methods, plus maybe the optimal one, and it also needs to set the delegate value:
In TabBarViewController.m, in the #implementation:
This goes in TabBarViewController.m:
- (void) dismissWithDone:(UIViewController *)viewController
{
[self saveData:viewController.dataToSave]; // this could be the results that need to be saved
[viewController dismissViewControllerAnimated:YES completion:^{
;
}];
}
- (void) dismissWithCancel:(UIViewController *)viewController
{
// don't save data
[viewController dismissViewControllerAnimated:YES completion:^{
;
}];
}
The delegate value is set where AViewController is first created and/or before it is presented:
This also goes in TabBarViewController.m:
AViewController * aVC = [AViewController.alloc init];
aVC.delegate = self;
aVC.data = ...; // this may be the data you want changed by the VC
[self presentViewController:aVC animated:YES completion:^{
}];
Setting the delegate here is the only connection that the AViewController class has with its presenting viewController - which is the whole point here: child classes really shouldn't have to know a whole lot about the classes that utilize them.
Lastly, the AViewController class needs to add the following in order to call, via the delegate, back to the presenting class - so in AViewController.m:
This goes in AViewController.m:
-(IBAction)userHitButton:(id)sender
{
if (sender == doneButton) {
if ([_delegate respondsToSelector:#selector(dismissWithDone:)]) {
[_delegate dismissWithDone:self];
}
} else {
if ([_delegate respondsToSelector:#selector(dismissWithCancel:)]) {
[_delegate dismissWithCancel:self];
}
}
}
If you are wondering why the calling class has to:
aVC.delegate = self;
It is because there are situations where the class that actually creates a child class, isn't the one that will handle the delegate calls. In that case, instead of self, you put an instance of a class that will handle the delegate callbacks. For example, lets say that you have AViewController, BViewController and CViewController. They all get data from the user that needs to be saved. A class by the imaginary name of ABCDataHandler could be the one that can handle the dismissWithDone: and dismissWithCancel: callbacks and saves the data as needed, and TabBarViewController can stay out of any data handling activity.
That's to put it as simply as I can. I hope I haven't made any typos here :-)
So I have taken the plunge into Objective-C programming for iOS development. I have a little app that I am working on, nothing special, but something to help teach me the ropes. The problem I am having is as follows:
Currently, I have two classes. The first being
ViewController
and the second being one that I created myself called
UserDecision
The View controller shows what is on screen, and UserDecisions currently takes the information from buttons pressed on screen, and performs the proper logic on it while working with my model class. My issue is, that I have an update UI method in UserDecision which needs to update the button properties (text, visibility, etc.) in ViewController if certain events take place. Because of this, I can't user an instance of ViewController because I won't be able to access the buttons on screen. So for this I created a delegate system:
#protocol updateUIDelegate <NSObject>
-(void)hideAll;
-(void)makeBackVisible;
-(void)updateOutput:(NSString *)output;
-(void)updateChoices:(NSString *)choices;
-(void)updateTrueButton:(NSString *)trueString;
-(void)updateFalseButton:(NSString *)falseString;
-(void)removeChoiceFromArray;
#end
The above protocol is defined in UserDecision.h, and then I assigned my ViewController as my delegate:
#interface ViewController : UIViewController <updateUIDelegate>;
And then I flush out said methods in my ViewController.m:
#pragma - updateUIDelegates -
//Called when the last screen is displayed
-(void)hideAll{
[_trueButton setHidden:true];
[_falseButton setHidden:true];
[_choicesText setHidden:true];
[_backButton setHidden:true];
[_resetButton setHidden:false];
}
//Makes back button visible
-(void)makeBackVisible{
[_backButton setHidden:false];
}
//Updates the text on the false button
-(void)updateFalseButton:(NSString *)falseString{
[_falseButton setTitle:falseString forState:UIControlStateNormal];
}
//Updates the text on the true button
-(void)updateTrueButton:(NSString *)trueString{
[_trueButton setTitle:trueString forState:UIControlStateNormal];
}
//Updates the output text box
-(void)updateOutput:(NSString *)output{
[_outputText setText:output];
}
//Updates the choices textbox
-(void)updateChoices:(NSString *)choices{
if(!choicesArray){
choicesArray = [[NSMutableArray alloc] initWithCapacity:4];
}
//If this is the first button press, add string to array and display
if([_choicesText.text isEqualToString:#""]){
[choicesArray addObject:choices];
_choicesText.text = [NSString stringWithFormat:#"%#", choices];
}
//Otherwise, add the new string to the array, and print the array
//using a comma as a way to concatinate the string and get rid of
//the ugly look of printing out an array.
else{
[choicesArray addObject:choices];
[_choicesText setText:[NSString stringWithFormat:#"%#",[choicesArray componentsJoinedByString:#", "]]];
}
}
//Removes the last choice from the array
-(void)removeChoiceFromArray{
[choicesArray removeLastObject];
[_choicesText setText:[NSString stringWithFormat:#"%#", [choicesArray componentsJoinedByString:#","]]];
}
This allows me to call theses methods by sending them as a message to self.delegate in my UserDecision class when needed.
This is my current setup. My issue has become that I want to create a modal seque view that pops up at the end (after a user presses a button to bring up the view), and which can be dismissed afterward. The problem I have is that this view, from the reading and research I have done online, can only be dismissed through delegation, unless I want things to get nasty. Now, I tried to implement this information in my class, but then I read that a class can only be a delegate to one other class. And since my ViewController(which is my main window) is already a delegate of my UserDecision class, I can't make it a delegate of the new View I have created, and thus can't dismiss the view. So, I am here to ask for your help. How can I go about solving this issue?
Also, for more of my code, should you want to have a look, here is a link to my gitHub: https://github.com/Aghassi/Xcode/tree/master/Bubble%20Tea%20Choice/Bubble%20Tea%20Choice
I read that a class can only be a delegate to one other class. And
since my ViewController(which is my main window) is already a delegate
of my UserDecision class, I can't make it a delegate of the new View I
have created
I don't believe that's true. You can make ViewController implement many different protocols, therefore being delegates to different classes/objects.
For example: (UITableViewDelegate and UITextViewDelegate can both be implemented on the same ViewController for 2 separate objects (UITextView and UITableView).
As for using delegation to close modal windows, another option is to use blocks as well.
It is possible for a viewController to dismiss itself. Just hook up a dismiss button to a function that calls something like:
[self.presentingViewController dismissViewControllerAnimated:YES];
or
[self.navigationController popViewControllerAnimated:YES];
Dismissal can be done with a delegate pattern but it is not required for everything.
You viewController class can be a delegate of multiple objects so it should be able to dismiss the modal view. The only issue is if its a delegate of multiple objects of the same class you may need to check which object is calling it.
Look at the tableView delegate methods as an example, the tableView calls them passing itself as the first parameter.
To dismiss a custom modal view you would define a different protocol anyway so there would be no problem with calling the same method.
See example below:
#protocol OSImageViewControllerProtocol <NSObject>
- (void)dismissImageViewer;
#end
#implementation OSImageViewController
- (void)loadView
{ //LOG(#"loadView called");
scrollView = [[ImageScrollView alloc] init];
scrollView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
UITapGestureRecognizer *doubleTapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(scrollViewDoubleTapped:)];
doubleTapRecognizer.numberOfTapsRequired = 2;
doubleTapRecognizer.numberOfTouchesRequired = 1;
[scrollView addGestureRecognizer:doubleTapRecognizer];
self.view = scrollView;
}
- (BOOL)prefersStatusBarHidden {
return NO;
}
- (void)scrollViewDoubleTapped:(UITapGestureRecognizer*)recognizer {
//LOG(#"scrollViewDoubleTapped called");
[self.delegate dismissImageViewer];
}
#end
#implementation ViewController
-(void)browseImage:(UIImage*)image
{
OSImageViewController *_imageViewerController = [[OSImageViewController alloc] init];
UIImage *img = [[UIImage alloc] initWithData:UIImagePNGRepresentation(image)];
_imageViewerController.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
_imageViewerController.modalPresentationStyle = UIModalPresentationFullScreen;
_imageViewerController.delegate = self;
[self presentViewController:_imageViewerController animated:YES completion:^(void){
[_imageViewerController setImage:img];
}];
}
- (void)dismissImageViewer {
[self dismissViewControllerAnimated:YES completion:nil];
}
#end
I believe you want to display a modal view from your ViewController.
Let the the modal view be managed by say ViewController2. In ViewController2.h declare a protocol of ViewController2
#protocol viewController2Delegate
-(void)dismissViewController2;
#end
Now make ViewController implement this protocol
#interface ViewController : UIViewController <updateUIDelegate,viewController2Delegate>
Add the method to the ViewController.m
-(void)dismissViewController2
{
[self dismissViewControllerAnimated:YES completion:nil];
}
Now whenever you push a modal view(managed by ViewController2) from ViewController you set the delegate to self. Your ViewController.m code might look like this
ViewController2 *objViewController2 = [[ViewController2 alloc]init];
objViewController2.delegate = self;
[self presentViewController:objViewController2 animated:YES completion:nil];
Hope this solves your problem
COMMUNICATION PATTERNS
Delegation is one of the communication patterns that more or less loosely coupled objects use to communicate each other. iOS Framework provides the following patterns: KVO, Notification, Delegation, Block, Target-Action.
In general, there are cases where the choice comes down to a matter of taste. However, there are many cases that are pretty clear cut.
It's also important to note that the use of each of this patterns results in a certain level of coupling among objects involved into the communication process.
Let's focus now on Delegation, Block, Target-Action.
DELEGATION
Level of coupling (proportional to the level of mutual ignorance) : loose
It allows us to customize an object’s behaviour (decoration) and to be notified about certain events (callback). In this case, the coupling is pretty loose, because the sender only knows that its delegate conforms to a certain protocol.
Since a delegate protocol can define arbitrary methods, you can model the communication exactly to your needs. You can hand over payloads in the form of method arguments, and the delegate can even respond in terms of the delegate method’s return value. Delegation is a very flexible and straightforward way to establish some sort of blind communication between two object that should be loosely coupled for design reason. Let's think to the communication mechanism between a tableview and its dataSource delegate.
Conversely, if two objects are that tightly coupled to each other that one cannot function without the other, there’s no need to define a delegate protocol (use composition instead). In these cases, the objects can know of the other’s type and talk to each other directly. Two modern examples of this are UICollectionViewLayout and NSURLSessionConfiguration.
TARGET-ACTION
Level of coupling : very loose
Target-Action is the typical pattern used to send messages in response to user-interface events. Both UIControl on iOS and NSControl/NSCell on the Mac have support for this pattern. Target-Action establishes a very loose coupling between the sender and the recipient of the message. The recipient of the message doesn’t know about the sender, and even the sender doesn’t have to know up front what the recipient will be. In case the target is nil, the action will goes up the responder chain until it finds an object that responds to it. On iOS, each control can even be associated with multiple target-action pairs.
A limitation of target-action-based communication is that the messages sent cannot carry any custom payloads. On the Mac action methods always receive the sender as first argument. On iOS they optionally receive the sender and the event that triggered the action as arguments. But beyond that, there is no way to have a control send other objects with the action message.
BLOCK
Blocks are usually used to pass to an object a behaviour to be executed before its lifecycle end. Besides, they can also substitute delegates with a caveat relevant to the potential creation of retain cycle.
self.tableView.didSelectRowAtIndexPath = ^(NSIndexPath *indexPath) {
...
[self.tableView reloadData];
...
}
In this case the sender retain the table view whose selection block retain the sender so we'd better use delegation pattern.
An example in which block communication shines:
self.operationQueue = [[NSOperationQueue alloc] init]
Operation *operation = [[Operation alloc] init];
operation.completionBlock = ^{
[self finishedOperation]
}
[operationQueue addOperation:operation];
There's a retain cycle in the above code as well, but once the queue removes the operation, the retain cycle is broken.
Blocks are a very good fit if a message we call has to send back a one-off response that is specific to this method call, because then we can break potential retain cycles. Additionally, if it helps readability to have the code processing the message together with the message call, it’s hard to argue against the use of blocks. Along these lines, a very common use case of blocks are completion handlers, error handlers, and the like.
A CHART HELPING US TO MAKE THE RIGHT CHOICE
source: objc.io
In your specific case, I'd use the target-action communication pattern to dismiss the presented modal view controller.
For example,
ModalViewController *modalViewController = [[ModalViewController alloc] init];
[self presentViewController:modalViewController animated:YES completion:^{
[modalViewController.closeButton addTarget:self action:#selector(dismissModalViewControllerAnimated:)
forControlEvents:UIControlEventTouchUpInside];
}];
I have several popovers in my application and I am having difficulty in determining which popover was dismissed. Is there a "tag" feature equivalent for UIPopOvers?
I can NSLog the popoverController in the popoverContorllerDidDismissPopover method and see the memory reference of each one but that doesn't help.
#pragma mark - Popover controller delegates
- (void)popoverControllerDidDismissPopover:(UIPopoverController *)popoverController {
NSLog(#"Popover dismised %#", popoverController);
}
An extract from here:
If I understand the question, then basically, no - and it's maddening.
On the one hand you're told that only one popover should be showing at
any one moment. On the other hand you don't automatically get a
reference to that popover. Thus it is up to you to store a reference,
manually, to the current popover controller at the time it shows its
popover, so that you can talk to it later in order to dismiss it.
Popover controller management can thus get really elaborate and
clumsy; you're doing all kinds of work that the system should just be
doing for you.
iOS is funny this way. I'm reminded of how there's no call in iOS 4
that tells you current first responder. Obviously the system knows
what the first responder is, so why won't it tell you? It's kind of
dumb. This is similar; the system clearly knows useful stuff it won't
share with you. m.
There are many ways how to distinguish between popovers. I will list few of them:
You are asking about tag. Note that every popover has a content view controller and this controller has a view that can be tagged. However, using magic integer tags to distinguish between views is arguable in general.
Store the type of the popover into a variable/property in your controller, e.g. as an enum. This is the simplest way.
Add the neccessary information to the popover, but be clever about it, e.g.
#interface MyPopoverController : UIPopoverController
#property (nonatomic, copy, readwrite) void (^dissmissHandler)(void);
#end
#implementation MyPopoverController
- (id)initWithContentViewController:(UIViewController*)contentView {
self = [super initWithContentViewController:contentView];
if (!self) {
return nil;
}
self.delegate = self;
return self;
}
- (void)popoverControllerDidDismissPopover:(UIPopoverController*)popover {
assert(popover == self);
if (self.dissmissHandler) {
self.dissmissHandler();
}
}
#end
MyPopoverController* popover = [MyPopoverController alloc] initWithContentViewController:...];
popover.dissmissHandler = ^{
...
};
As #Anoop stated, you can usually only have one popover showing at a time.
One possible solution is to check the contentViewController property on the pop over. If you are storing a reference of each view controller you could do something like:
- (void)popoverControllerDidDismissPopover:(UIPopoverController *)popoverController {
if ( popoverController.contentViewController == self.someUIViewController ) {
// do stuff
}
else if ( popoverController.contentViewController == someoTherViewController ) {
//
}
NSLog(#"Popover dismised %#", popoverController);
}
If storing a reference to each content view controller is not possible (or maybe just not a good idea), you could always check its type:
- (void)popoverControllerDidDismissPopover:(UIPopoverController *)popoverController {
if ( [popoverController.contentViewController isKindOfClass:[MyAwesomeViewController class]] ) {
// do stuff
}
else if ( [popoverController.contentViewController isKindOfClass:[MyOtherViewController class]] ) {
//
}
NSLog(#"Popover dismised %#", popoverController);
}
Another possible solution, which is probably better from a design stand point of view, would be to pass in a delegate to the view controller contained in the pop over. More here. This way, the view controller displayed can send data back to your main view controller.