So I thought I'd have a go at building my own simple app. Please go easy on me I'm new to all this! The idea is this. For iPad have a single view controller with a text box and a text field. Text box takes a title, and text field takes the body of a report. There's a button on the page to submit the report, which bundles the two texts into an object and adds it to a table view within the same view controller. I have set the view controller as a delegate with <UITableViewDelegate, UITableViewDataSource> in my header file. My table view works fine for adding items in the viewDidLoad method. But adding items from the text inputs via a UIButton connected to -(IBAction) addItem falls over with: Property 'tableView' not found on object of type 'ReportsViewController'
- (IBAction)addReportItem
{
int newRowIndex = [reports count];
ReportObject *item = [[ReportObject alloc] init];
item.title = #"A new title";
item.reportText = #"A new text";
[reports addObject:item];
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:newRowIndex inSection:0];
NSArray *indexPaths = [NSArray arrayWithObject:indexPath];
[self.tableView insertRowsAtIndexPaths:indexPaths withRowAnimation:UITableViewRowAnimationAutomatic];
}
I understand that I'm trying to call a method within my object but I have other method calls to tableView which work fine. i.e.
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [reports count];
}
I thought this was the point of delegation. I know I'm missing something, but as I say I am new to all this and have looked everywhere for an answer before posting. What do I need to do to send my IBAction message to tableView?
Do you have a tableView instance variable setup in your .h file of the view controller?
The reason you are able to access it in the delegate and data source methods is because they are passed in as part if the methods.
You will need to add the IBOUTLET tableView ivar and connect it to the tableView in your .xib.
Or perhaps your ivar for the tableView is named something else?
Good luck.
I had the same problem.
What helped was to inherit the View Controller from UITableViewController, instead of UIViewController. Not using the protocol names in angled brackets.
The TableView is then linked to the dataSource and delegate via the storyboard (resp. InterfaceBuilder).
The parent class UITableViewController has an IBOutlet tableView defined.
MyViewController.h:
#interface MyViewController : UITableViewController
Related
I have seen similar questions a lot on Stackoverflow and I tried a lot of things but I can't seem to figure this out. I have multiple TableViewControllers and 1 MainViewController. The MainViewController has buttons calling the different TableViewControllers and on selecting a tablecell the tableViewController dismisses.
The problem is that im pushing a new instance of my MainViewController every time I push from either one of my tableViewControllers. I currently use Segues to push between these different controllers.
In short: When switching from TableViewControllers to ViewController I want to prevent the ViewController to get pushed as a new instance because this way its removing my previous data input.
Im pretty sure I have to use either:
[self dismissModalViewController: withCompletion:]
performSegue
prepareForSegue
Or set some global variables in a class and call those, but im not experienced enough yet to implement this correctly.
A simple example of end result would be: 3 textfields in VC. On clicking textfield1 it opens tableview1 and on clicking a cell it updates textfield1. Textfield2 opens tableview2, etc.
Hope im clear enough, could post sample code if needed.
Edit, posting code (keep in mind, segues are performed in storyboard):
TableViewExample.h:
#interface IndustryViewController : UIViewController <UITableViewDelegate, UITableViewDataSource> {
NSArray *tableViewArray;}
#property (nonatomic, strong) IBOutlet UITableView *tableViewIndustry;
TableViewExample.m:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:#"showIndustry"]) {
NSIndexPath *indexPath = [self.tableViewIndustry indexPathForSelectedRow];
ViewController *destViewController = segue.destinationViewController;
destViewController.industryText = [tableViewArray objectAtIndex:indexPath.row];
destViewController.industryTextName = [tableViewArray objectAtIndex:indexPath.row];
}}
Then in ViewController.m, viewDidLoad:
[industry setTitle:industryText forState:UIControlStateNormal];
These are the most important parts I think.
Is the segue of type "Push"? If so you should try dismissing the table view controllers using:
[self.navigationController popViewControllerAnimated:YES];
If the segue is of type "Modal" instead you should do something like this on your table view controller:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
// your logic here
[self dismissModalViewControllerAnimated:YES];
}
As for the data exchange between controllers what I would personally do is creating a public property in the header file of the Table View Controller, like the following:
#property (nonatomic, weak) <Your_UIViewController_Subclass_Here> *mainController
Than, in the main controller, override the prepareForSegue:sender: method to set the newly created property to point to the main controller, like this:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
<Your_Subclass_Of_UITableViewController_Here> *destinationController = segue.destinationController;
destinationController.mainController = self;
}
Now the Table View Controller will have a pointer to the main controller to send the data basically all you have to do is to implement some public method or property in the Main Controller to be called when the user selects a table view row in the table view controller in order to update the text in the textfields or whatever data model you are using.
I have been trying to figure this out for a while and not coming up with a solution. I have a view controller with a table and the first cell of the table is allocated for a button called "Add Friends". When clicked, it takes you to another view controller with a list of contacts in a table. When you click on a person, it goes back to the other view controller and adds the selected person. This is what I have so far.
ContactsViewController.m
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
FirstViewController *newVC = [self.storyboard instantiateViewControllerWithIdentifier:#"newVCSegue"];
newVC.peopleArray = [[NSMutableArray alloc] init];
Person *user = [contactsList objectAtIndex:indexPath.row];
NSArray *userKeys = [NSArray arrayWithObjects:#"FirstName", #"LastName", nil];
NSArray *userObjects = [NSArray arrayWithObjects:user.firstName, user.lastName, nil];
NSDictionary *userDictionary = [NSDictionary dictionaryWithObjects:userObjects forKeys:userKeys];
[newVC.peopleArray addObject:userDictionary];
[self.navigationController pushViewController:newVC animated:YES];
[tableView deselectRowAtIndexPath:indexPath animated:YES];
}
FirstViewController.h
#property (strong, nonatomic) NSMutableArray *peopleArray;
FirstViewController.m
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
//...
if (indexPath.row == 0) {
contactName.text = #"Add Person";
imgView.image = [UIImage imageNamed:#"plus-icon.png"];
} else {
NSString *firstName = [[peopleArray objectAtIndex:(indexPath.row)-1] objectForKey:#"firstName"];
NSString *lastName = [[peopleArray objectAtIndex:(indexPath.row)-1] objectForKey:#"lastName"];
contactName.text = [NSString stringWithFormat:#"%# %#", firstName, lastName];
}
return cell;
}
This lets me add one friend so far and if I decided to add another to the list, it replaces the first friend added.
What's basically happening is every time you select a new contact, you're recreating the array in the first view controller, hence it is replacing things. You ideally want to try and avoid getting the FirstViewController using the storyboard like that as well, it's pretty bad practice and may well lead to various problems later.
What I'd suggest in this situation is creating a protocol (look at the delegate pattern). This way, what you'd have is :
Use taps "Add Contact"
Contacts list appears, and FirstViewController is set as the delegate
User taps contact to add them
ContactsViewController informs the delegate of the user that was selected
FirstViewController adds the user, and dismissed the view controller
This is generally the approach you'd take, and it's pretty simple to implement. Start with the protocol
#protocol ContactsDelegate
-(void) contactsViewController:(ContactsViewController *)vc didSelectContact:(Person *)person;
#end
Then, make your FirstViewController implement this protocol. To do this, in your header file, in the angle brackets after the name (< >) add ContactsDelegate
In the implementation of FirstViewController, add the new method of the contacts delegate.
In your ContactsViewController.h file, add
#property (nonatomic, assign) NSObject<ContactsDelegate> *delegate;
Then when you display your contacts view controller, set the delegate
userVc.delegate = self;
[self presentModalViewController:userVc];
Then, in the user view controllers didSelectRowAtIndexPath:, simply inform the delegate that you've selected that person
[delegate contactsViewController:self didSelectContact:[contactsList objectAtIndex:indexPath.row]];
And lastly, in your FirstViewController, in the delegate method you added, we need to ADD the user to the list, not re-create the list
[peopleArray addObject:person];
And that should do what you're after :)
From what I understand, you are instantiating a new FirstViewController every time you select a contact in the ContactsViewController. Instead, you should reference the original FirstViewController (perhaps save it before transitioning to ContactsViewController), and use this reference to add the contact to the original array [original.people addObject:userDict]. As long as you make sure to reload the table, this should work.
i am having a tableview in which custom cells are loaded.Custom cell has a button on click of which a pickerview will open which will have options to choose from.
The problem is that modalViewController method is not working, it is giving the following error.
Selector *sel = [[Selector alloc]initWithNibName:#"Selector" bundle:nil];
[self PresentModalViewController:sel animated:YES];
error:property presentModalViewController not found on object of type CustomCell *...and selector is the pickerview controller class...the method is written in ibaction function in customcell.m file
How can v call other view from custom cell?
thanks
First, naming your class "Selector" is a horribly confusing idea. You should use something more descriptive, and something that is not already an obj-c keyword.
As for your problem, I think you should use a delegate to get a reference from your cell view to the controller. In your custom cell view class, do something like:
#property (nonatomic, assign) id delegate;
// implementation
#synthesize delegate = _delegate;
// in your cell... method
[self.delegate presentPicker];
Here, the delegate ivar would point back to your view controller. To set that up, find the place where you alloc your cell, and do
ACell *aCell = [ACell alloc] init];
aCell.delegate = self;
Tried programming more in an OO manner.
NOTE:
Seems the problem is related to having a UITableView(which is a type of scrollview) ontop of another UIScrollView. Trying to get clarification on how to make this work.
So i made a UITableViewController subclass and add that as an object in my Mainview class.
#interface NRTableVC : UITableViewController <UITableViewDelegate, UITableViewDataSource>
{
NSArray * tableLabelsArray;
}
#end
The table scrolls and displays OK.
But my
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSLog(#"Did select row");
}
method never gets called.
in my Mainview class I use it as follows
RTable = [[[NRTableVC alloc] initWithStyle:UITableViewStylePlain]autorelease];
[scrollview addSubview:RTable.view];
I was wondering so I need to somehow set self as the delegate or something like that.
Anybody able to give me some hints as to what is wrong?
Many Thanks,
Code
Have you set delegate for tableview? Without delegate set it won't call any delegate methods.
What I usually do is, in the View I want the tableview is create an instance
I do this in ViewDidLoad or the LoadView methods
UITableView *tableView = [UITableView alloc] init...
tableView.delegate = self;
tableView.datasource = self;
Then I add it as a subview of the current view.
This means your MainView needs to implement the tableView datasource and delegate methods.
Edit:
If you use interface builder, remember to drag the datasource and delegates to the fileowner
Is anything retaining RTable? You're autoreleasing it but you don't show any code that indicates it's being retained anywhere.
My problem is how to reload the tableview
I have 2 viewcontrollers.
In first Viewcontroller I have one tableview. if I select any row in tableview it goes to second viewcontroller.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath;
{
NextPageController *nextView = [[NextPageController alloc] initWithNibName:#"NextPageView" bundle:nil];
[self.navigationController presentModalViewController:nextView animated:YES];
[nextView release];
}
in second view controller I have one textfield. If I enter any value into the textfield I need to disaplay that value into the first viewcontroller tableview.
can any one help me?
Thanks in advance.
give NextPageController a protocol and a delegate,just like this:
#protocol (NextPageControllerDelegate)
-(void)displayString:(NSString *)inputString;
#end
#interface FirstTableViewController : UITableViewController {
id<NextPageControllerDelegate> stringDelegate;
}
#property (nonatomic, assign) id<NextPageControllerDelegate> stringDelegate;
and in the .m file:
#implementation
#synthesize stringDelegate;
then, when you alloc the NextPageViewController, insert this:
(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath;
{
NextPageController *nextView = [[NextPageController alloc] initWithNibName:#"NextPageView" bundle:nil];
nextView.stringDelegate = self;
[self.navigationController presentModalViewController:nextView animated:YES];
[nextView release];
}
of course, your table view controller must conforms to the NextPageControllerDelegate protocol, and give the implementation of
-(void)displayString:(NSString *)inputString;
after that, when you are in nextView, and want to have the table view display that string, you may do this:
if(nil != self.stringDelegate)
[self.stringDelegate displayString:someString];
ant then it's done
Make Global String ,
Assign it in secondViewController and when you pop from secondViewController to FirstViewController , viewWillAppear will call.
make logic such it uses global variable and then reload table ..
you need to have a global NSString variable in appDelegate that stores your text of text filed in second view.
Now, when you come back to previous page, in
- (void) viewWillAppear
{
[yourDataSourceArray addObject:appDelegate.yourGlobalString];
[yourTableView reloadData];
}
and yes make sure that your yourDataSourceArray is NSMutableArray.
Hope it helps you.
You could create a property for an NSString in your first view, and a property for an instance of the first view controller (UITableViewController?) in your second view. Then upon saving or popping or whatever you're doing in the second view controller once you have entered the text you want, you could set the property of the NSString, pop the view, and reload the tableView in viewWillAppear. Alternatively you could use delegation as Lewen described.
create global NSMutableArray.
Store and add all data in that NSMutableArray.
In -(void) viewwillappear of first class, call [tablename reloadData];