iOS - setting outlets on a ViewController being used as a UITableView header - iphone

I'm using custom headers for my tableview...
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
CustomHeaderController *header = [[CustomHeaderController alloc]
initWithNibName:#"TableHeader" bundle:nil];
header.title.text = #"Test";
return header.view;
}
The title label is never set though. I even tried creating a viewWillAppear method and setting it there, but that didn't work. My outlets are set up too!
Thanks!
SOLUTION: View wasn't load on the return header.view call. Call header.view or add a viewDidLoad method to the class of header to get it to work! Thanks all!

So I'm not sure "title" here means what you think it means. Setting the title on a view controller isn't going to affect what's in the view. If you're trying to set a title on a section, you may just want to use tableView:titleForHeaderInSection. tableView:viewForHeaderInSection is for more complex section headers (like if you wanted to put buttons, or multiple rows of text or something like that). If it matters, you can use both of these methods. I'm not 100% certain on the order they're called, but I'm pretty sure it looks for a viewForHeaderInSection and then if that's nil it goes to titleForHeaderInSection.

The function expects a UIView to be returned, but you appear to be returning a UIViewController. Did you mean to do return header.view?

Related

iOS - UITableView delegate not being set?

The UITableView object I have in my storyboard theoretically should have its delegate set, but it does not. I dragged the UITableView object from storyboard into the header and added it as an IBOutlet property and synthesized it. However, I checked and only the data source method is being called. So something seems to be wrong with the way I'm implementing the main delegate protocol. As you can see in the images below, I seem to be doing everything standardly? But the delegate is not being set still! Thoughts?
I think the reason your solution is not working is because you are using a TableView inside of a UiViewController instead of a UiTableViewController. I had this same issue a while back. Here is what I did. Create an IBOutlet to the header file and synthesize it in the implementation file (I believe you have already completed this step). Go back to the storyboard. control + click on your table view and drag the connector to the view controller (the yellow circle with the white box). Select datasource. Repeat this step again and instead of selecting datasource select delegate. In the menu on the right-hand side you should be able to see your outlets if it is set up correctly. This should fix your delegate problems.
See the screenshot from my example below:
Try changing your numberOfRows method (for better debugging).
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
int rows = [allParties count];
NSLog(#"number of rows: %d", rows);
return rows;
}
If you're returning 0, it won't work.
Also in the future, please paste your code instead of taking screen shots.
You have put a log in both numberOfRowsInSection: which is being called but not the one in cellForRowAtIndexPath: which shows that no cells are being created for your table. You simply are returning 0 as number of rows for the table. Check for that.
And the delegate method didSelectRowAtIndexPath: will be called when you select a row, but for that, you have to have a row first in your table.

Best approach to add Static-TableView-Cells to a UIViewcontroller?

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.

Sharing data between iPhone tableviews?

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.

How to call a detail view from a table view within a Storyboard

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.

Custom UITableViewCell woes

I have an iPhone app with a form input screen. I did this by making a custom UITableViewCell that has a UILabel and a UITextfield. I set it up so that the "cellForRowAtIndexPath" fetches the appropriate value from Core Data, and the UITextField's "textFieldDidEndEditing" method saves the appropriate value to Core Data. It works great... except:
If I edit a text field, then scroll it off screen, then click on another cell's text field:
The cell has been autoreleased because it scrolled off screen
The "textFieldDidEndEditing" gets a BAD ACCESS error
I understand the problem completely, I'm just not sure the best way to fix it. My first thought was to add the logic from "textFieldDidEndEditing" to "dealloc", but that seems hacky. Any suggestions?
I ended up using a delegate method for scroll view (which is built in to the UITableView). When the user starts dragging, I resign first responder.
This works perfectly because it looks nice, and "textFieldDidEndEditing" gets called when the user starts to scroll, which is always before the text field goes off screen.
disable scrolling while editing
retain your textField
that are the things you could do. In my opinion its best to disable scrolling while editing because the user has no need to, so make sure he also can not do so. Limit the things your user can do, makes it more "secure" for you and easier to use for the user.
If you are not sure about such things just look at what apple does, they are always right in their applications. Like in the settings app on iOS 5, when you change your phone's name. You simply get 1 row in the next tableview so you can't really mess anything up as the user..
In your custom cell's dealloc, set the textfield's delegate to nil
I had a similar issue... The problem lies in the reusable cells as part of the Table View. Every time the table is scrolled, the cellforRowatIndexPath gets called, and dequeues a cell and returns it. Sadly, this functionality doesnt work well with cells having retainable data such as labels. Hence you must opt for your own method of dequeueing the cell.
I have an NSMutableArray called cells which holds all my cells.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
CustomTableViewCell *cell = [self dequeuwReusableCellwith:indexPath];
if (cell == nil)
{
cell = [[CustomTableViewCell alloc] init];
[cells addObject:cell];
}
// Configure the cell.
NSString *temp = [NSString stringWithFormat:#"Cell %d",indexPath.row];
cell.textField.placeholder=temp;
return cell;
}
and this is my custom method to deque Reusable cells.
-(CustomTableViewCell*)dequeuwReusableCellwith:(NSIndexPath*)indexpath
{
if([cells count]>indexpath.row)
{
return [cells objectAtIndex:indexpath.row];
}
return nil;
}
Hope this helps...