Editable TextView with Second NavBar - Text appears, but too late.
The app has a single Navigation Controller.
I have an iPhone App that has basically three levels.
Level 1 - Table with category Names
Level 2 - Table with list of items for selected category
Level 3 - Tabbed View with several views, including UITextView for details of item
One to these Tabbed Views with a TextView is editable.
When the user taps in the editable TextView the KeyBoard
appears. User can type in the TextView. Characters appear
as they are typed.
At the top of this Level 3 TextView there is a NavBar (present for all 3 levels with
changes) with a BackButton and a "home->Level1" button on the right.
All works just fine until in the editable TextView I add a second NavigationBar
below the existing NavBar. This second NavBar has two buttons
as well. They are Save/Cancel.
When I click these Save and Cancel buttons the correct action
methods are reached. All is perfect with one exception, The text
which is typed does not appear in the TextView until either
the Save or the Cancel button is touched. The relevant Button setup and
action methods in my TabViewController.m are below. I need to persist this
data.
I thought that getting a Notification from the TextView and the action handleTextChange would do the trick, but no luck. I am stuck.
.........
- (void)loadView {
self.myTextView = [[UITextView alloc] init];
self.myTextView.delegate = self;
self.view = self.myTextView;
//UITextViewTextDidChangeNotification
NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
[nc addObserver:self
selector:#selector(handleTextChange:)
name:UITextViewTextDidChangeNotification
object:nil];
NSLog(#"Registered DG_HandleChangeTextNotification with notification center.");
}
- (void)handleTextChange:(NSNotification * )note
{
[self.myTextView setNeedsDisplay] ;
NSLog(#"...Handled Text Change.");
}
- (void)textViewDidBeginEditing:(UITextView *)textView
{
// provide my own Done/Save button to dismiss the keyboard
saveNavigationBar = [[UINavigationBar alloc] initWithFrame:CGRectMake(0, 0, 320, 44)];
saveNavigationBar.barStyle = UIBarStyleBlackOpaque;
UINavigationItem *doneItem = [[UINavigationItem alloc] init];
doneItem.title = #"My Notes";
UIBarButtonItem *doneItemButton = [[UIBarButtonItem alloc]
initWithBarButtonSystemItem:UIBarButtonSystemItemSave
target:self action:#selector(saveAction:)];
UIBarButtonItem *cancelItemButton = [[UIBarButtonItem alloc]
initWithBarButtonSystemItem:UIBarButtonSystemItemCancel target:self
action:#selector(cancelAction:)];
[doneItem setRightBarButtonItem:doneItemButton animated:NO];
[doneItem setLeftBarButtonItem:cancelItemButton animated:NO];
[saveNavigationBar pushNavigationItem:doneItem animated:NO];
[self.view addSubview:saveNavigationBar];
[doneItem release];
[cancelItemButton release];
[doneItemButton release];
}
- (void)saveAction:(id)sender
{
// finish typing text/dismiss the keyboard by removing it as the first responder
self.text = self.myTextView.text;
[self.saveNavigationBar removeFromSuperview];
[self.myTextView resignFirstResponder];
}
- (void)cancelAction:(id)sender
{
[self.saveNavigationBar removeFromSuperview];
[self.myTextView resignFirstResponder];
}
The Second NavBar was hiding the area of the UITextEdit
such that I had to type about four lines before I saw the text. I believe
I need to lower the height of the UITextEdit by 44 pixels.
Related
UIPickerView select and hide
I used this to pop up a date picker view when I "touch down" on a text field. That is, I need the text field to display whatever I choose on the date picker view as its contents.
- (IBAction)showPicker:(id)sender {
// create a UIPicker view as a custom keyboard view
UIDatePicker* pickerView = [[UIDatePicker alloc] init];
[pickerView sizeToFit];
pickerView.autoresizingMask = (UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight);
//pickerView.delegate = self;
//pickerView.dataSource = self;
//pickerView.showsSelectionIndicator = YES;
self.datePickerView = pickerView; //UIDatePicker
fromDate.inputView = pickerView;
// create a done view + done button, attach to it a doneClicked action, and place it in a toolbar as an accessory input view...
// Prepare done button
UIToolbar* keyboardDoneButtonView = [[UIToolbar alloc] init];
keyboardDoneButtonView.barStyle = UIBarStyleBlack;
keyboardDoneButtonView.translucent = YES;
keyboardDoneButtonView.tintColor = nil;
[keyboardDoneButtonView sizeToFit];
UIBarButtonItem* doneButton = [[[UIBarButtonItem alloc] initWithTitle:#"Done"
style:UIBarButtonItemStyleBordered target:self
action:#selector(pickerDoneClicked:)] autorelease];
fromDate.text = (NSString *)datePickerView.date; // fromDate is the Text Field outlet, where I am trying to display the selection on the picker.
[keyboardDoneButtonView setItems:[NSArray arrayWithObjects:doneButton, nil]];
// Plug the keyboardDoneButtonView into the text field...
fromDate.inputAccessoryView = keyboardDoneButtonView;
[pickerView release];
[keyboardDoneButtonView release];
}
- (IBAction)pickerDoneClicked:(id)sender {
[fromDate resignFirstResponder];
}
Edit : I am able to do this for one text field on the screen. No matter what, only a keyboard pops up for the second text field.
Any ideas to do this for a second text field?
fromDate.inputView = pickerView;
This line of code makes the picker view the input view for the fromDate field. You need similar code for the other date field. You'll also need to set the input accessory on your other field.
Creating a special method to do this and touch down actions on all your text fields is unnecessary work. Simply create the picker view and toolbar in viewDidLoad and assign them to the input views and accessory views of whatever fields you want them on. Text fields alread have a touch down method which will bring up the keyboard.
Just an alternate solution for your problem.
1) Put a custom UIButton over the textfield on tap of which you want to show the UIDatePickerView
2) set the delegate of the textfield to nil on which you want to show the picker view.
fromDate.delegate = nil;
3) on the IBAction("touch down") of that custom button write the below code :-
- (IBAction) customButtonAction: (id) sender
{
[_previousTextField resignFirstResponder]; // The instance of last textfield tapped.
[self showDatePicker];
}
4) On tap of "Done" button of Picker view set selected value to the textfield
- (IBAction)pickerDoneClicked:(id)sender
{
fromDate.text = #"value from UIPickerView's selected row"
}
I have a class that subclasses ABNewPersonViewController. As I understand, when the Done button in the navigation bar is clicked, the delegate method
- (void)newPersonViewController:(ABNewPersonViewController *)newPersonViewController didCompleteWithNewPerson:(ABRecordRef)person
gets called. But before entering the delegate method, the changes will be saved to address book.
What I need is, as soon as the save button gets pressed. I need to load a UIView with 2 buttons asking the user,
whether he wants the changes and
whether he should abort the changes made,
But this should be done before the changes are reflected in the address book. And only on the click of the first button in the UIView, that the changes should be saved in the address book.
On the click of the 2nd button, the view should disappear and I should return to the view controller class from where the UIView is loaded.
My question is, how will I load the view on save button click, before the changes are reflected in the address book
I have created a custom save button
UIBarButtonItem *okBtn = self.navigationItem.rightBarButtonItem;
UIBarButtonItem *saveBtn = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemSave target:okBtn.target action:okBtn.action];
self.navigationItem.rightBarButtonItem =saveBtn;
[saveBtn release];
On the save button action, the control goes to the delegate method
- (void)newPersonViewController:(ABNewPersonViewController *)newPersonViewController didCompleteWithNewPerson:(ABRecordRef)person` .
I want the control go to my custom method, where I can load my UIView before the edits are saved in the address book.
Edit:
When I load the ABNewPersonViewController
ABPersonViewController *displayVcardViewController = (ABPersonViewController*)[self.navigationController visibleViewController];
ABRecordRef person = displayVcardViewController.displayedPerson;
EditAddressNewPersonDetailViewController *newVCardViewController = [[EditAddressNewPersonDetailViewController alloc] init];
newVCardViewController.displayedPerson = person;
newVCardViewController.newPersonViewDelegate = self;
newVCardViewController.isEditingMode = YES;
[self.navigationController setToolbarHidden:YES];
[self.navigationController pushViewController:newVCardViewController animated:YES];
[newVCardViewController release];
Isn't this strong reference already or else Where should I include the strong reference.
On
- (void)actionSave:(UIBarButtonItem *)sender {
if([[NSBundle mainBundle] loadNibNamed:#"MyView" owner:self options:nil]) {
[self.myView setFrame:CGRectMake(0, 0, 320, 480)];
[[UIApplication sharedApplication].keyWindow addSubview:self.myView];
UIActionSheet * action = [[UIActionSheet alloc]initWithTitle:#""
delegate:self
cancelButtonTitle:#"Do"
destructiveButtonTitle:#"Cancel"
otherButtonTitles: nil];
action.tag = 101;
[action showInView:self.view];
[action release];
}
}
I am loading a UIView with UIAlertView over it.
Update: Starting with iOS 7.0, ABNewPersonViewController is not subclassable anymore and this won't work.
First, keep a reference to the default rightBarButtonItem before overriding it.
If you're subclassing ABNewPersonViewController, your viewDidLoad would look like:
- (void)viewDidLoad {
[super viewDidLoad];
// Store the old button item into a custom property
// #property (nonatomic, retain) UIBarButtonItem *defaultRightBarButtonItem;
self.defaultRightBarButtonItem = self.navigationItem.rightBarButtonItem;
UIBarButtonItem *saveBtn = [[UIBarButtonItem alloc]
initWithBarButtonSystemItem:UIBarButtonSystemItemSave
target:self
action:#selector(actionSave:)];
self.navigationItem.rightBarButtonItem = saveBtn;
[saveBtn release];
}
And you call the default action on the default target in your custom action method:
- (void)actionSave:(UIBarButtonItem *)sender {
// Do what you want to do before the data is saved
// ....
// ....
// Trigger the default action
[self.defaultRightBarButtonItem.target
performSelector:self.defaultRightBarButtonItem.action
withObject:self.defaultRightBarButtonItem.target];
}
So,
I have a form (which is basically a UITableView), and once I finish the form, I click the 'Done' button which is on top of the screen.
After clicking, I need to add the data to another tableView (which is in another tableViewController). This table is also inside a Navigation Controller.
After I press the Done Button, I need the presentModalViewController to be the new TableView (with the new data) along with the Navigation Controller on top of the tableView.
So, to summarize:
The Done Button is in someTableViewController.
I need to add an object (lets just say I am adding a a name called "Dobby" for simplicity) into another tableView called dogTableViewController.
I reload the data , and present the screen which has dogTableViewController inside the dogNavigationController.
All the classes are referenced properly and included.
I am pasting the -(IBAction) when the Done Button is clicked.
-(IBAction) doneWithData: (UIBarButtonItem*) sender{
UIActivityIndicatorView *indicator = [[UIActivityIndicatorView alloc]initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
indicator.activityIndicatorViewStyle = UIActivityIndicatorViewStyleWhiteLarge;
[indicator sizeToFit];
indicator.autoresizingMask = (UIViewAutoresizingFlexibleLeftMargin |
UIViewAutoresizingFlexibleRightMargin |
UIViewAutoresizingFlexibleTopMargin |
UIViewAutoresizingFlexibleBottomMargin);
indicator.tag = 1;
[self.view addSubview:indicator];
[indicator setBackgroundColor:[UIColor clearColor]];
indicator.center = self.view.center;
indicator.hidden = FALSE;
[indicator startAnimating];
if (self.dogTableViewController == nil)
{
DogTableViewController *temp = [[DogTableViewController alloc] init];
self.dogTableViewController = temp;
[temp release];
}
if (self.dogNavigationController == nil)
{
DogNavigationController *temp = [[DogNavigationController alloc] init];
self.dogNavigationController = temp;
[temp release];
}
[self.dogTableViewController.dogArray addObject:#"Dobby"];
[self.dogTableViewController.tableView reloadData];
NSLog (#"%#", [self.dogTableViewController.dogArray objectAtIndex:0]);
//Prints out "Null" //
[self presentModalViewController:dogNavigationController animated:YES];
[indicator release];
}
When I do all this and Click the Done button,
I get an empty Navigation Screen with NO TABLE in it. Plus I also had some buttons on the dogNavigationController screen. Nothings visible !!
My objective is to just transfer the screen to this new screen (which happens to be a home screen, and not the rootController). Do you think I should go with the modalViewController for this task ? Do you think I should use some other way to transfer data to another screen ?
p.s. I do not want to use PushViewController.
I think you should do
[self.navigationController popToRootViewControllerAnimated:YES];
rather. To get the root view controller, you can do,
DogTableViewController * viewController = [self.navigationController.viewControllers objectAtIndex:0];
[viewController.dogArray addObject:aDog];
Original Answer
Shouldn't you be initializing a navigation controller with the root view controller?
DogNavigationController *temp = [[DogNavigationController alloc] initWithRootViewController:self.dogTableViewController];
I believe I can't disable it because I can't access that UIBarButttonItem programmatically
(with either viewWithTag or rightBarButtonItem).
Any suggestions (short from adding the interface without IB)?
As a test, I also tried adding a button programmatically (on the left of the nav bar), but it did not display in the nav bar.
RELEVANT CODE (In MyEditorViewControler.m):
- (void)textFieldDidBeginEditing:(UITextField *)sender { //successfully executes when keyboard slides in
UINavigationItem *item = self.navigationItem; //item = 0x6420e0 OK. (value at debugger breakpoints)
UIBarButtonItem *doneButton4 = (UIBarButtonItem *) [self.view viewWithTag:44]; //doneButton4 = 0x0, not OK.
doneButton4.enabled = NO;
}
- (void)textFieldDidEndEditing:(UITextField *)sender { //successfully executes when keyboard slides out.
...
UIButton* doneButton = (UIButton *)[self.view viewWithTag:44]; //Attempt to re-enable button.
doneButton.enabled = YES;
}
- (void)viewDidLoad { //Attempt to programmatically add a *left* button to the nav bar. Result: Button does not display in nav bar.
....
UIBarButtonItem *leftBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self action:#selector(done)];
self.navigationItem.leftBarButtonItem = leftBarButtonItem;
[leftBarButtonItem release];
}
DETAILS
I would think this is a common case because that Done button:
a) is a UIBarButttonItem added from IB Library to navigation bar that is in a Scroll View that has some UITextField's.
b) behaves as expected (to save the user-entered data etc),
except for not getting disabled when keyboard appears.
c) IB > Inspector > Bar Button Item Attributes shows:
Identifier = Done
Tag = 44
Class = UIBarButtonItem
You should just be using
UIBarButtonItem *doneButton = self.navigationItem.leftBarButtonItem;
doneButton.enabled = YES;
//Both of these should work, you shouldn't need any type of IBOutlets for this
UINavigationItem *item = self.navigationItem;
UIBarButtonItem *doneButton = item.leftBarButtonItem;
You can listen to a notification (UIKeyboardWillShowNotification) posted when the keyboard slides in:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil];
Then implement -keyboardWillShow:.
-(void)keyboardWillShow {
UIButton *button = self.navigationItem.leftBarButtonItem;
button.enabled = NO;
}
To reenable the button again, do the same for the UIKeyboardDidHideNotification
Currently I have a textfield, which pulls up a NumberPad keyboard for input.
I want the NumberPad keyboard layout, but I need a button to resign that responder.
Is there a way to code the UITextField so that it has a NumberPad keyboard and the space on the left of the keyboard which is blank is replaced with a 'Done', 'Send' or 'Go' button.
Or do I need to create a custom inputView for the UITextField?
I would have thought since there is a blank key on the NumberPad apple would have made it useful for something.
Just try having a UIToolBar with a done button on it as the inputAccessoryView of the text field. In the button click event provide the code to dismiss the keyboard. Think that should suit you.
Or if you want to have a custom button drawn over the keyboard you can search here to get the solution
The empty button is needed if you use phonePad. But you can add inputAccessoryView with 'Apply' and 'Cancel' buttons, and dismiss the number pad with then.
- (void)viewDidLoad
{
[super viewDidLoad];
UIToolbar* numberToolbar = [[UIToolbar alloc]initWithFrame:CGRectMake(0, 0, 320, 50)];
numberToolbar.barStyle = UIBarStyleBlackTranslucent;
numberToolbar.items = [NSArray arrayWithObjects:
[[UIBarButtonItem alloc]initWithTitle:#"Cancel" style:UIBarButtonItemStyleBordered target:self action:#selector(cancelNumberPad)],
[[UIBarButtonItem alloc]initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil],
[[UIBarButtonItem alloc]initWithTitle:#"Apply" style:UIBarButtonItemStyleDone target:self action:#selector(doneWithNumberPad)],
nil];
[numberToolbar sizeToFit];
numberTextField.inputAccessoryView = numberToolbar;
}
-(void)cancelNumberPad{
[numberTextField resignFirstResponder];
numberTextField.text = #"";
}
-(void)doneWithNumberPad{
NSString *numberFromTheKeyboard = numberTextField.text;
[numberTextField resignFirstResponder];
}
You need to do a custom inputview now and assign it to the inputview property of your text field. I am currently researching it, but that is the right, approved way of doing it. It seems overkill just for changing a single button though.