I want to be able to share data between table views for an app that I'm making. The reason for this is that I want to be able to tell a subview which table row was selected so that I don't have to make a bunch of views and I can just test to see what the integer variable was. I watched a video tutorial on how to do this but they did not use tableviews. So when I tried this it did not seem to work. I used the app delegate as a "data center" that held that variables and then I tried to assign values to the variables in didSelectRowAtIndexPath method. (Pushing the new view works fine by the way it's just the shared application)
Here's the code for the first tableview where I assign the variable to a number.
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
NSUInteger row = [indexPath row];
ApplicationAppDelegate *appDelegate = (ApplicationAppDelegate *)[[UIApplication sharedApplication] delegate];
appDelegate.rowPicked = row;
SecondLevelViewController *nextController = [self.controllers objectAtIndex:row];
[self.navigationController pushViewController:nextController animated:YES];
}
In the app delegate I did this and I synthesized it in the .m file:
#property (nonatomic) NSInteger rowPicked;
As well as other NSIntegers that I needed.
Any suggestions? If you think I'm doing this totally wrong could you please enlighten me with specific instructions or a link to a website or video tutorial?
Thank you all!
Here's how I usually accomplish this:
I don't use the AppDelegate for this. The logic and model data for both list and detail should reside in classes that make sense. In this case, we'll use the list and detail view controller classes themselves.
I'll begin by creating a UITableViewCell sublcass for the list view's row. In that sublcass, I'll create an ivar that houses the "entity" or whatever data the cell will need to display it's information. This can be an NSManagedObject or even an NSDictionary.
I'll override the setter of that ivar so that when data is set on the UITableViewCell, it updates the cell outlets to display it correctly. Notice how I keep the logic of how the cell is displayed contained completely within the subclass. It's important that you do things like this throughout your application to promote code cleanliness and organization.
In your tableView:didSelectRowAtIndexPath method, you'd then call the UITableView class' cellForRowAtIndexPath: method to return the cell that was selected. You can then cast it to your UITableViewCell subclass and get the entity information you set earlier.
Next, you'll need to create an ivar in your detail view controller. You'll want to set this variable from the tableView:didSelectRowAtIndexPath: method---right before you push the detail view onto the stack.
You should now have the necessary data in your detail view controller sublcass for processing, querying, or whatever.
Hope this helped!
It's really looks bad. Much better add id<$YOUR_PROTOCOL> delegate to SecondLevelViewcontroller and set nextController.delegate = self.
Protocol can looks like
#protocol RowAccessProtocol
#optional
-(NSUInteger)selectedRow;
#end
Your current tableViewController must be created:
#protocol RowAccessProtocol;
#class FirstLevelTableViewController:UITableViewController<RowAccessProtocol>
…
#end
And implementation:
…
-(NSUInteger)selectedRow{
return [self.tableView indexPathForSelectedRow].row;
}
In SecondLevelViewController you can call [self.delegate selectedRow] to get selected row.
Related
I want to add a tableview-look-a-like-login to my app, but it seems to be not that easy to implement. I tried to accomplish my goal using more then one approach, but i am not sure about which solution is the best.
For example, Dropbox and Facebook have a login page like this.
Here are my 3 approaches :
I added 2 UITextfields to my View (no border) and placed a . png behind, which looks like a tableviewcell. ( Not the best approach cause i want to use real tableviews )
I added a Container View to my ViewController placed a tableview with static Table Views inside. The Problem here is, that i dont know how to access the information inside my viewcontroller?
I added a tableview to my ViewController and used dynamic cells with it. Connected the outlets for delegate and datasource to my viewcontroller and initialized them with the delegate and datasource methods. The Problem here is, that i can not use static table views inside a uiviewcontroller.
Is there any better way of solving this problem ?
I would really like to know how to do this in a more elegant way.
EDIT:
A ContainerViewController basically solved this issue for me some month ago.
After embedding one into the main controller you can access it through the prepareForSegue function and define a protocol-based interface for that specific controller to interact with the embedded controller.
If you want to use static cells inside a regular UIViewController, just add the static cells and design them the way you like in interface builder, then connect the table cells as strong IB outlets (weak won't work, make sure they are strongly referenced). This will work flawlessly if you have a few table cells. Then set the view controller as the data source of the tablet view, implement -(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section to return the number of cells and implement -(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath to return your strongly referenced cell instance for the specified index path. I've used this method for a simple table view in my view controller that had four cells and it is working perfectly. For a large-dynamic data set, I definitely do not recommend this approach but for small, static tables, this does the job right.
I have an idea how to solve this. I think it's a clean way to do so. You do not need storyboard for this controller.
Make your controller subclass UITableViewController like so:
#interface YourViewController : UITableViewController
Then in your viewDidLoad you create the instances of the cells:
- (void) viewDidLoad {
usernameCell = [YourTextFieldCell new];
passwordCell = [YourTextFieldCell new];
}
The YourTextFieldCell is of course your own subclass of a UITableViewCell, which could be something like this:
#implementation YourTextFieldCell {
UITextField textField;
}
- (id) init {
self = [super init];
if (self) {
// Adjust the text's frame field to your liking
textField = [[UITextField alloc] initWithFrame:CGRectMake(0, 0, 200, 20)];
[self addSubview:textField];
}
}
// A getter method to access the textfield from the outside
- (UITextField *) textField {
return textField;
}
#end
Back in YourViewController:
- (NSInteger) tableView:(UITableView *) tv numberOfRowsInSection:(NSInteger) section {
return 2;
}
- (UITableViewCell *) tableView:(UITableView *) tv cellForRowAtIndexPath:(NSIndexPath *) indexPath {
if (indexPath.row == 0) {
return usernameCell;
} else if (indexPath.row == 1) {
return passwordCell;
}
return nil;
}
Do you get where I am going with this? This is how I think you should do it! Good luck!
I think your approach 2 is the best. If you need to access information in the table view controller, from your UIViewController (which will be the parent view controller), you can get a reference to that table view controller with self.childViewControllers.lastObject. In the viewDidLoad method of the UIViewController subclass, you could set yourself as the delegate of the table view with this line if you want:
[[(UITableViewController *)self.childViewControllers.lastObject tableView] setDelegate:self];
That way, you could implement the tableView:didSelectRowAtIndexPath: method in the view controller, which will get the information I'm guessing you need.
If you go with your option 2) using a storyboard and have a ContainerView containing your own subclass of UITableViewController with static cells then you can implement the prepareForSegue: method in your parent ViewController to take a reference to the UITableViewController (it'll be the destinationController of the segue) and also to pass itself down to the UITableViewController subclass if necessary (which should hold onto it with a weak reference).
Disclaimer - This answer will work for any size of UITableView, but if you're just making a login view, Tom's answer will work quite well.
I'm not sure if this will help, but what I did for this was create my own UITableView-esque subclass with a UITableViewCell-esque subclass as well.
This may not be what you want to hear, but I find what I made to be really helpful, since I've used it a number of times now. Basically, you have a UIView with the stylistic approach for the different types (10.0f - 20.0f cornerRadius and a 1px border (divide by UIScreen's scale property for retina). As for the cell, you'll want to have a full sized UIButton on it that responds to your table view for the touch events either with a delegate or by setting the target and tag inside your table view's class.
Last, you'll have a delegate system just like the UITableView for your information for building the specific tables.
In short, you'll need:
2 UIView subclasses (TableView and TableViewCell)
2 Delegates/Protocols (TableViewDataSource and TableViewDelegate)
Optionally
1 Delegate (TableViewCellResponseDelegate)
1 NSObject Subclass (Contains all of the information needed in each cell - Ease of use)
I found Can's solution to be the best / easiest, but unfortunately it breaks in XCode 5.1 --
I found a workaround which builds off the same basic idea, but unfortunately requires a little more involvement: http://www.codebestowed.com/ios-static-tableview-in-uiviewcontroller/
To summarize, you can add TableViewCells directly to views (and create IBOutlets from them, etc), but in order for them to get "moved" to the TableView properly, you need to remove them from the view in code, and you also need to set Auto-Layout constraints in IB.
i want to use uitableview just like a uipickerview. on click a button the uitableview comes.and on clicking any cell the selected value will be shown on the button on previous page
The normal way to pass data back to another controller is to use a delegate protocol. The controller with the button would implement the protocol method(s) declared in an #protocol declaration put in the .h file of the table view controller. In the tableView:didSelectRowAtIndexPath: method, you would call the delegate method, passing the value of the clicked on cell.
rdelmar is correct, the simplest way to do this would probably be to use a delegate protocol and pass the selected value back to the previous view.
--
However, supposing you wanted to store this value outside of the table view and the picker, you could create a singleton or use NSUserDefaults to avoid having to pass the value around and potentially losing something. If this tableview/picker was in a settings menu or the like, that's what I would do.
See this for basics:
http://mobile.tutsplus.com/tutorials/iphone/nsuserdefaults_iphone-sdk/
Some code for delegation; A protocol in your tableview class:#protocol SelectionDelegate <NSObject>
-(void)setSelectedTableCellValue:(NSString*)value;#end Have your previous view confirm to and be the delegate for the tableview that you wish to display and also implement the protocol method. Now on cell selection, just retrieve the value for the cell and pass it over to your delegate via [delegate setSelectedTableCellValue:<some cell value>]; HTH.
You may want to take a look into core data, you will be able to save the data and re-access the data/value and reload in into any view controller you like. It is a very versatile tool, and once you learn it, you will be able to do so many things a lot easier. Take a look at this Tutorial
Just use this
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
[tableView deselectRowAtIndexPath:indexPath animated:YES];
UITableViewCell *cell=[self.m_TableView cellForRowAtIndexPath:indexPath];
AppDelegate* appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
[appDelegate.m_SavedDictionary setObject:cell.textLabel.text forKey:#"driver"];
[self.navigationController popViewControllerAnimated:YES];
}
and on ViewwillAppear method of previous view use;
if([[appDelegate.m_SavedDictionary objectForKey:#"driver"]length]>=1)
{
[m_driverButton setTitle:[appDelegate.m_SavedDictionary objectForKey:#"driver"] forState:UIControlStateNormal];
}
Thats it.
I have following problem.
I have created a tab based Application with three Views and Viewcontroller.
FirstView(Start screen stuff), SecondView (Detailpage), ThirdView (Table for listing items).
(Connections from storyboard were set automatically).
In the third view a table is integrated and the content is displayed fine.
Now I would like to call the SecondView , when a row in the table is selected.
I also tried to add a forth View , outside the tabBar Controller, to get the Detailview, but this also did not help.
I have tried several tutorials and forum tips, but cannot get it working.
The class is set to the right ViewController, the identifier is set to detail.
-(void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
DetailViewController *detail = [self.storyboard instantiateViewControllerWithIdentifier:#"detail"];
[self.navigationController pushViewController:detail animated:YES];
detail.label1.text = #"It is working";
}
When clicking on the row, it becomes blue, but nothing happens. No error Message , nothing.
Hope that you can help :-)
OK, I have tried to "optimize" my design.
Still have the tab based Views, but when clicking on a row in the table, a new (not linked in Storyboard) view should appear to display the details of the selected quote.
I have created a view in the storyboard and called it "detailzitat"
I have created a DetailViewController.h/m as UIViewcontroller class
I have set the custom class to DetailViewController
I import the DetailViewController.h in the ThirdViewController.h
I have modified the didSelectRowAtIndexPath method in ThirdViewController.h accordingly.
-(void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
DetailViewController *detailVC = [self.storyboard instantiateViewControllerWithIdentifier:#"detailzitat"];
[self.navigationController pushViewController:detailVC animated:YES];
}
and my DetailViewController.m looks like
#import "DetailViewController.h"
#implementation DetailViewController
#synthesize label1, labeltext;
- (void)viewDidLoad
{
[super viewDidLoad];
labeltext=#"JUHU";
label1.text=labeltext;
}
But again, nothing happens, besides the row gets blue.
I do not understand. If I am using this code in a Non-Storyboard project, it is working.
What am I doing wrong ? Is there any tutorial for this combination within Storyboards ? Have not found one for this approach yet.
Try to learn from the different tutorials on the web, but the biggest problem is, most ones are not for iOS5 and I am not so good to transfer then.
Hope to get some hints :-)
You might want to think about your design. If I am understanding your description correctly, the user will be on the third tab, tap on a row in a table, and then you will be switching them back to the second tab. A navigation controller might be a more natural, less confusing, choice there.
But in any case, something like this will work sometimes:
-(void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
DetailViewController *detailVC = [self.tabBarController.viewControllers objectAtIndex:1];
detailVC.label1.text = #"It is working"; // <- this will not work consistently!
self.tabBarController.selectedViewController = detailVC;
}
The problem with this is that while the user is on that third tab, it's possible that the second tab view controller's view is unloaded (due to memory pressure for example).
It's also possible the user went from tab 1 to tab 3 immediately and therefore the 2nd tab's view isn't even loaded yet at all. (To even test the above code you would have to select tab 2 and then tab 3.)
If the second tab's view hierarchy is not loaded, the label1 property will be nil, and so this will not work. A better strategy would be to create a new #property of type NSString* on the DetailViewController. And set that property instead of trying to set the label1 directly.
Then in your viewWillAppear: for the DetailViewController you can update your labels as needed. At that point of course you can be sure that label1 is loaded and connected to the correct UILabel.
I hope that helps.
I think problem is at self.navigationController. If your view is not inside the navigation controller this will not work. So what you do is create a new navigation controller object there and then use it to show your detail view.
In my UINavtigationController application, I have a user select a value in UITableViewController and once that is done, they are sent back to a previous UITableViewController where that value must be shown. The problem I am anticipating is that once a view is "popped", I am assuming that its viewUnload is called that gets rid of the array making it unaccessible in another view controller.
Any thoughts?
You need to pass the new value to previous controller from your didSelectRowIndex function by using anyone of the approaches
1-> Using NSUserDefault.
iPhone Programming Tutorial – Saving/Retrieving Data Using NSUserDefaults
2-> Using Delegate concept.
3-> you could also access the previous ViewController from you UINavigationController methods.
You can access the first view controller via self.parentViewController, so it would be best to synthesize an array in the first view, then set it before you call popViewControllerAnimated: in the second. Basically it would look like this:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
[self.parentViewController setNewArray:myArray];
[self.navigationController popViewControllerAnimated:YES];
}
Just make sure you have NSArray *newArray; and #property (nonatomic, retain) NSArray *newArray specified in the first view's header file and synthesized in the implementation file
you can use NSUserDefaults to achieve the same
I have a simple UITableViewController in a UINavigationController that displays a list of strings from an array with the default Edit/Done button on the right-hand side of the navigation bar.
When pressing the Edit button, the UITableView animates correctly and shows the red minus icons to delete. Pressing the delete button removes the row from the table view and the array (implemented in the tableView:commitEditingStyle:forRowAtIndexPath: method of the UITableViewController).
I would now like to allow the user to add a row to the view (and add the string to the underlying array), but I'm not sure how to go about doing so. The commitEditingStyle method has else if (editingStyle == UITableViewCellEditingStyleInsert), but I don't know how I can get the user to input the string.
I've read the Table View Programming Guide (more specifically the example of adding a table-view row), but this seems to require a whole new UIViewController subclass just to get a string from the user.
Is there no easier way?
Creating another view controller is probably going to be the easiest way in the long run. You can present it modally by calling
SomeViewController* theViewController = [[SomeViewController alloc] init];
[self presentModalViewController: theViewController animated: YES];
[theViewController release];
When the theViewController is ready to go away it can call
[[self parentViewController] dismissModalViewControllerAnimated: YES];
OR
you can setup a protocol for your new view controller so it can notify your original view controller of completion and send a value back, if you wanted an NSString back you might use
#protocol MyViewControllerDelegate
- (void)myViewControllerDelegate: (MyViewController*)myViewController didFinishWithValue: (NSString*)theString;
#end
MyViewController would then have a delegate property
#interface MyViewController
{
id<MyViewControllerDelegate> delegate;
}
#property(nonatomic,assign) id<MyViewControllerDelegate> delegate;
#end
If you use the protocol method your original view controller will adopt that protocol and will dismiss the modal view itself when it receives this message.
I hope that helps out, it may seem a little complicated at first, but it makes gathering data very easy.
You could use a UIAlertView or similar class yourself. Just pop up the modal view to request the string, establish the right callbacks, then pop it in your dataSource.
You can also insert a cell with a UITextView and a "Tap to Edit" placeholder, then on the textView Callbacks, remove the textView and display the string. Further editing would need to drill down or do something else