presentPopoverFromBarButtonItem - objective-c

I am trying to display my popover menu whenever the user clicks on a button.
I have two methods, the first creates the button
- (void)viewDidLoad {
[super viewDidLoad];
UIBarButtonItem *btnMenu = [[UIBarButtonItem alloc]initWithTitle:#"Forms List" style:UIBarButtonItemStyleBordered target:self action:#selector(showPopover:)];
self.navigationItem.leftBarButtonItem = btnMenu;
}
And the second is called by the first, to display the popover
-(IBAction)showPopover:(id)sender{
NSLog(#"called with %#", sender);
self.popoverController = popoverController;
popoverController.delegate = self;
[popoverController presentPopoverFromBarButtonItem:sender permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];
}
Presently, when I click the button nothing happens. I think the problem lies in the second method, mainly the presentPopoverFromBarButtonItem line.
Any help would be appreciated, I don't really understand how that method call works.
Thanks.
EDIT 1:
Here is the code where I (think) initialize the popoverController
#interface DetailViewController ()
#property (nonatomic, retain) UIPopoverController *popoverController;
- (void)configureView;
#end
#implementation DetailViewController
#synthesize toolbar, popoverController, detailItem, detailDescriptionLabel;

Have you initialized your UIPopoverController?
UIPopoverController *popoverController = [[UIPopoverController alloc] initWithContentViewController:VCTHATGOESINSIDE];
If all else fails, try presenting it from rect and specify a CGRect. Hope this helps!
edit: initWith*Content*ViewController

Related

How do I 'share' pieces of code throughout my whole app?

I am working on a simple app. This app has various textfields that require the 'Done' button in a toolbar.
Currently, this toolbar requires <10 lines of code, but I dislike the fact that for every textfield in every viewController I need to add those lines.
So, is there a way to shorten this 'toolbar setup'? Ideally, I would need to call a single method containing all I need:
[self addDefaultToolbar:field]
But then again adding this method everywhere I need seems a bit 'dirty' (maybe this is not clear enough, self implies that within the controller there's a method called addDefaultToolBar).
So this is what I tried:
I made a nice folder, an header and an .m file. In the header I put this:
#ifndef UIUtils_h
#define UIUtils_h
#import <UIKit/UIKit.h>
#interface UIUtils : NSObject
-(void)addDefaultToolbar:(UITextField *)field;
#end
#endif /* UIUtils_h */
And in the .m file I put this:
#import "UIUtils.h"
#implementation UIUtils
-(void)addDefaultToolbar:(UITextField *)field{
UIToolbar *textFieldToolbar = [[UIToolbar alloc] init];
[textFieldToolbar sizeToFit];
UIBarButtonItem *doneButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self action:#selector(doneClick)];
UIBarButtonItem *flexSpace = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:self action:nil];
[textFieldToolbar setItems:[NSArray arrayWithObjects:flexSpace, doneButton, nil] animated:NO];
field.inputAccessoryView = textFieldToolbar;
}
-(void)doneClick
{
[[UIApplication sharedApplication] sendAction:#selector(resignFirstResponder) to:nil from:nil forEvent:nil];
}
#end
And then I call this from where I need it, like this:
#import "UIUtils.h"
UIUtils *utils = [[UIUtils alloc] init];
[utils addDefaultToolbar:field];
Now, unfortunately, the button doesn't work. The keyboard should be dismissed but it isn't. Secondly, I have no idea if I am doing this right.
How do I do this properly?
Edit:
Further research indicates that the doneClick method is not being called at all. It does work if I add doneClick to the viewController, but this kinda defeats the purpose of what I'm trying to achieve here.
What you want is a toolbar that takes care of its own button press. You can create one of those.
#interface MYDefaultToolBar : UIToolbar
#end
#implementation MYDefaultToolBar
- (instancetype)init
{
self = [self initWithFrame: CGRectZero];
if (self) {
[self sizeToFit];
UIBarButtonItem *doneButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone
target:self action:#selector(doneClick)];
UIBarButtonItem *flexSpace = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace
target:self action:nil];
[self setItems:[NSArray arrayWithObjects:flexSpace, doneButton, nil] animated:NO];
}
return self;
}
-(void)doneClick
{
[[UIApplication sharedApplication] sendAction:#selector(resignFirstResponder) to:nil from:nil forEvent:nil];
}
#end
Then you want to be able to add it to text fields. I'd probably recommend doing that by hand:
textField.inputAccessoryView = [MYDefaultToolBar new];
This is very clear that it's modifying the inputAccessoryView (so if something else in the same code also modifies the inputAccessoryView, it'll be more obvious that there's a conflict). But you can also do this with a category, particularly if it were more complex.
#interface UITextField (AddDefaultToolBar)
- (void)my_addDefaultToolBar;
#end
#implementation UITextField (AddDefaultToolBar)
- (void)my_addDefaultToolBar {
self.inputAccessoryView = [MYDefaultToolBar new];
}
Or you could modify -[MYDefaultToolBar init] to take a text field, and add itself.

How to set title Edit and Done for Button from UIBarButtonItem?

I have a problem,Top button does not change own name but it works
- (IBAction)switchEdit:(UIBarButtonItem *)sender {
sender.possibleTitles = [NSSet setWithObjects:#"Edit", #"Done", nil];
if (sender.style == UIBarButtonSystemItemEdit){
sender.style = UIBarButtonSystemItemDone;
[self.navigationItem.rightBarButtonItem setTitle:#"Done"];
self.tableView.editing = NO;
}else{
sender.style = UIBarButtonSystemItemEdit;
[self.navigationItem.rightBarButtonItem setTitle:#"Edit"];
self.tableView.editing = YES;
}
}
Consider using UIViewController's editButtonItem property, since it seems to do automatically what you need:
Returns a bar button item that toggles its title and associated state
between Edit and Done.
If you want to keep your approach, you can declare these two buttons as properties and set them up in viewDidLoad for example, like that:
#interface ViewController ()
#property (nonatomic, strong) UIBarButtonItem *editBarButtonItem;
#property (nonatomic, strong) UIBarButtonItem *doneBarButtonItem;
#end
and then
- (void)viewDidLoad
{
[super viewDidLoad];
self.editBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemEdit target:self action:#selector(editSelectorName)];
self.doneBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self action:#selector(doneSelectorName)];
}
and then in your code just do:
self.navigationItem.rightBarButtonItem = self.editBarButtonItem
or
self.navigationItem.rightBarButtonItem = self.doneBarButtonItem

Mirror text from UITextField on inputAccessoryView - UIToolBar to text on UITextField on navigationController.toolbar

In my app I have a UITextField on the navigationController toolbar.
#import "ViewController.h"
#interface ViewController ()
#property (nonatomic,strong) NSArray *toolBarButtonItems;
#property (nonatomic,strong) UITextField *textField;
#property (nonatomic,strong) UITextField *textField2;
#end
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
self.textField = [[UITextField alloc]initWithFrame:CGRectMake(0, 0, 60, 40)];
self.textField.delegate = self;
self.textField.borderStyle = UITextBorderStyleRoundedRect;
UIBarButtonItem *flexibleSpace = [[UIBarButtonItem alloc]initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil];
UIBarButtonItem *barButtonItem = [[UIBarButtonItem alloc]initWithCustomView:self.textField];
self.toolBarButtonItems = #[flexibleSpace,barButtonItem,flexibleSpace];
self.toolbarItems = self.toolBarButtonItems;
self.navigationController.toolbar.barTintColor = [UIColor blueColor];
[self.navigationController setToolbarHidden:NO animated:NO];
}
When the textField is clicked the keyboard opens up and I create a new inputAccessoryView toolbar with another textField.
-(UIToolbar *)addToolBar{
UIToolbar *toolbar = [[UIToolbar alloc]initWithFrame:self.navigationController.toolbar.frame];
toolbar.barTintColor = [UIColor darkGrayColor];
self.textField2 = [[UITextField alloc]initWithFrame:CGRectMake(0, 0, 60, 40)];
self.textField2.delegate = self;
self.textField2.borderStyle = UITextBorderStyleRoundedRect;
UIBarButtonItem *flexibleSpace = [[UIBarButtonItem alloc]initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil];
UIBarButtonItem *barButtonItem = [[UIBarButtonItem alloc]initWithCustomView:self.textField2];
[toolbar setItems:#[flexibleSpace,barButtonItem,flexibleSpace]];
return toolbar;
}
The idea is to change the firstResponder to the textField on the inputAccessoryView so this way I can see what I'm editing. The reason I am doing this is cause I can't scroll the Navigation toolbar up past the keyboard and I want to see the text that I am editing.
-(void)textFieldDidBeginEditing:(UITextField *)textField{
textField.inputAccessoryView = [self addToolBar];
if(self.textField2.isFirstResponder != NO){
[self.textField2 becomeFirstResponder];
}
}
It doesn't seem to be working when I click on the textField in the navigationController toolbar. The new inputAccessoryView toolbar shows up over the keyboard but I can't edit the field because the responder doesn't seem to be changing. The return key doesn't work either. I have to hit it twice in order to close the keyboard and when I do the text doesn't match up between the two text fields.
-(BOOL)textFieldShouldReturn:(UITextField *)textField{
[textField resignFirstResponder];
self.textField.text = self.textField2.text;
return YES;
}
I got it to work like this:
#import "KJMViewController.h"
#interface KJMViewController ()
#property (strong, nonatomic) UITextField *textField1;
#property (strong, nonatomic) UITextField *textField2;
#end
#implementation KJMViewController
- (void)viewDidLoad
{
[super viewDidLoad];
self.textField1 = [[UITextField alloc]initWithFrame:CGRectMake(30, 7, 260, 30)];
self.textField1.borderStyle = UITextBorderStyleRoundedRect;
self.textField1.delegate = self;
UIToolbar *navToolbar = self.navigationController.toolbar;
[navToolbar addSubview:self.textField1];
UIToolbar *toolbar = [[UIToolbar alloc]initWithFrame:CGRectMake(0, 0, 320, 44)];
self.textField2 = [[UITextField alloc]initWithFrame:CGRectMake(30, 7, 260, 30)];
self.textField2.borderStyle = UITextBorderStyleRoundedRect;
self.textField2.delegate = self;
[toolbar addSubview:self.textField2];
self.textField1.inputAccessoryView = toolbar;
[[NSNotificationCenter defaultCenter]addObserver:self selector:#selector(firstRes:) name:UIKeyboardDidShowNotification object:nil];
}
- (void)firstRes:(id)sender
{
[self.textField2 becomeFirstResponder];
}
- (void)textFieldDidEndEditing:(UITextField *)textField
{
}
- (BOOL)textFieldShouldReturn:(UITextField *)textField
{
if (textField == self.textField2) {
self.textField1.text = self.textField2.text;
}
[textField resignFirstResponder];
[self.textField1 resignFirstResponder];
return YES;
}
- (void)viewDidDisappear:(BOOL)animated
{
[[NSNotificationCenter defaultCenter]removeObserver:self forKeyPath:UIKeyboardDidShowNotification];
[super viewDidDisappear:animated];
}
#end
Here's what's happening in viewDidLoad:
Initialise toolbar and textField2
Set the inputAccessory for textField1 (the one hidden by the keyboard) here so it's ready to become firstResponder
Then in the viewDidAppear method:
Sign up for a notification that's sent when the keyboard is shown. You'll then write some code in the "firstRes" method to make textField2 the firstResponder. You need to make it the firstResponder using this notification because you know that it's in the view hierarchy by this time, which means it's able to become firstResponder. Calling it in the -(void)textFieldDidBeginEditing:(UITextField *)textField seems to fire it before textField2 comes on screen, meaning that it can't become firstResponder. We sign up for it in the viewDidAppear method because we only want to get the notification if we're on screen.
After textField2 resignsFirstResponder, textField1 becomes first responder again, so you have to call resignFirstResponder twice in the textFieldShouldReturn method.
Also, if we leave the screen, we need to remove ourself as an observer of the keyboard notification in the viewDidDisappear method.
Here's a link to the project I made in Xcode so you can see how it works:
https://github.com/kylejm/UIToolBar-UITextView

Creating multiple subviews inside view controller

I have a view controller named ViewController.
ViewController displays a UIView that has a button on it, which allows me to advance to a second view controller - SecondViewController.
SecondViewController also has a button on it, which allows me to advance to a third view controller.
However, I am having trouble displaying ThirdViewController. When I tap the button in SecondViewController it throws an error:
Warning: Attempt to present ... on ... whose view is not in the window hierarchy!
I have been to a bunch of other sites and posts that address this issue, but cannot seem to find a working solution. I have implemented quite a few solutions, but none seem to work.
Here is my code:
- (void)secondaryView:(UIView *)secondaryView
{
UIView *theView = secondaryView;
UIViewController *viewController = [[UIViewController alloc] init];
viewController.view.frame = [UIScreen mainScreen].bounds;
[viewController.view addSubview:theView];
viewController.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;
[self presentViewController:viewController animated:YES completion:nil];
}
secondaryView is a UIView that I am constructing elsewhere in the application. I add it to the viewController then present the viewController.
Is there any way to dynamically create UIViewControllers, add a UIView to each one, and add them to the window hierarchy?
If you can use navigation in your ViewControllers, you can try the below code
In the place where you call the firstViewController,
-(void)callFirst
{
FirstViewController *first = [FirstViewController alloc] init];
UINavigationController *navigationController = [UINavigationController alloc] initWithRootViewController:first];
navigationController.modalPresentaionStyle = UIModalPresenationFormSheet;
[self presentModalViewController:navigationController animated:YES];
}
And in the FirstViewController button action ,you can write
-(void)callSec
{
SecondViewController *sec = [SecondViewController alloc] init];
[self.NavigationController pushViewController:sec animated:YES];
}
And in SecondViewController you can do the same
-(void)callThird
{
ThirdViewController *third = [ThirdViewController alloc] init];
[self.NavigationController pushViewController:third animated:YES];
}
Try this...And in these ViewControllers you can do whatever coding you want to meet the requirement like
-(void)viewDidLoad
{
UIView *newView = [[UIView alloc] initWithFrame:CGRectMake(20,20,400,400)];
newView.backGroundColor = [UIColor redColor];
[self.view addSubView:newView];
}

Creating a UIPopoverController

I'm creating a popover in Xcode 4.3.
I don't get any error messages but when I click build and run and I press the button that is supposed to open the popover screen, the app crashes and highlights in green a line in my code and says:
Thread 1: breakpoint 2.1..
What does this mean and how can I fix it?
- (IBAction)popOver
{
SecondView *secondview = [[SecondView alloc] init];
UIPopoverController *popover = [[UIPopoverController alloc] initWithContentViewController:secondview];
[popover setDelegate:self];
[popover presentPopoverFromRect:CGRectMake(801, 401, 300, 200) inView:self.view permittedArrowDirections:UIPopoverArrowDirectionDown animated:YES];
[popover setPopoverContentSize:CGSizeMake(300, 200)];
}
Thanks in advance.
Few suggestions here...
First, are you using Automatic Reference Counting (ARC)? if yes you need to have an instance variable to control your UIPopoverController. For example:
#property (strong, nonatomic) UIPopoverController* popover;
if you are not using ARC create a retain one:
#property (retain, nonatomic) UIPopoverController* popover;
In the first case you have to do this because if not, ARC will release for you the just created popover at the end of your IBAction. In addition you do this to have a reference for your popover. See NOTE. This is valid also if you don't use ARC.
NOTE At some point you could have the necessity to release the popover. For example in - (void)popoverControllerDidDismissPopover:(UIPopoverController *)popoverController do
self.popover = nil;
Then, if your SecondView is a UIViewController call it SecondViewController. But this is a simple naming suggestion.
Finally, the correct way for display the popover from the action sender (e.g a UIButton), instead of hardcode positions, could be:
- (IBAction)openPopover:(id)sender
{
// create the popover here...
UIButton* senderButton = (UIButton*)sender;
[popover setPopoverContentSize:CGSizeMake(300, 200)];
[popover presentPopoverFromRect:senderButton.bounds inView:senderButton permittedArrowDirections:UIPopoverArrowDirectionLeft animated:YES];
}
-(IBAction)showpop:(id)sender
{
UIPopoverController *pop = [[UIPopoverController alloc]initWithContentViewController:popoverview];
[pop setDelegate:self];
[pop presentPopoverFromRect:popbutton.frame inView:self.view permittedArrowDirections:UIPopoverArrowDirectionUp animated:YES];
}
The SecondView that you're passing into UIPopOverController is a UIView. Make it a UIViewController, because, what UIPopOverController expects is a UIViewController.
-Suraj
Make sure you simulate on iPad device
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone) {
// We are using an iPhone
UIActionSheet *alertSheet = [[UIActionSheet alloc] initWithTitle:#"Where do you want to get your daily image?" delegate:(self) cancelButtonTitle:#"Cancel" destructiveButtonTitle:nil otherButtonTitles:#"Camera", #"Library", nil];
[alertSheet setTag:0];
[alertSheet setDelegate:self];
[alertSheet showFromTabBar:[[self tabBarController] tabBar]];
[alertSheet release];
}else {
// We are using an iPad
/// here you should pass UIViewController inside the popover
/// follow #flexaddicted notes to implement the popover
}
Check below link for popover tutorial in ios
UIPopOverController Tutorial