how to make pushViewController: work in the following case? - ios

I have a Navigation Controller embedded in a RootViewController. The elements present inside the RootViewController are a segmented control and a UIView. As the different segments are selected, the UIView displays a corresponding ViewController.
Now each of the ViewController being displayed in the UIView is a UITableViewController.
I need to display the table values as per the segment selections. And also if any of the row of the table is selected, it has to move to a newer ViewController with the detailed view.
I am able to display the UITableViewControllers as per the segment change. However, when i click on the row of the table, the newer ViewController that comes up is blank (Black Screen) even though I have created the particular ViewController in the Storyboard and set up an identifier as well.
Code for the Segmented Control change in ViewController.m
- (void)segmentedControlChangedValue:(UISegmentedControl*)segmentedControl {
int selIndex = segmentedControl.selectedIndex;
if (selIndex == 0)
{
aTable = [[aTableViewController alloc] init];
aTable.view.frame = self.myView.bounds;
[self.myView addSubview:aTable.view];
[self addChildViewController:aTable];
}
if (selIndex == 1)
{
bTable = [[bTableViewController alloc] init];
bTable.view.frame = self.myView.bounds;
[self.myView addSubview:bTable.view];
[self addChildViewController:bTable];
}
if (selIndex == 2)
{
//NSLog (#"Well you selected a 3rd option");
}}
Code for the didSelectRowAtIndexPath:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
[super tableView:tableView didSelectRowAtIndexPath:indexPath];
newDetailController* newController = [self.storyboard instantiateViewControllerWithIdentifier:#"Detail"];
newController = [[newDetailController alloc] init];
[[self navigationController] pushViewController:newController animated:YES];}
The pushViewController pushes an empty view for me even though I have specified the ViewController to use in the Storyboard design and also using an identifier.
Please help me in understanding what I must do to rectify this error.
UPDATE - The above problem has been resolved.
The problem I face now is being unable to access the data in the newViewController.
I pass the data to the newViewController as follows:
Create a class named newClass which contains all the elements I want to pass across.
Create a NSMutableArray "newObjects" in the firstViewController.
Create each newClass object at this method.
(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath object:(PFObject *)object
{
static NSString *CellIdentifier = #"Cell";
customBigTableCell *cell = (customBigTableCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[customBigTableCell alloc] init];
}
cell.name.text = [object objectForKey:#"objName"];
newClass* newObj = [[newClass alloc] init];
newObj.objName = cell.name.text;
[newObjects addObject:newObj];
return cell;
}
Pls pardon me .. for the indenting with the code block is messed up...
Also the PFObject is cos i use Parse for database.
Add the created object to the newObjects array.
In the - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath method, this is the code
(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
[super tableView:tableView didSelectRowAtIndexPath:indexPath];
UIStoryboard* storyboard = [UIStoryboard storyboardWithName:#"MainStoryboard" bundle:[NSBundle mainBundle]];
newDetailController* newController = [storyboard instantiateViewControllerWithIdentifier:#"UpcomingDetail"];
obj = [[newClass alloc] init];
NSIndexPath* path = [self.tableView indexPathForSelectedRow];
obj = [newObjects objectAtIndex:path.row];
NSLog(#"Row selected is %ld", (long)path.row); //this returns the correct row selected
NSLog(#"Movie selected is %#", [[newObjects objectAtIndex:path.row] objName]);//however this is not the right one as per the cellForRowAtIndexPath method.
[newController setNewObj:obj];
[[self navigationController] pushViewController:newController animated:YES];
}
When I run this code, the row is selected correctly. However I dont get the corresponding row in the database. The array gets stored with redundant data.

You need to get rid of that middle line:
newController = [[newDetailController alloc] init];
That just redefines newController (as an empty controller with no view set up), which you defined in the first line.

Related

Passing data between viewcontroller through property

I have a dictionary on Viewcontroller A .I am passing it on viewcontroller B.On ViewController A it shows data on the dictionary of viewcontroller B.But ,In Viewcontroller it doesnt show any data in it?
ViewController A:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
TableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"Cell" forIndexPath:indexPath];
if (cell== nil) {
cell = [[TableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"Cell"];
}
if(_List.count>0) {
[cell setupCell:[_List objectAtIndex:indexPath.row]];
ViewControllerB *vc=[[ViewController alloc]init];
vc.userIdInfo=[_List objectAtIndex:indexPath.row];
}
return cell;
}
ViewController B:
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(#"_userIdInfo %#",userIdInfo);
if([userIdInfo valueForKey:#"UserID"]) {
[self fetchDataFromServer:getdetail];
}
}
This NSLog(#"_userIdInfo %#",userIdInfo) prints null. while vc.userIdInfo prints dictionary
{
Count = 3;
UserID = "6gdab241-j176-1121-lde2-as8d21f62231";
UserName = "abc#gmail.com";
}
In objective C, cellForRowAtIndexPath methods is used for showing the data.
As per code, you are intiallising the ViewController B in cellForRowAtIndexPath.
it will allocate ViewController B multiple time which lead to memory consumptions.
I would suggest you to initialise in ViewDidLoad of ViewController A.
For Passing the data from A to B, you need to Use didSelectRowAtIndexPath method of UITableViewDelegate.
Example:
ViewController A:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
TableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"Cell" forIndexPath:indexPath];
if (cell== nil) {
cell = [[TableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"Cell"];
}
if(_List.count>0) {
//Use to show data
cell.textLabel.text = [NSString stringWithFormate:#"%#",[_List objectAtIndex:indexPath.row]]];
// [cell setupCell:[_List objectAtIndex:indexPath.row]];
// ViewControllerB *vc=[[ViewController alloc]init];
// vc.userIdInfo=[_List objectAtIndex:indexPath.row];
}
return cell;
}
ViweController B
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
// 1st Type
ViewControllerB *vc=[[ViewController alloc]init];
vc.userIdInfo=[_List objectAtIndex:indexPath.row];
[self.navigartionController pushViewController:aVC animated:YES];
}
You need to do ViewController B initialization in DID select method like below.
Don't initialize it in cellForRowAtIndexPath.
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
// 1st Type
ViewControllerB *vc=[[ViewController alloc]init];
vc.userIdInfo=[_List objectAtIndex:indexPath.row];
[self.navigartionController pushViewController:aVC animated:YES];
//2nd Type
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"Main" bundle:nil];
ViewControllerB *aVC = [storyboard instantiateViewControllerWithIdentifier:#"ViewController"];
aVC.userInfo = <set your data>;
[self.navigartionController pushViewController:aVC animated:YES];
}
Hope this will helps.
The code
ViewControllerB *vc=[[ViewController alloc]init];
vc.userIdInfo=[_List objectAtIndex:indexPath.row];
only creates ViewController; it doesn't present it anywhere. moreover, the instance vc will be deallocated right after you leave the scope of
if(_List.count>0){}
your method
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(#"_userIdInfo %#",userIdInfo);
if([userIdInfo valueForKey:#"UserID"])
{
[self fetchDataFromServer:getdetail];
}
}
will never be called, because your view will never load.
I assume, that what you really want is to present your vc instance with navigationController instance, or from current viewController
[self.navigationController presentViewController: vc animated: YES completion: nil];

How to populate an array from detailViewController and return to masterViewController in iOS?

I have a tableView from which I navigate to a 'viewController' ie PassengerInfoViewController in which I have a form.I fill up the form , make a dictionary out of it and add it to a NSMutableArray.
So when I am going back to the tableView' I would like to pass this array and then reload thetableView` with the filled array.
So here's what I am doing : -
//navigate to the form
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
PassengerInfoViewController *pivc = [self.storyboard instantiateViewControllerWithIdentifier:#"PassengerInfoViewController"];
[self.navigationController pushViewController:pivc animated:YES];
}
//After filling up the form
-(void)goBackToPassengerDetails:(UIButton*)sender{
NSString *title = self.titleTextField.text;
NSString *fname = self.firstNameTextField.text;
NSString *lname =self.lastNameTextField.text;
NSString *ageStr = self.ageTextField.text;
NSMutableDictionary *dict = [[NSMutableDictionary alloc]init];
[dict setValue:title forKey:#"title"];
[dict setValue:fname forKey:#"firstName"];
[dict setValue:lname forKey:#"lastName"];
[dict setValue:ageStr forKey:#"age"];
Passenger *passengerObj = [Passenger sharedInstance]; //Singleton
[passengerObj.passengerDetailArray addObject:dict];
PassengerDetailsViewController *pdc = [self.storyboard instantiateViewControllerWithIdentifier:#"PassengerDetailsViewController"];
[pdc getPassengerinfo:passengerObj.passengerDetailArray];
[self.navigationController popViewControllerAnimated:YES];
Once I navigate back I reload the table view.However celForRow method doesn't populate new values.
-(void)getPassengerinfo:(NSMutableArray*)arr{
passenger_infoArray =[[NSMutableArray alloc]init];
passenger_infoArray = arr;
NSLog(#"Passenger Info Array : %#", passenger_infoArray);//Shows array of dictionaries
[passengerInfoTableView reloadData];
}
My cellForRow method looks like this:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
//Not called when I am doing reload data
static NSString *CellIdentifier = #"Cell";
cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
{
cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:CellIdentifier ];
}
NSLog(#"Table view array : %#",passenger_infoArray);
if (passenger_infoArray.count>0) {
NSString *temp= [[passenger_infoArray objectAtIndex:indexPath.row]objectForKey:#"firstName"];
cell.textLabel.text = temp;
}else{
cell.textLabel.text = [NSString stringWithFormat:#"Passenger %ld", (long)indexPath.row+1];
cell.imageView.image = [UIImage imageNamed:#"user.png"];
}
return cell;
}
You can pass data using
Delegates
Notifications
Properties
Global variables
Using Delegates is good practice, and last two are not preferred.
You can pass data to previous view controller by using delegate method
In PassengerInfoViewController.h add below code just above of your import statement
#protocol passengerInfoViewControllerdelegate <NSObject>
-(void)sendData:(NSMutableArray *)arrData;
#end
After this create one property, as shown below
#property(nonatomic,assign)id delegate;
Now, in PassengerInfoViewController.m synthesize the property and after that
-(void)viewWillDisappear:(BOOL)animated
{
[delegate sendData:yourArray];
}
Now in tableview
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
PassengerInfoViewController *pivc = [self.storyboard instantiateViewControllerWithIdentifier:#"PassengerInfoViewController"];
pivc.delegate = self; //Add this line to your code
[self.navigationController pushViewController:pivc animated:YES];
}
Now, it's time to implement delegate method which we created in viewcontroller
-(void)sendData:(NSMutableArray *)arrData
{
passenger_infoArray = arrData;
[passengerInfoTableView reloadData];
}
Old way to do this was using delegates. Better way to do this is via an unwind segue because there's much less code and you don't have to create a protocol.
Once your form completes, your PDC should call [self performSegue:#"theNameOfTheUnwindSegueeYouCreateInTheStoryboard"]. Then, in your PDC's prepareForSegue method, get the destinationViewController property from the storyboardSegue parameter - this is your VC containing the Table View. Set whatever model property you're using to back the table view - in your case it looks to be the passenger_infoArray object, which you'll have to make mutable if isn't already and also expose publicly if it isn't already.

Load Custom UiTableView as a ChildViewController in custom TabBarNavigationController

I'm trying to incorporate XLMailBox, Custom ViewController as a parent viewController that can switch between different childViewController using a TabBar.
I'm loading a Custom UITableViewController
called SwipeTableViewController as a childViewController within a parent viewController
I use this code to load the ParentViewController in AppDelegate as RootViewController:
#implementation SwipeTableAppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
SwipeTableViewController * child_1 = [[SwipeTableViewController alloc] initWithStyle:UITableViewStylePlain];
NSArray * childViewControllers = #[child_1];
XLSwipeContainerController * containerController = [[XLSwipeContainerController alloc] initWithViewControllers:childViewControllers];
self.window.rootViewController = [[UINavigationController alloc] initWithRootViewController:containerController];
return YES;
}
The SwipeTableViewController loads the table view as follows:
- (void)viewDidLoad
{
[super viewDidLoad];
patternImages = [NSMutableArray arrayWithObjects:#"neon-autumn.gif", #"alchemy.jpg", #"white-wood.jpg", #"green-goblin.png", #"subway-lines.png", #"canvas-orange.jpg", #"kiwis.png", #"cuadros.png", #"hodgepodge.png", #"naranjas.png", #"bunting-flag.png", nil];
self.title = #"Rounds";
_patients = [RND_PatientDB loadScaryBugDocs];
[self.tableView registerClass:[CustomTableViewCell class] forCellReuseIdentifier:#"Cell"];
NSLog(#"FirstViewController - viewDidLoad");
self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc]
initWithBarButtonSystemItem:UIBarButtonSystemItemAdd
target:self action:#selector(addTapped:)];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellIdentifier = #"Cell";
CustomTableViewCell *cell = (CustomTableViewCell *)[tableView dequeueReusableCellWithIdentifier:cellIdentifier forIndexPath:indexPath];
RND_Patient *patients = [self.patients objectAtIndex:indexPath.row];
cell.patternImageView.image = [UIImage imageNamed:[patternImages objectAtIndex:indexPath.row]];
cell.patternLabel.text = patients.data.ptName;
cell.ptDesc.text = patients.data.ptDesc;
cell.ptNumber.text = patients.data.ptNumber;
if (patients.data.completed == 1){
cell.backgroundColor = [UIColor greenColor];
}
return cell;
}
It then configures the cell using the CustomTableViewCell class and populates with data from RND_Patients mutablearray which it loads initially.
Now my problem is that when I load the SwipeTableViewController as a childViewController, the cells are empty and are not populated with data.
When I load directly the SwipeTableViewController as part of the RootViewController without using the XLMailBox custom navigation controller, the table view loads just fine and has the appropriate cells and the appropriate data.
I do not understand the discrepancy between these two views. All my code seems correct, but in my first example, the CustomTableViewCell does not load and in the second it loads. Any ideas on how can I fix this?

Detail View Controller from Table View Controller WITHOUT Storyboard?

I have a tableview in a controller that shows receipts and their image, date created, etc.
I made it so when the user taps a cell, it goes to DetailViewController. Using this code:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
ReceiptDetailViewController *controller = [[ReceiptDetailViewController alloc] initWithNibName:#"ReceiptDetailViewController" bundle:nil];
[self.navigationController pushViewController:controller animated:YES];
}
I'd like to pass the image, date, category, etc, to my new view in a UIImageView, textView, etc.
This is the code for my Table in the first controller:
-(UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"Cell"];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:#"Cell"];
}
cell.selectionStyle = UITableViewCellSelectionStyleGray;
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
Receipt *receipt = [self.receiptsArray objectAtIndex:indexPath.row];
ReceiptCategory *category = receipt.relationshipToCategory;
ReceiptImage *image = receipt.relationshipToImage;
cell.textLabel.text = category.receiptCategory;
cell.detailTextLabel.text = receipt.date.description;
if(self.imageCache.count <= indexPath.row) {
[self.imageCache addObject:[UIImage imageWithData:image.data]];
}
UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 40, 40)];
imageView.image = [self.imageCache objectAtIndex:indexPath.row];
cell.imageView.image = imageView.image;
return cell;
}
How do I pass this data? I have searched around for two days now and all solutions include a storyboard, which I am not using.
You should get reference to your receipt in didSelectRowAtIndexPath: and pass it to ReceiptDetailViewController. I assume you have property to accept the data in ReceiptDetailViewController:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
//Get reference to receipt
Receipt *receipt = [self.receiptsArray objectAtIndex:indexPath.row];
ReceiptDetailViewController *controller = [[ReceiptDetailViewController alloc] initWithNibName:#"ReceiptDetailViewController" bundle:nil];
// Pass data to controller
controller.receipt = receipt;
[self.navigationController pushViewController:controller animated:YES];
}
It this example you should create property in ReceiptDetailViewController.h file:
#property(nonatomic, strong) Receipt *receipt;
Now you can use it in your ReceiptDetailViewController object.
You should just use Table View Delegate Method
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
There you can retrieve the selected receipt and pass the data to the detail view like that:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
ReceiptDetailViewController *controller = [[ReceiptDetailViewController alloc] initWithNibName:#"ReceiptDetailViewController" bundle:nil];
Receipt *selectedReceipt = [self.receiptsArray objectAtIndex:indexPath.row];
controller.receipt = selectedReceipt;
[self.navigationController pushViewController:controller animated:YES];
}
You can create a custom class for your detailed view controller and create an object of the same when the user clicks on any cell.
Something like this
DetailViewControllerClass *newObject = [[DetailViewControllerClass alloc] init];
newObject.data1 = dataToBeSent ; //assign data
You should make an object of Receipt in ReceiptDetailViewController and make the property nonatomic and strong Than in tour view controller Write this
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
Receipt *receipt = [self.receiptsArray objectAtIndex:indexPath.row];
ReceiptDetailViewController *controller = [[ReceiptDetailViewController alloc] initWithNibName:#"ReceiptDetailViewController" bundle:nil];
controller.objReceipt= receipt;
[self.navigationController pushViewController:controller animated:YES];
}

Two UITableView Controllers in SplitViewController in iPad, Delegate

Hi This is my first iPad app and trying to port my iphone app to iPad.
I have followed all the tutorials from http://www.raywenderlich.com/ still having a problem.
Also review this question and still having the problem . Splitviewcontroller with two tableviews, delegate problem
Basically, I have two UITableViewControllers in SplitViewController and when I click the tableview cell in root view controller, I want to populate the details in DetailsViewController in Right side on another Tableview.
The problem is I can manage to pass the array data from but I can't call tableview reload method.
Here is the code
LeftViewController
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSUInteger row = [indexPath row];
if (row == 0){
NSLog(#"Row 0 Pressed");
RightViewController *rightvc = [self.storyboard instantiateViewControllerWithIdentifier:#"displayenglish"];
_locallayleft = [ConversationDatabase database].conversationsInfos;
NSLog(#"Just pushed the array");
rightvc.detailItem = _locallayleft;
rightvc.title = #"Greetings";
}
else if (row == 1) {
NSLog(#"Row 1 Pressed");
RightViewController *rightvc = [self.storyboard instantiateViewControllerWithIdentifier:#"displayenglish"];
_locallayleft = [ConversationDatabase database].conversationsInfosgeneral;
rightvc.detailItem = _locallayleft;
rightvc.title = #"General Conversation";
}
-----------------------------------------------------------------------------------------
RightViewController
- (void)setDetailItem:(NSArray *)newDetailItem
{
if(_detailItem != newDetailItem) {
_detailItem = newDetailItem;
[self configureView];
}
}
- (void)configureView
{
if (self.detailItem) {
self.locallay = self.detailItem;
_listOfCoversation = [[NSMutableArray alloc] init];
for (ConversationInEnglish *c in _locallay)
{
NSString *english = c.english;
NSLog(#"setDetails Item get called");
NSLog(#"%#",english);
[_listOfCoversation addObject:english];
}
[self.tableView reloadData];
NSLog(#"Trying to reload TableView");
}
}
- (void)viewDidLoad
{
[super viewDidLoad];
[self configureView];
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [_locallay count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"English";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
ConversationInEnglish *con = _locallay [indexPath.row];
_englishLabel = (UILabel *) [cell viewWithTag:200];
_englishLabel.text = con.english;
NSLog(#"My data from cell %#",con.english );
[_englishLabel setFont:[UIFont fontWithName:#"Open Sans" size:22]];
_myanmarLabel = (UILabel *) [cell viewWithTag:300];
[_myanmarLabel setFont:[UIFont fontWithName:#"TharLon" size:17]];
_tonebasedLabel = (UILabel *) [cell viewWithTag:400];
_tonebasedLabel.text = con.tone_based;
UIColor *background = [[UIColor alloc] initWithPatternImage:[UIImage imageNamed:#"tableviewcell.png"]];
self.tableView.backgroundColor = background;
return cell;
}
It looks like when you tap a row in the table on the left, instead of updating the table on the right, you're instantiating a whole new table from the storyboard instead, but not replacing the one on the right with it.
There isn't enough context here to say exactly how to fix it, but what you'd want to do is when you tap a row in the table on the left, update the table on the right by setting its detailItem property.
You'll need access to the other table view. There are a few ways to do this depending on how you've got your application set up - if you're using the same left table view on both the iPhone and iPad then you'll probably need some conditional code to locate it, for example:
if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad) {
DetailViewController *detailViewController = (DetailViewController *)[[self.splitViewController.viewControllers lastObject] topViewController];
detailViewController.detailItem = newDetailItem;
}
Or you could configure it through the storyboard. Either way, the key is to find and update the existing table view instead of instantiating a new one from the storyboard.

Resources