Does UIPopOverController have a tag property?
I have multiple UIPopOverControllers, how do I distinguish between them from the delegate methods?
Thanks.
No, tag properties are only on views and bar button items, not view controllers.
However all UIPopoverViewControllerDelegate methods get passed a popoverViewController variable. You can tell which one is calling the delegate method by comparing that variable to your popover controller objects.
- (void)popoverControllerDidDismissPopover:(UIPopoverController *)popoverController {
if (popoverController == myFirstPopoverController) {
// do something
}
}
Note that this is true for all delegate methods in iOS, UITableViewDelegate methods all receive a tableView variable and so on.
According to the apple documentation there is no tag property. The tag property comes from being a subclass of UIView which UIPopoverController is not. In fact, UIPopoverController inherits directly from NSObject.
When your delegate callbacks run they will pass in exact instance of whichever UIPopoverController invoked the callback.
You can observe this on the UIPopoverControllerDelegate documentation with the following two protocol methods:
– popoverControllerShouldDismissPopover:
– popoverControllerDidDismissPopover:
Related
I have a UITableView comprised of custom UITableViewCells. In each cell, there is a UILabel and a UISlider. Does anyone know how to, upon a change in value of one of the sliders, send the new value of the slider from the custom UITableViewCell (in a separate file) to the UITableViewController, so that I can then update the array from which the table was populated?
The closest I've got so far is a failed hack: firing a setSelected event when a slider value is changed. Whilst this highlights the changed custom cell, the event is not picked up by didSelectRowAtIndexPath in the UITableViewController.
Whilst code is always appreciated, a conceptual/method solution is what I am looking for.
Thank you in advance,
Jamie
What you need is called Delegate Pattern.
Quoting from there to explain what does it mean:
Delegation is a simple and powerful pattern in which one object in a
program acts on behalf of, or in coordination with, another object.
The delegating object keeps a reference to the other object—the
delegate—and at the appropriate time sends a message to it. The
message informs the delegate of an event that the delegating object is
about to handle or has just handled. The delegate may respond to the
message by updating the appearance or state of itself or other objects
in the application, and in some cases it can return a value that
affects how an impending event is handled. The main value of
delegation is that it allows you to easily customize the behavior of
several objects in one central object.
These diagrams will help you understand what goes on:
Architecture:
Operation:
Now as to how to implement it, this is what you have to do.
For Objective-C:
First of all, create delegate methods of your UITableViewCell. Lets name it ContactTableViewCell.
In your ContactTableViewCell.h file, do this:
#protocol ContactCellDelegate <NSObject>
#required
-(void) didMoveSliderWithValue:(float) value;
#end
#interface ContactTableViewCell : UITableViewCell
#property (weak, nonatomic) id<ContactCellDelegate> delegate;
Now conform your TableViewController to this delegate. Let's name your VC MyTableViewController.
In MyTableViewController.h, Do this:
#interface MyTableViewController : UIViewController <ContactCellDelegate> //Use UITableViewController if you are using that instead of UIViewController.
In your cellForRowAtIndexPath, before returning cell, add this line:
cell.delegate = self;
Add implementation for the delegate method INSIDE your MyTableViewController.m.
-(void) didMoveSliderWithValue: (float) value
{
NSLog(#"Value is : %f",value);
//Do whatever you need to do with the value after receiving it in your VC
}
Now let's get back to your ContactTableViewCell.m. In that file you must have added some IBAction to capture the value change event in slider. Let's say it is the following:
- (IBAction)sliderValueChanged:(UISlider *)sender {
self.myTextLabel.text = [#((int)sender.value) stringValue]; //Do whatever you need to do in cell.
//Now call delegate method which will send value to your view controller:
[delegate didMoveSliderWithValue:sender.value];
}
When you call delegate method, it will run the implementation that we wrote earlier in the MyTableViewController. Do whatever you need in that method.
What happens here is that your Cell sends the message to your desired VC (Which is delegate of the Cell), that "Hey, Call the delegate method that we wrote earlier in your body. I am sending you parameters right away". Your VC takes the parameters and does whatever you wanted it to do with that info and at that time.
For Swift:
First of all, your TableViewCell.swift file, create a protocol like this:
#class_protocol protocol ContactCellDelegate {
func didMoveSliderWithValue(value: Float)
}
Now in your Cell class, create a delegate property like:
var cellDelegate: ContactCellDelegate?
In your Slider IBAction, call the delegate method like this:
self.cellDelegate?.didMoveSliderWithValue(slider.value)
In your VC do these changes:
Make it conform to the delegate:
class MyTableViewController: UIViewController, ContactCellDelegate
Add this line before returning cell in cellForRowAtIndexPath
cell.cellDelegate = self //Dont forget to make it conform to the delegate method
Add the implementation of required delegate method:
func didMoveSliderWithValue(value:float) {
//do what you want
}
I have kept the Swift part precise and summarized because It should be very easy to change the detailed Obj-C explanation to Swift implementation. However If you are confused about any of the pointers above, leave a comment.
Also see: StackOverflow answer on using Delegate pattern to pass data back
I have a custom class myCustomClass which is a subclass of UITextField. (I'm going to call myCustomClass in a viewControllers class.)
In myCustomClass, I'm trying to check what kind of viewController the class that called it is. (UIViewController, UITableViewController etc.)
I tried:
if ([self.superview.nextResponder isKindOfClass[UIViewController class]]) {
NSLog(#"View Controller");
} else if ([self.superview.nextResponder isKindOfClass[UITableViewController class]) {
NSLog(#"TableView Controller");
}
I only get a result if the superclass is a viewController. So I did the following:
NSLog(#"%#", self.superview.nextResponder);
Results
UIViewController Class - ViewController
UITableViewController Class - UITableViewCell
How can I check if it's a UITableViewController?
For your specific case, you can use [self.superView isMemberOfClass:[UITableViewCell class]] to check if your custom view is inside a table view cell, which (unless you are using tableViewCell in an unusual way!) means that it's being called from a UITableViewController.
More generally, if you wanted to find out the containing view controller, you can recursively walk up the responder chain to find the containing viewController as in the second answer in this post:
Get to UIViewController from UIView?
It's also important to note that there is a difference between isKindOfClass: and isMemberOfClass:
isKindOfClass returns YES if 'the receiver is an instance of given class or an instance of any class that inherits from that class.'
isMemberOfClass returns YES if ' the receiver is an instance of a given class.'
Therefore, your UITableViewController, which inherits from UIViewController, will answer YES to isKindOfClass:[UIViewController class], will your if statement to act unexpectedly. (Though in the example it also didn't work correctly because you still needed to walk up the responder chain further).
So, if you in fact are comparing a UIViewController to a UITableViewController use -isMemberOfClass and your logic in the example would work as expected.
I am making master detail application, i have dynamic Detail ViewController. Detail ViewController are changed.
But in every Detail ViewController I have one common method updateInfo I want to call that method
Here is my code
UINavigationController *nav=[self.splitViewController.viewControllers objectAtIndex:1];
UIViewController *controller=[nav.viewControllers objectAtIndex:0];
[controller updateLastInfo];
But it gives me error no method found.
it will work if i use UIViewController name.
HomeViewController *controller=(HomeViewController)[nav.viewControllers objectAtIndex:0];
[controller updateLastInfo];
But i dnt want to do above things.
I have tried to explain. Please help
You can use id
UINavigationController *nav=[self.splitViewController.viewControllers objectAtIndex:1];
id controller=[nav.viewControllers objectAtIndex:0];
[controller updateLastInfo];
You could subclass UIViewController and make a base DetailViewController class that houses common functionality of your detail view controllers. Then you would make all of your detail view controllers subclass DetailViewController instead of UIViewController. This would be a safe way to do it and would also allow you to add extra functionality to your updateInfo method in the specific detail view controllers.
If you want an unsafe way, you could make your controller object of type id. I wouldn't suggest this approach as it relies on your personal knowledge of the code. If someone else (or yourself down the road) sets it to a view controller that doesn't have that method, the code will still try to run and will crash.
UIViewController doesn't have a method named updateInfo, so the compiler will of course complain when you try to send that message to a pointer that's known only to point to an instance of UIViewController. When you use the class name, like this:
HomeViewController *controller=(HomeViewController)[nav.viewControllers objectAtIndex:0];
you're providing more information to the compiler, using a type cast to tell it "Hey, don't worry, I know for certain that the object I'll get back is a HomeViewController. Since you seem to have several types of view controllers that all have this method, the best thing to do is to declare the updateInfo method in a protocol and then have each of those UIViewController subclasses implement that protocol. So, your protocol declaration would be in a header file and might look like:
#protocol SomeProtocol
- (void)updateInfo
#end
and each class that has an -updateInfo method would just need to declare that it adopts the protocol:
#interface HomeViewController <SomeProtocol>
//...
#end
and then make sure that you have an -updateInfo in your class implementation:
#implementation HomeViewController
- (void)updateInfo {
//...
}
//...
#end
Then, in your code, you can either check that the object conforms to the protocol using -conformsToProtocol: like this:
if ([controller conformsToProtocol:#protocol(SomeProtocol)]) {
UIViewController<SomeProtocol> *c = (UIViewController<SomeProtocol>*)controller;
[c updateInfo];
}
or else just check that the object responds to the selector before calling it:
if ([controller respondsToSelector:#selector(updateInfo)]) {
[controller performSelector(updateInfo)];
}
The other answers you've received (using id or creating a common base class) are also good ones, but to be safe make sure you do some checking before calling your method. For example, you can use -isKindOfClass to make sure that the view controller you get back is in fact an instance of your common base class, and you can use -respondsToSelector: as above to check that an id points to an object that implements updateInfo.
I got a problem.
That when I initial a Controller I used initwithnib:bundle: method and then I think this controller has been loaded to the memory. So I call its method selectUnreadMessage to change its header. But I found that viewdidload was called after selectUnreadMessage.
Some one can tell me why ? Thanks.
- (void)selectUnreadSegmentedHeaderButton {
if ([YDNetworkingManager sharedNetworkingManager].badgeNumbers.count) {
for (NSNumber *unreadMessagesCount in [YDNetworkingManager sharedNetworkingManager].badgeNumbers) {
if (unreadMessagesCount.intValue > 0) {
NSInteger index = [[YDNetworkingManager sharedNetworkingManager].badgeNumbers indexOfObject:unreadMessagesCount];
self.segmentedHeader.selectedIndex = index;
[self loadTableViewAtIndex:index];
break;
}
}
}
}
The code of my private method is above. This controller has the property SegmentedHeader to switch between different kinds of messages.
This method is called After the Controller which used this method as addSubView;
viewDidLoad method is called when view of Controller is preparing to be addSubView into another view. And when view of Controller was added, viewWillAppear and viewDidAppear will be called. If view of Controller is removed from superview, viewWillDisAppear and viewDidDisAppear will be called.
And view of Controller is added again, viewDidLoad will NOT be called. Other methods will call the same as I've said.
It means when you initial a Controller, viewDidLoad was not called immediately. This method will be called later. It is lazy-loading
Hope it useful!
ViewController initialization is not creating view - viewcontroller's view is loaded lazily - when it's view is actually added to view hierarchy. So, -viewDidLoad method gets called when you access viewcontroller's .view property - thus, you may consider initializing your data structures in the init method (fetch data, allocate containers, setup default values, etc.) and later, when -viewDidLoad is called - apply you data to your views / UI.
I have a viewcontroller that can show several popovers. Not at the same time. Which would be the best way to know which popover is being dismissed at popoverControllerDidDismissPopover?
I have to do different actions regarding the popover that is being dismissed.
Thanks a lot
Something like this should work. (This code is not complete - I assume you know basic memory and class management and other stuff so I focus on the actual problem)
In your class keep some ivars to store reference to the popovercontrollers you created
#interface MyClass : NSObject <UIPopoverControllerDelegate> {
UIPopoverController *popover1;
UIPopoverComtroller *popover2;
}
Init your popovercontrollers as usual and save a referance to each of them in popover1 and popover2.
Then in your implementation of the UIPopoverDelegate protocol:
- (void)popoverControllerDidDismissPopover:(UIPopoverController *)popoverController {
if(popoverController == popover1) {
//popover1 was dismissed
} else if (popoverController == popover2) {
//popover2 was dismissed
}
}
EDIT: Looking at your comments, it seems that you mean that you are using only ONE popovercontroller, and replacing it's content view with different UIViewControllers.
If this is the case, I suggest you perform whatever the actions are inside those particular UIViewController in such a way that it affects your application´s state.
Then, once the popover is dismissed, you reload your original view with the new refreshed state.
Or you change it to use two different instances of UIPopoverController instead.
This is how in Swift as of Xcode 6.3 beta 3, should be similar in Objective-C.
Your presented view should have a ViewController for itself.
import UIKit
class MenuBookmarksViewController: UITableViewController {
}
Add an extension to the UIViewController class or place the code (below) inside the UIViewController that will be presenting your popovers:
extension UIViewController: UIPopoverPresentationControllerDelegate {
public func popoverPresentationControllerDidDismissPopover(popoverPresentationController: UIPopoverPresentationController) {
if popoverPresentationController.presentedViewController as? MenuBookmarksViewController != nil {
///do your stuff
}
}
}
You are passed which popover is being dismissed in popoverControllerDidDismissPopover:. Use that to determine what you want to do in each case.
You'll probably want to store your UIPopoverController instances as ivars of whatever object is presenting them, and then just compare against the value that you're passed in the delegate method.