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)
Related
There is undoubtedly a wealth of information re: memory management in iOS. Having read a huge amount about it, I am still unclear as to 'BEST' practice for certain situations. Please can I seek clarification on two examples below...
I have an NSMutableArray which is acting as a datasource for a tableView and a UIBarButtonItem called editButton both declared as follows:
#interface MyTableViewController : UITableViewController {
NSMutableArray *datasourceArray;
UIBarButtonItem *editButton;
}
#property (nonatomic, retain) NSMutableArray *datasourceArray;
#property (nonatomic, retain) UIBarButtonItem *editButton;
#end
I have then synthesized them and alloc'd/init'd them as follows:
#implementation
#syntesize datasourceArray, editButton;
-(void)viewDidLoad {
self.datasourceArray = [self retrieveDatasourceArray];
self.editButton = [[UIBarButtonItem alloc] initWithTitle:#"Edit" style:UIBarButtonItemStylePlain target:self action:#selector(editTable)];
[self.navigationItem setRightBarButtonItems:[NSArray arrayWithObjects:editButton, nil] animated:NO];
[editButton release];
}
-(void)retrieveDatasourceArray {
NSMutableArray *datasource = [[[NSMutableArray alloc] initWithObjects #"example1", #"example2", nil] autorelease];
return datasource;
}
-(void)dealloc {
[datasourceArray release];
[editButton release];
[super dealloc];
}
Question 1: The NSMutableArray
As you can see, I have separated the actual creation of the array into a different method as there is lots of code retrieving from core data and sorting going on (not needed for this question) which I wanted to separate out. I have therefore chosen to return an NSMutableArray which is autoreleased and set this to the self.datasourceArray which is defined in the header file. Is this a sensible and leak free way of implementing this?
Question 2: The Edit Button
As I need to change the title and style of the editButton later, I need to have access to it, hence declaring it. I then alloc/init it in the viewDidLoad method and added it to an array (with some other buttons not shown here) before using this array to add the buttons to the navigationBar. I have then released the editButton as I have alloc'd it and handed it to an array. Is this necessary or essential or even in the correct place given my dealloc method?
Many thanks in advance
EDIT: Further question 3:
When accessing either of these ivars elsewhere in my code (say when calling [datasourceArray count] or resetting the title of the 'Edit' button to 'Done', should I use self. notation or not?
EDIT: Further question 4:
Elsewhere I have used the following code to initialise a synthesised NSMutableArray. Given the below answers, is this more leaky...?
[self setDatasourceArray: [[NSMutableArray arrayWithArray: [self retrieveDatasourceArray]];
1st point for the Array
NSMutableArray *datasource = [[[NSMutableArray alloc] initWithObjects #"example1", #"example2", nil] autorelease];
return datasource;
here you are doing it correct..returning an autoreleased object..which will be retained by the variable because you defined it to be of type retain (when you did #property).
2nd point for the edit button
self.editButton = [[UIBarButtonItem alloc] initWithTitle:#"Edit" style:UIBarButtonItemStylePlain target:self action:#selector(editTable)];
[self.navigationItem setRightBarButtonItems:[NSArray arrayWithObjects:editButton, nil] animated:NO];
[editButton release];
Here you are apparently over releasing the object..
remember the variable retains the new variable you defined..so edit button retains the new bar button item..so releasing it . is necessary one time..which you do in dealloc..but releasing here also will lead to overrelease..to solve this just remove the release line and update your code to be like this
self.editButton = [[[UIBarButtonItem alloc] initWithTitle:#"Edit" style:UIBarButtonItemStylePlain target:self action:#selector(editTable)]autorelease];
[self.navigationItem setRightBarButtonItems:[NSArray arrayWithObjects:editButton, nil] animated:NO];
Here you see that the new instance which will be created will be auto released..and its value will be retained by your variable
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
I am currently adding a UISegmentedControl to the toolbar in the navigation controller programmatically (as seen below).
This approach works fine, I have my UISegmentedControl, it fires the selector that I have setup no problems.
Problem is - I would like to use the selectedIndex of this control in order to query my data model and present a different view of data for each 'segment' - but I am having trouble getting the selectedIndex.
In my travels I have been consulting the 'Top Songs' example code provided by Apple.
In this code they build up their interface via UISegmentedControl object in the view controller and IB. In doing so they can access the UISegmentedControl's selectedIndex. I am adding mine programmactically and do not have this freedom.
'SHOULD' I have a UISegmentedControl defined in my view controller? If so, if I want to continue building my menu programmactically, how do I go about accessing the information from the control buried within the navigation controller's UIToolBar?
I'm clearly missing something basic. Any assistance is always greatly appreciated :)
[self.navigationController setToolbarHidden:NO];
// Set up the edit and add buttons.
self.navigationItem.leftBarButtonItem = self.editButtonItem;
UIBarButtonItem *addButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:self action:#selector(insertNewObject)];
self.navigationItem.rightBarButtonItem = addButton;
[addButton release];
NSArray *tabitems = [NSArray arrayWithObjects:#"ONE", #"TWO", #"THREE", #"FOUR", nil];
UISegmentedControl *tabs = [[UISegmentedControl alloc] initWithItems:tabitems];
[tabs addTarget:self
action:#selector(pickedSegment:)
forControlEvents:UIControlEventValueChanged];
tabs.segmentedControlStyle = UISegmentedControlStyleBar;
tabs.frame = CGRectMake(60, 8, 180, 30);
tabs.selectedSegmentIndex = 0;
//[self.navigationController.navigationBar addSubview:tabs];
[self.navigationController.toolbar addSubview:tabs];
[tabs release];
You need to have tabs defined in your .h file -
#interface YourViewController : UIViewController
....
UISementedControl *tabs;
....
#end
....
#property (nonatomic, retain) UISegmentedControl *tabs;
Then, after the [tabs release]; line, you should still be able to access the object, as it is a retained property - access the selectedItemIndex as normal.
I'm developing iphone app with UITabBarController as main view. Every ViewController in each tab is UINavigationController which must have a same button in leftBarButtonItem. Can I inherit some class from UINavigationController and override it's -(id) initWithRootViewController:(UIViewController *)rootViewController method to realize this ?
I made something like this. But this code doesn't work;
#implementation MainNavagaionController
-(id) initWithRootViewController:(UIViewController *)rootViewController {
if (self = [super initWithRootViewController:rootViewController]) {
// Set user name title
UIBarButtonItem * userNameButton = [[UIBarButtonItem alloc] initWithTitle:#"Title"
style:UIBarButtonItemStylePlain
target:self
action:nil];
self.navigationItem.leftBarButtonItem = userNameButton;
[userNameButton release];
}
return self;
}
#end
What is shown in the navigation bar is the navigationItem of the current viewcontroller in each navigation controller. You are one level too high when you are setting this left bar button.
I have an UITableViewController which I would like to add UIToolbar to with one button. In the
- (void)viewDidLoad;
method of UITableViewController I have:
- (void)viewDidLoad {
[super viewDidLoad];
UIBarButtonItem *button = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAdd
target:self
action:#selector(pressButton1:)];
self.navigationItem.title = #"Some title";
self.navigationItem.leftBarButtonItem = button;
}
Unfortunately I don't see the toolbar when I run my app.
Any hints? Should I do something more?
The navigationItem property of a view controller is useless if that controller is not displayed inside a UINavigationController.
If your view controller is inside a navigation controller I don't know what the problem is.
Otherwise you can use an UINavigationItem but you need to create a UINavigationBar yourself.
Either in the Interface Builder (add a UINavigationBar and add a UINavigationItem, then connect the UINavigationItem to a property outlet declared your view controller (you don't need to connect the Bar).
Or in your code :
UIBarButtonItem *item = [[UIBarButtonItem alloc]
initWithBarButtonSystemItem:UIBarButtonSystemItemAdd
target:self action:#selector(pressButton1:)];
UINavigationItem* navItem = [[UINavigationItem alloc] init];
navItem.rightBarButtonItem = item;
navItem.title = #"Your title";
naviBar = [[UINavigationBar alloc] init];
naviBar.items = [NSArray arrayWithObject:navItem];
naviBar.frame = CGRectMake(0.0, 0.0, self.view.frame.size.width, 44.0);
[self.view addSubview:naviBar];
[navItem release];
Your method requires an autorelease:
- (void)viewDidLoad {
[super viewDidLoad];
UIBarButtonItem *button = [[[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:self action:#selector(pressButton1:)] autorelease];
self.navigationItem.title = #"Some title";
self.navigationItem.leftBarButtonItem = button;
}
There's nothing wrong with your code per se. Your question states you want to add an UIToolBar to your view? Really? Or do you just want to add a button to the NavigationItem for UITableView?
If you don't have to use a UITableViewController and are not using a UINavigationController in your app already, you can set your view controller up as a regular UIViewController with a toolbar and tableview.
To do this in IB, drag out a UIViewController object and add a toolbar and tableview. Hook up outlets for both and set the delegate and datasource of the tableview to Files Owner. Add any other toolbar items or buttons and give them outlets and methods if you need them for buttons, etc. In your ViewController.h file, make sure you sign it up to conform to the UITableViewDataSource and UITabBarDelegate:
#interface ViewController : UIViewController <UITableViewDataSource, UITabBarDelegate>
From there, just build out your tableview delegate and datasource methods like you normally would, and write your button action methods for any buttons you added to the toolbar.
You just didn't show the toolbar. It is hidden by default. To fix it, you just put this line of code:
self.navigationController.toolbarHidden = NO;
I tried it and it worked. Just make sure that you put in the implementation file's viewDidLoad method.