UIPickerView Delegate and DataSource methods not getting called - objective-c

I want to display PickerView when I click the button.
I have a view which has 3 buttons. In the corresponding actions to the buttons, I have alloc'ed and init'ed the pickerView as follows:
pickerView = [[UIPickerView alloc]init];
Also I have added the PickerView to the Subview as follows:
[self.view addSubview:pickerView];
I've also subclass'ed my class with UIPickerViewDataSource and UIPickerViewDelegate as follows:
MyClass : UIViewController
By NSLoging i saw that PickerView DataSource and Delegate methods are not getting called.
When I click(TouchUpInside) the button, the view shows up pickerView with origin=(0,0) which should be at bottom and pickerView appears total Black. I fixed the PickerView's frame by using:
pickerView.frame = CGRectmake(0,180,320,260);
I googled to check when the pickerView's DataSource and Delegate method are called but I couldn't find the proper answer.
I also tried the "hidden" property here as follow:
In viewDidLoad method:
pickerView.hidden = YES;
In the method which is called after clicking button:
pickerView.hidden = NO;
Help me please. I'm new to Objective C.

If you create your picker programmatically - did you actually set its delegate and dataSource?
// Assuming that delegate and data source is controller where picker is created
pickerView.delegate = self;
pickerView.dataSource = self;

Related

Storyboards: Open UIViewController from storyboard as popover from image in UITableViewCell

I have a Custom UITableViewCell class and the custom cell has an image button in it that is linked back to a method on the cell class. When this method is triggered, I want to launch an orphaned UIViewController from the storyboard inside a popover. I've tried several techniques for doing this. Interface Builder will not compile if I add the UIViewController as a segue from the button on the prototype cell. Does anyone have any suggestions?
UPDATE: I Got it working with the following:
UITableView *tv = (UITableView *) self.superview;
UITableViewController *vc = (UITableViewController *) tv.dataSource;
UIStoryboard *storyboard = vc.storyboard;
UIViewController *actionView = [storyboard
instantiateViewControllerWithIdentifier:#"ActionView"];
popoverController = [[UIPopoverController alloc]
initWithContentViewController:actionView];
popoverController.popoverContentSize = CGSizeMake(320, 416);
[popoverController presentPopoverFromRect:self.actionButton.bounds
inView:self.actionButton
permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];
You should be able to use instantiateViewControllerWithIdentifier: from UIStoryboard to pick up any view controller if you have it identified correctly. (Since the easiest way to get a reference to the storyboard is from some view controller that already came from it, you may want to have the cell notify the current controller to do this...that depends on how your objects are connected.)

Where to set popover size?

I have a UIViewController called HomeViewController and it has a model that contains an array of data. HomeViewController also has a button that when pressed will show a UITableViewController that display's the model's array of data.
My question is, what is the standard/best way to set the popover's size? I only want the popover to be tall enough to display the data, but no taller. What is common practice? I assume that I need to set contentSizeForViewInPopover at some point but I'm not sure where...In the viewDidLoad method of the popover class? In the prepareForSegue method?
Here's the code I have so far: (Note that DataPresentingViewController is my popover view controller class`)
//In HomeViewController
- (void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if([segue.destinationViewController isKindOfClass:[DataPresentingViewController class]])
{
DataPresentingViewController *dest = (DataPresentingViewController *) segue.destinationViewController;
dest.mySavedData = self.myModel.mySavedData;
}
}
I know that I could just set the contentSizeForViewInPopover here in the prepareForSegue method; however, this seems like something the popover class should deal with.
As of iOS7 contentSizeForViewInPopover is deprecated. You should use UIViewController's preferredContentSize property instead.
Hope this helps!
set contentSizeForViewInPopover in ViewDidLoad method in HomeViewController
contentSizeForViewInPopover is deprecated in iOS 7
The property: popoverContentSize of UIPopoverController represents the size of the content view that is managed by the view controller in the contentViewController property of UIPopoverController.
Reference
The advantage is that it is available in iOS 3.2 and later, so you don't need to check device version everytime, just replace contentSizeForViewInPopover method with your UIPopOverController object instance.
Have you tried:
setPopoverContentSize:<#(CGSize)#> animated:<#(BOOL)#>
in your segue logic block. Useful if you want the popover size to be variable based upon a data set, or view placement or whatever.
// self.contentSizeForViewInPopover = CGSizeMake(320.0, 600.0); //Deprecated in ios7
self.preferredContentSize = CGSizeMake(320.0, 600.0); //used instead
A little known trick to sizing your UIPopoverViewController to match the height of your UITableView is to use the tableView's rectForSection method to give you the height. Use the height in your viewController's contentSizeForViewInPopover like this:
- (CGSize)contentSizeForViewInPopover {
// Currently no way to obtain the width dynamically before viewWillAppear.
CGFloat width = 200.0;
CGRect rect = [self.tableView rectForSection:[self.tableView numberOfSections] - 1];
CGFloat height = CGRectGetMaxY(rect);
return (CGSize){width, height};
}
Try the following code:
- (IBAction)setData:(id)sender
{
UIViewController *popoverContent=[[UIViewController alloc] init];
UITableView *tableView=[[UITableView alloc] initWithFrame:CGRectMake(265, 680, 0, 0) style:UITableViewStylePlain];
UIView *popoverView=[[UIView alloc] initWithFrame:CGRectMake(0, 0, 200, 300)];
popoverView.backgroundColor=[UIColor whiteColor];
popoverContent.view=popoverView;
popoverContent.contentSizeForViewInPopover=CGSizeMake(200, 420);//setting the size
popoverContent.view=tableView;
tableView.delegate=self;
tableView.dataSource=self;
self.popoverController=[[UIPopoverController alloc] initWithContentViewController:popoverContent];
[self.popoverController presentPopoverFromRect:CGRectMake(400, 675, 0, 0) inView:self.view permittedArrowDirections:UIPopoverArrowDirectionDown animated:YES];
}
It looks like you want to do it programmatically, but in the storyboard, before you hook up a view controller to a segue, under the attributes inspector there is a "popover, use explicit size option." Maybe you can set the size that would work the best for your App first and not worry about the size with using code. Hope this helps, let us know how it goes.

How do I get acces to the parent view?

I'm trying to acces the storyboard from code to be able to use this line:
DetailViewController *detail = [self.storyboard instantiateViewControllerWithIdentifier:#"detail"];
I use that in my mapview and listview, but want to use it somewhere else to.
The problem is that this view, is a subview of another view.
It's set up as followed:
thisBigView is a view I added in the storyboard and it's is ThisBigViewController
In storyboard I added another view to that view, let's call it thisSmallView. The class is set to ThisSmallView.
ThisSmallView is a custom view where I generate buttons dynamically in on the view. These buttons call the following action:
-(void) radarEventClick:(UIButton *)sender{
SingletonManager *sharedManager = [SingletonManager sharedManager];
DetailViewController *detail = [self.storyboard instantiateViewControllerWithIdentifier:#"detail"]; // PROBLEM 1
Event *a;
for(int i = 0; i < [sharedManager.eventsManager count]; i++){
if(sender.tag == ((Event*)([sharedManager.eventsManager objectAtIndex:i])).id_nr){
a = [sharedManager.eventsManager objectAtIndex:i];
break;
}
}
[detail setEvent:a];
[self.navigationController pushViewController:detail animated:YES]; // PROBLEM 2
}
This is code I'm using in my mapviewcontroller to respond to annotationdisclosure clicks, and want to use it here to, but I have 2 problems!
PROBLEM 1: Because thisSmallView is a subview of anotherview, it doesn't have direct access to the storyboard and don't know how to get that access.
PROBLEM 2: thisBigView is embed in a navigationcontroller, but again, I don't know who to access thisBigView, so I can't access the navigationcontroller.
(I think if I could solve problem 2, I would automatically be solving problem 1 to?)
-- EDIT: what I tried --
DetailViewController *detail = [self.superview.storyboard instantiateViewControllerWithIdentifier:#"detail"];
But then I just get 'property storyboard not found on object of type UIView*'
superview
[smallView superview]
http://developer.apple.com/library/ios/documentation/uikit/reference/uiview_class/uiview/uiview.html#//apple_ref/occ/instp/UIView/superview
Add the subview as a property of the superview in interface builder (control-drag to the header file). Then add a UIViewController property to the subview. In the superview's code then do
nameOfSubView.superViewPropertyName = self;

iOS UISplitViewController's Popover controller button disappear after pushing new view controller in portrait mode

In my UISplitViewController application, I have
RootViewController - view controller in the left pane.
DetailViewController - view controller in the right pane.
When one item (which is in a UITableView) in RootViewController is tapped, new view controller will be set as the following shows:
[detailViewController setViewControllers:[NSArray arrayWithObjects:newViewController, nil] animated:animated];
//detailPane is my DetailViewController
All works pretty well in landscape mode. However, I can't make the UISplitViewController work as what I want in portrait mode, that is, the RootViewController's popover button does not appear appropriately in my DetailViewController when I launch and use the application in portait mode.
When I launch the app in portrait mode, the popover button appears appropriately. But after tapping one item in the popover and a new view controller has been set on detailViewController, the button disappeared. I have to rotate the device to landscape and then back to portrait again to make the button appear again.
I set my UISplitViewController's delegate in my application's AppDelegate as follows:
self.splitViewController.delegate = self.detailViewController
And here is my UISplitViewControllerDelegate implementation
- (void)splitViewController: (UISplitViewController*)svc willHideViewController:(UIViewController *)aViewController withBarButtonItem:(UIBarButtonItem*)barButtonItem forPopoverController: (UIPopoverController*)pc {
NSLog(#"Will hide view controller");
barButtonItem.title = #"Menu";
[self.navigationItem setLeftBarButtonItem:barButtonItem];
self.popoverController = pc;
}
- (void)splitViewController: (UISplitViewController*)svc willShowViewController:(UIViewController *)aViewController invalidatingBarButtonItem:(UIBarButtonItem *)barButtonItem {
NSLog(#"Will show view controller")
NSMutableArray *items = [self.navigationItem.leftBarButtonItems mutableCopy];
[items removeAllObjects];
[self.navigationItem setLeftBarButtonItems:items animated:YES];
[items release];
self.popoverController = nil;
}
Any hint or help is greatly appreciated.
Thanks.
Just came up with a new solution.
Subclass UINavigationController and implement UISplitViewControllerDelegate. Set an instance of this class as the right ViewController of the splitViewController. Everytime you want to change the detail view controller from the master
NewDetailViewController *newDetailVC = ....// Obtain the new detail VC
newDetailVC.navigationItem.leftBarButtonItem = [[[[self.splitViewController.viewControllers objectAtIndex:1]topViewController]navigationItem ]leftBarButtonItem]; //With this you tet a pointer to the button from the first detail VC but from the new detail VC
[[self.navigationController.splitViewController.viewControllers objectAtIndex:1]setViewControllers:[NSArray arrayWithObject:newDetailVC]]; //Now you set the new detail VC as the only VC in the array of VCs of the subclassed navigation controller which is the right VC of the split view Controller
This works for me and I can avoid defining a hole protocol and setting the master as the delegate, which is a big trade off. Hope it helps.
If you still need it:
http://developer.apple.com/library/ios/#samplecode/MultipleDetailViews/Introduction/Intro.html
What I did to my source (I had similar setup to you) to fix it:
I have the master viewcontroller (UITableViewController in my case) be the delegate of the UISplitViewController. In the two delegate methods for UISplitViewControllers (so this would be in your master viewcontroller implementation) you would save the popupviewcontroller and the barbuttonitem in your class. Now, if you change your details viewcontroller, you do:
self.viewControllers = [NSArray arrayWithObjects:[self.viewControllers objectAtIndex:0], newDetailsViewController, nil];
UIViewController <SubstitutableDetailViewController>*vc = (UIViewController <SubstitutableDetailViewController>*)newDetailsViewController;
[vc invalidateRootPopoverButtonItem:_tableViewController.rootPopoverButtonItem];
[_createReportViewController showRootPopoverButtonItem:_tableViewController.rootPopoverButtonItem];
where we have
#protocol SubstitutableDetailViewController
- (void)showRootPopoverButtonItem:(UIBarButtonItem *)barButtonItem;
- (void)invalidateRootPopoverButtonItem:(UIBarButtonItem *)barButtonItem;
#end
the delegate that each of your detailsViewControllers should adhere to. You would implement like this:
- (void)showRootPopoverButtonItem:(UIBarButtonItem *)barButtonItem {
self.navigationItem.leftBarButtonItem = barButtonItem;
}
- (void)invalidateRootPopoverButtonItem:(UIBarButtonItem *)barButtonItem {
self.navigationItem.leftBarButtonItem = nil;
}
Let me know if this helps you.
I liked Nekto's solution, but it misses one key problem.
It's not clear what action: selector will cause the UISplitViewController to show the MasterViewController in a popover. When I finally figured this out, by examining the BarButtonItem in the debugger, I realized why it was so tricky to figure this out: the action: selector isn't documented anywhere in Apple's iOS SDK. Oops.
Try this:
UIBarButtonItem *showListView = [[UIBarButtonItem alloc] initWithTitle:#"List" style:UIBarButtonItemStyleBordered target:[self splitViewController] action:#selector(toggleMasterVisible:)];
[[detailViewController navigationItem] setLeftBarButtonItem:showListView];
You may want to surround this code with a conditional that checks the window is in in portrait mode, such as if ([self interfaceOrientation] == UIInterfaceOrientationPortrait)
When you are setting new view controllers placed on navigation stack, probably, all navigation buttons are reset. You can manually add appropriate buttons after changing navigation stack.
For example, you can pick code from - (void)splitViewController: (UISplitViewController*)svc willHideViewController:(UIViewController *)aViewController withBarButtonItem:(UIBarButtonItem*)barButtonItem forPopoverController: (UIPopoverController*)pc where default popover controller button is created:
UIBarButtonItem *item = [[UIBarButtonItem alloc] initWithTitle:#"Menu" style:UIBarButtonItemStyleBordered target:self action:#selector(appropriateSelector)];
[self.navigationItem setLeftBarButtonItem:barButtonItem];
self.popoverController = pc;

How do I bind programmatically in the view subclass of my NSCollectionView?

I've successfully created an NSCollectionView and added a label to the view prototype in IB, bound to a property of my represented object. I now want to programmatically create an NSButton and NSTextField with the NSTextField bound to a property of my represented object. When the button is clicked I want to show and hide the NSTextField.
The problem I've come across is if I put my initialization code for my controls in the view's initWithCoder method, and the binding in the view's awakeFromNib, the binding doesn't get hooked up. If I put the initialization for my controls in the awakeFromNib, when the button is clicked, I don't have access to the controls in my view (they are null when printed out using NSLog).
From what I can tell it looks like the issue may be that the way NSCollectionView works is, it creates an instance of the view, then copies it for how every many objects are in the collection view. How do I get the the buttons to initialize and the binding to work with the copy of the prototype?
Below is my initialization code and my binding in the awakeFromNib for my subclassed view:
SubView.h
#interface SubView : NSView {
NSButton *button;
NSTextField *textField;
IBOutlet NSCollectionViewItem *item; // Connected in IB to my NSCollectionViewItem
}
- (IBAction)buttonClicked:(id)sender;
#end
SubView.m
#implementation SubView
- (id)initWithCoder:(NSCoder *)decoder
{
id view = [super initWithCoder:decoder];
button = [[NSButton alloc] initWithFrame:NSMakeRect(50, 95, 100, 20)];
[button setTitle:#"Begin Editing"];
[button setTarget:self];
[button setAction:#selector(buttonClicked:)];
[self addSubview:button];
textField = [[NSTextField alloc] initWithFrame:NSMakeRect(10, 10, 100, 75)];
[self addSubview:textField];
return(view);
}
- (void)awakeFromNib
{
// Bind the textField to the representedObject's name property
[textField bind:#"value"
toObject:item
withKeyPath:#"representedObject.name"
options:nil];
}
- (IBAction)buttonClicked:(id)sender
{
[button setTitle:#"End Editing"];
[textField setHidden:YES];
}
#end
This sounds similar to something I just did, so maybe it's what you need.
Subclass NSCollectionView and override:
- (NSCollectionViewItem *)newItemForRepresentedObject:(id)object
In newItemForRepresentedObject:, retreive the view item, then add your controls and any programmatic bindings:
#implementation NSCollectionViewSubclass
- (NSCollectionViewItem *)newItemForRepresentedObject:(id)object {
// Allow the superclass to create or copy the collection view item
NSSCollectionViewItem *newItem = [super newItemForRepresentedObject:object];
// Get the new item's view so you can mess with it
NSView *itemView = [newItem view];
//
// add your controls to the view here, bind, etc
//
return newItem;
}
#end
Hopefully this is somewhere close to where you need to be...
-awakeFromNib is not called on the view for a NSCollectionViewItem if that view is in the same nib as the NSCollectionView, but it is called if you put the view in a separate nib.
Create an empty nib file (BlahBlahCollectionViewItem.nib).
Cut the collection item view out of whatever nib you have it in
Paste it into the new nib file
Change the class of its owner to NSCollectionViewItem.
Connect the view outlet on the owner to the newly pasted view
Open the nib file containing the NSViewController
Select the associated NSViewControllerItem
Change its Nib Name property to the name of the new nib
Keep your code in -awakeFromNib
-awakeFromNib is not called for views copied from the prototype NSCollectionViewItem. Put your binding code in initWithCoder: and you should be fine.