How can I override the "Done" button in ABNewPersonViewController - iphone

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];
}

Related

iPhone keyboard toolbar class

I was following the tutorial at http://www.youtube.com/watch?v=YgeuauhsDhQ to create the previous, next, done button above the keyboard that slides up with the keyboard.
However, I noticed he is doing it to a specific view and I do not want to have to copy / paste this into every view that has a text field. I tried making a class for this but then realised that he resigns the keyboard based on the field that is currently in focus.
Is anyone aware of a class already out there so I do not have to create this for every view controller that has a text field? Or know how I can modify his version to work as a class I could import into my view controllers?
Sorry I am sorta new to iPhone development.
//KeyboardToolbar.h
#import <Foundation/Foundation.h>
#interface KeyboardToolbar : NSObject
{
UIToolbar *keyboardToolbar;
}
#property (nonatomic, retain) UIToolbar *keyboardToolbar;
-(void)resignKeyboard:(id)sender;
-(void)previousField:(id)sender;
-(void)nextField:(id)sender;
#end
and
//KeyboardToolbar.m
#import "KeyboardToolbar.h"
#implementation KeyboardToolbar
#synthesize keyboardToolbar;
- (void)loadToolbar
{
if (keyboardToolbar == nil) {
keyboardToolbar = [[UIToolbar alloc] initWithFrame:CGRectMake(0.0, 0.0, 100.0, 44.0)];
UIBarButtonItem *previousButton = [[UIBarButtonItem alloc] initWithTitle:#"Previous" style:UIBarButtonItemStyleBordered target:self action:#selector(previousField:)];
UIBarButtonItem *nextButton = [[UIBarButtonItem alloc] initWithTitle:#"Next" style:UIBarButtonItemStyleBordered target:self action:#selector(nextField:)];
UIBarButtonItem *extraSpace = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil];
UIBarButtonItem *doneButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self action:#selector(resignKeyboard:)];
[keyboardToolbar setItems:[[NSArray alloc] initWithObjects:previousButton, nextButton, extraSpace, doneButton, nil]];
}
}
- (void)resignKeyboard:(id)sender
{
//Resign the keyboard here? I would need to get the element with the keyboard then hide it.
}
#end
How would I make this class more dynamic so I can use it in all of my views with very little copy / pasting into each controller.
I'm new at this too but I think I can help. I did something similar. I set up a helper object to act as delegate for whatever view controller the user was currently interacting with. You'll write your code for making the toolbar appear. You'll implement those methods in the .m file of your helper (delegate) class. Add your helper class as an observer for the UIKeyboardDidShowNotification. So, in the viewDidLoad of my UIViewControllers, I set the delegate to my helper object. Then, when a keyboard appears on that current view controller, the notification is sent to the delegate (helper object).
Here's a little code to clarify:
In init method of helper object:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardWasShown:)
name:UIKeyboardDidShowNotification
object:nil];
Then, in the .m file of your helper object, in keyboardWasShown:, call [self makeToolbar] or whatever your methods are called. Sorry for the verbosity. Like I said, I'm pretty new but I hope this helps.
EDIT: So I made a simple little test/illustration. Here's the .h for my ViewController class. This is the ViewController that will contain the UITextFields, both of which bring up a keyboard and add a toolbar to it via a delegate method. Sorry for the lengthy response, but I thought it would be most useful to just give you all of what got it working for me.
#import <UIKit/UIKit.h>
#import "TextFieldDelegate.h"
#interface ViewController : UIViewController <UITextFieldDelegate> {
//Outlets for 2 UITextFields, added in IB. Selecting either will add the toolbar to your keyboard as long as the delegate is assigned and the methods in the delegate are implemented as shown.
IBOutlet UITextField *myTextField;
IBOutlet UITextField *myOtherTextField;
//we'll need to set the delegate for this class, so go ahead and declare a variable and make it a property (and synthesize it in the .m).
id delegate;
}
#property (nonatomic, strong) id delegate;
#end
ViewController.m:
#implementation ViewController
#synthesize delegate;
//removed method stubs and only left my modifications to the template.
- (void)viewDidLoad
{
[super viewDidLoad];
//create an instance of your delegate class and set it as the view controller's delegate.
//the text fields need their parent as the delegate, and the parent in turn assigns the helper class as its delegate.
//really this is the whole point, that you can just assign delegates to your view
//controllers and text fields and they can all access the one method implementation
//instead of each having to implement it separately themselves.
[myTextField setDelegate:self];
[myOtherTextField setDelegate:self];
TextFieldDelegate *myDelegate = [[TextFieldDelegate alloc] init];
[self setDelegate: myDelegate];
//set the delegate's currentViewController property so that we can add a subview to this View.
[delegate setCurrentViewController:self];
}
- (void)textFieldDidBeginEditing:(UITextField *)textField {
//set the delegate's current text field property so that we can resignFirstResponder.
[delegate setCurrentTextField:textField];
}
#end
Now here's the delegate class:
TextFieldDelegate.h:
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#interface TextFieldDelegate : UIViewController <UITextFieldDelegate> {
UIViewController *currentViewController;
UITextField *currentTextField;
UIToolbar *keyboardToolbar;
CGSize kbSize;
}
- (void)loadToolbar;
#property (nonatomic, strong) UIViewController *currentViewController;
#property (nonatomic, strong) UITextField *currentTextField;
#end
And the implementation of the delegate:
TextFieldDelegate.m:
#import "TextFieldDelegate.h"
#implementation TextFieldDelegate
//synthesize properties so that View Controllers can set them as needed.
#synthesize currentViewController, currentTextField;
- (id)init {
self = [super init];
if (self) {
//register for the keyboard did show notification
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardWasShown:)
name:UIKeyboardDidShowNotification
object:nil];
}
return self;
}
- (void)keyboardWasShown:(NSNotification *)aNotification {
//get the keyboard size for positioning the toolbar. (depending on where you want it, I guess, I was just imagining it directly above the keyboard.
NSDictionary *info = [aNotification userInfo];
kbSize = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;
//call the method to make the toolbar appear.
[self loadToolbar];
}
- (void)loadToolbar
{
if (keyboardToolbar == nil) {
//setting the position of the toolbar.
CGRect frameRect = self.view.frame;
frameRect.size.height -= kbSize.height;
keyboardToolbar = [[UIToolbar alloc] initWithFrame:CGRectMake(0.0, frameRect.size.height - 60.0, 320.0, 60.0)];
//your code for toolbar setup.
UIBarButtonItem *previousButton = [[UIBarButtonItem alloc] initWithTitle:#"Previous" style:UIBarButtonItemStyleBordered target:self action:#selector(previousField:)];
UIBarButtonItem *nextButton = [[UIBarButtonItem alloc] initWithTitle:#"Next" style:UIBarButtonItemStyleBordered target:self action:#selector(nextField:)];
UIBarButtonItem *extraSpace = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil];
UIBarButtonItem *doneButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self action:#selector(resignKeyboard:)];
[keyboardToolbar setItems:[[NSArray alloc] initWithObjects:previousButton, nextButton, extraSpace, doneButton, nil]];
//this line sends a message to the currently active view controller, telling it to add as a subview the toolbar that we have just created.
[[currentViewController view] addSubview:keyboardToolbar];
}
}
- (void)resignKeyboard:(id)sender {
[currentTextField resignFirstResponder];
[keyboardToolbar removeFromSuperview];
//set the toolbar to nil so that when you touch the other text field, it will build again.
keyboardToolbar = nil;
}
#end
So, there it is. Like I said, I'm new as well. But I hope this helps.
#jostster
EDIT: Oh, if you move the call to the custom delegate method loadToolbar into the ViewController's textFieldDidBeginEditing method, it works. In ViewController.m
- (void)textFieldDidBeginEditing:(UITextField *)textField {
//set the delegate's current text field property so that we can resignFirstResponder.
[delegate setCurrentTextField:textField];
[delegate loadToolbar];
}
And then you can just remove the [self loadToolbar] call from keyboardWasShown in the delegate implementation.
EDIT: to get your next and previous buttons working, first give your delegate a property: NSArray *textFieldArray. Then, in your view controller, create an array of your text fields (we'll call it tfArray), and then do [delegate setTextFieldArray:tfArray]; Then implement your previousField: and nextField: methods something like this:
- (void)previousField:(id)sender {
//get the index in the array of your currently active textField
int activeIndex = [textFieldArray indexOfObject:currentTextField];
//make sure you're not going to try to access an empty spot in the array
if (activeIndex > 0) {
UITextField *previousField = [textFieldArray objectAtIndex:activeIndex - 1];
[previousField becomeFirstResponder];
}
}
//same again but with a plus sign
- (void)nextField:(id)sender {
int activeIndex = [textFieldArray indexOfObject:currentTextField];
if (activeIndex < 1) {
UITextField *nextField = [textFieldArray objectAtIndex:activeIndex + 1];
[nextField becomeFirstResponder];
}
}
There might be a cleaner way to do this but the amount of code is so minimal that I don't feel like typing it twice is that big of a deal. That's the gist of it though and if you had a whole lot of text fields to advance through, you would just want to say something like:
if (activeIndex < [textFieldArray count]) {
//advance to the next textField
}
I found this project on Github that might be useful.
https://github.com/rexfinn/RFKeyboardToolbar

How to use UISegmentControl to change detailViewController of CoreData

I have implemented a UISegmentControl as the rightBarButton of my detailViewController.
This view controller displays that of the information passed through from a UITableView.
This UITableView's cells are populated with CoreData attribute values.
What I want to do is enable the user to go up and down in the list via the detailViewController. Instead of having to make the user have to go back to the rootViewController, they'll gain the ability to scroll through via the UISegmentControl.
I currently have this in my detailViewController.m
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
// Setting up UISegmentedControl
// Segmented Control - Custom right bar button with a view
UISegmentedControl *segmentedControl = [[UISegmentedControl alloc] initWithItems:
[NSArray arrayWithObjects:
[UIImage imageNamed:#"arrowdown.png"],
[UIImage imageNamed:#"arrowup.png"],
nil]];
[segmentedControl addTarget:self action:#selector(segmentAction:) forControlEvents:UIControlEventValueChanged];
segmentedControl.frame = CGRectMake(0, 0, 75, 30);
segmentedControl.segmentedControlStyle = UISegmentedControlStyleBar;
segmentedControl.momentary = YES;
UIBarButtonItem *segmentBarItem = [[UIBarButtonItem alloc] initWithCustomView:segmentedControl];
[segmentedControl release];
self.navigationItem.rightBarButtonItem = segmentBarItem;
[segmentBarItem release];
}
This is then attached to the following method, for detecting the tapped control.
- (void)segmentAction:(id)sender
{
UISegmentedControl* segCtl = sender;
// the segmented control was clicked, handle it here
if([segCtl selectedSegmentIndex]==0){
NSLog(#"You clicked the down arrow - the segment clicked was %d", [segCtl selectedSegmentIndex]);
}else {
NSLog(#"You clicked the up arrow - the segment clicked was %d", [segCtl selectedSegmentIndex]);
}
}
I am also curious as to whether or not anyone knows how to detect whether or not there is anymore to go to. So say, if the note loaded is in the first position, then the down arrow is disabled, and if the note loaded is in the last position, the up arrow is disabled. Is this even possible?
Any help would be greatly appreciated.
I'd suggest you to create a formal protocol MyDataSource which provides methods for accessing the data. As a minimum, there must be a method to get number of data objects and object for a specified index.
In your DetailViewController you should have a reference to an object which conforms to MyDataSource. I'd recommend you to use instance of RootViewController as a data source for DetailViewController.
You should also keep track of index of the object that is currently displayed in DetailViewController and update UI appropriately.

Problem With Editing TableView Code

I have made a table containing an array editable (you can delete rows) by swiping and clicking delete. However, I am trying to make it so you click the edit button in the navbar and the red minus sign comes up next to each cell.
I am using the code from my book but the tableView variable is not working. I can't figure out how it is working in the book but not in my project.
I think it is because the book's class is a subclass of UITableViewController while mine is a UIViewController with a UITableViewController Object in it. So how can I get this to work?
I have an IBOutlet for tableView in my interface file too.
Here is the relevant code:
#import "RoutineTableViewController.h"
#import "AlertPrompt.h"
#implementation RoutineTableViewController
#synthesize myArray;
#synthesize myData;
- (void)viewDidLoad
{
myArray = [[NSMutableArray alloc] init];
myData = [[NSMutableArray arrayWithContentsOfFile:#"mydata"] retain];
if (myData == nil)
{
myData = [NSMutableArray array];
}
UIBarButtonItem * addButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:self action:#selector(showPrompt)];
[self.navigationItem setLeftBarButtonItem:addButton];
[addButton release];
UIBarButtonItem *editButton = [[UIBarButtonItem alloc]initWithTitle:#"Edit" style:UIBarButtonItemStyleBordered target:self action:#selector(toggleEdit)];
self.navigationItem.rightBarButtonItem = editButton;
[editButton release];
[super viewDidLoad];
}
-(IBAction)toggleEdit:(id)sender
{
[self.tableView setEditing: !=self.tableView.editing animated:YES];
if (self.tableView.editing)
[self.navigationItem.rightBarButtonItem setTitle:#"Done"];
else
[self.navigationItem.rightBarButtonItem setTitle:#"Edit"];
}
You need to set the editingStyle of each UITableViewCell to be UITableViewCellEditingStyleDelete. This should be done when creating the cell within the table view's delegate (tableView:cellForRowAtIndexPath:)
This might look like I am assuming you are a total newbie, but trust me, many people forget this (I'm not saying I'm an expert either), but you should check if your edit button is connected to the corresponding IBOutlet in Interface Builder.
EDIT: Forgot to ask, is your button an IBOutlet (IB element)? Or is it created programmatically? If the second is true, then you should NOT use IBActions for programmatically created objects (hence Interface Builder Action = IBAction)

How to create an action for a UITabBarItem?

I have created a UITabBar and UITabBarItems without UITabBarController on it, now i want to know how to place an action on click of the UITabBarItem.What is the method i should use for action on UITabBarItem?
You can't set an action on a UITabBarItem object directly. Instead, your view controller should implement the following UITabBarDelegate method:
- (void)tabBar:(UITabBar *)tabBar didSelectItem:(UITabBarItem *)item;
This method is called when the user selects a tab (i.e. UITabBarItem).
Are you using a UINavigationController? If so, from the active view controller subclass you get the navigationItem and add the buttons to it, e.g.:
- (void) viewWillAppear:(BOOL)animated;
{
[super viewWillAppear: animated];
UIBarButtonItem * leftButtonItem = [[[UIBarButtonItem alloc] initWithTitle: #"Don't Show Again" style: UIBarButtonItemStyleBordered target: self action: #selector(permanentlyCloseWelcomeView)] autorelease];
[[self navigationItem] setLeftBarButtonItem: leftButtonItem];
}
Can you get away with using instances of UIToolbar and UIBarButtonItem instead? It could be more straightforward.
toolBar = [[UIToolbar alloc] init];
newPlayerItem = [[UIBarButtonItem alloc] initWithTitle:#"+"
style:UIBarButtonItemStyleBordered
target:self
action:#selector(newPlayer:)];
NSArray *toolBarItemsArray = [[NSArray alloc] initWithObjects:newPlayerItem, nil];
[toolBar setItems:toolBarItemsArray];
[toolBarItemsArray release];
There is a better method than didSelectItem:
for each TabBarItem you create an action:
[item1 setAction:#selector(pressItem1:)];
[item2 setAction:#selector(pressItem2:)];
[item3 setAction:#selector(pressItem3:)];
[item4 setAction:#selector(pressItem4:)];
and then you can use the new actions:
-(void)pressItem1:(UITabBarItem *) item1 {<br/>
// Here comes your code which<br/>
// occurs after pressing item1.<br/>
}
That works for me

iphone - programmatically change navigation bar button to activity indicator

I have added a refresh UIBarButtonItem to my navigation bar on my iPhone app. When the user taps the button I'd like the refresh button to change to the animated activity indicator and once the operation (in this case a download) is complete switch the activity indicator back to the refresh button.
I have added the refresh button using IB. Then on the button tap I create a new activity indicator and keep an pointer to the original refresh button. Like so:
refreshButtonItem = self.navigationItem.leftBarButtonItem;
if (activityButtonItem == nil)
{
activityIndicator = [[UIActivityIndicatorView alloc] initWithFrame:CGRectMake(0, 0, 20,20)];
activityButtonItem = [[UIBarButtonItem alloc]initWithCustomView:activityIndicator];
}
self.navigationItem.leftBarButtonItem = activityButtonItem;
[activityIndicator startAnimating];
So far, so good. Problem is that when my download finishes and I try to re-add the refresh button (using the following):
[activityIndicator stopAnimating];
self.navigationItem.leftBarButtonItem = refreshButtonItem;
I get the following error:
[UIBarButtonItem retain]: message sent to deallocated instance
I'm not explicitly calling release.
A) When/where is this being deallocated
B) Is there a better way to achieve what I'm looking for?
When you assign the activityButtonItem to leftBarButtonItem, the item that leftBarButtonItem used to point to is released. The leftBarButtonItem (and all properties with the retain option) are implemented similarly to this:
- (void)leftBarButtonItem:(UIBarButtonItem *)newItem {
if (newItem != self.leftBarButtonItem) {
[self.leftBarButtonItem release];
leftBarButtonItem = [newItem retain];
}
}
If you want to use the refreshButtonItem after reassigning the leftBarButtonItem, change your first line to:
refreshButtonItem = [self.navigationItem.leftBarButtonItem retain];
Since iOS 5 with the introduction of ARC you no longer need to do retain.
Solution can be obtained as #cagreen explained while refreshButtonItem can be stored as class property, as well as loadingButton and loadingView.
In your interface declare:
#property (strong, nonatomic) UIBarButtonItem *refreshButton;
#property (strong, nonatomic) UIBarButtonItem *loadingButton;
#property (strong, nonatomic) UIActivityIndicatorView *loadingView;
Init loadingButton and loadingView in your viewDidLoad method:
self.loadingView = [[UIActivityIndicatorView alloc] initWithFrame:CGRectMake(0, 0, 20, 20)];
self.loadingButton = [[UIBarButtonItem alloc] initWithCustomView:self.loadingView];
Then to show the loading spinner you can simply do:
// Shows loading button
- (void)showLoadingView {
// Keep reference to right bar button
if (self.navigationItem.rightBarButtonItem) {
self.refreshButton = self.navigationItem.rightBarButtonItem;
}
// Start animating and assign loading button to right bar button
[self.loadingView startAnimating];
self.navigationItem.rightBarButtonItem = self.loadingButton;
}
And to hide:
// Hides loading button
- (void)hideLoadingView {
[self.loadingView stopAnimating];
self.navigationItem.rightBarButtonItem = self.refreshButton;
}