I've put a lot of effort into creating a solid UITableViewController with custom cells. Now I want to create a separate UITabbarItem that uses that UITableViewController within a UISearchDisplayController.
Adhering to OO design principles, I imagine that when defining the UISearchDisplayController I'd subclass the original UITableViewController.
e.g.
#interface SearchViewController : CustomTableViewController
{
NSArray *listContent; // The master content.
NSMutableArray *filteredListContent; // The content filtered as a result of a search.
// The saved state of the search UI if a memory warning removed the view.
NSString *savedSearchTerm;
NSInteger savedScopeButtonIndex;
BOOL searchWasActive;
}
However this approach doesn't work at all - the cells are not updated at all in SearchViewController, and the UITableView delegate methods do not seem to have an effect (e.g. rows are not resized).
So I have several questions:
Is this the correct way to go about this, if so, how do I update the listContent and the filteredListContent from the superview.
Would it be better to just add a UISearchBar to the original search view and hide it as necessary?
I don't think you can subclass UISearchDisplayController and have it work correctly. It does a lot of stuff in methods which aren't public, so you wouldn't be able to override them with the correct behavior.
You can, however, use the built in UISearchDisplayController with your custom table cells in the search results, as is. You need to encapsulate the creation and configuring of your custom cells such that it works in any table view just by overriding -...cellForRowAtIndexPath (this is the standard method of displaying custom data in a tableview). Make sure that controller is the UISearchDisplayDelegate and it'll use that method to create the rows in your search list.
To set the custom height, implement
- (void)searchDisplayController:(UISearchDisplayController *)controller willShowSearchResultsTableView:(UITableView *)searchTableView
to set the rowHeight on the searchTableView.
Related
I noticed in using UITableView, UICollectionView, UIPickerView, UIScrollView, ..., and numerous other UIKit classes that the UIViewController containing the object instance often bears the role of DataSource and Delegate.
I understand from Apple's documentation what these data source and delegate methods are called, and how to implement them... for a single instance of each class.
My question is, how do you handle different instances in the same UIViewController? For example, if I have two UICollectionViews, or three UIPickerViews, ...., or fifty UIScrollViews? I can implement the data source method only once per UIViewController, but I somehow have to tell the program different instructions?
The only thing I can conceive is a gigantic switch statement or a bunch of cascading if-else if-else comparing the input to the delegate or data source method to each object instance in the UIViewController, which might get out of hand if there are many.
While we're used to using our view controller as the delegate, there's no need to do so. You can actually create NSObject subclasses that conform to the delegate protocol in question. You can then instantiate those objects and use them as the delegates of the UI objects (or whatever) as needed.
For example, consider a scene where I have two text fields, one which permits only numeric values, and one that does not accept numeric values. I can write a separate delegate object for each type of text field.
If implementing this programmatically, I would manually instantiate the two delegate objects, keep some strong references to them in my view controller, and then in viewDidLoad I can set each text field's delegate to be the appropriate delegate object.
If doing this in Interface Builder, you can actually drag an "Object" from the "Object Library" onto the scene (either in the bar above the scene or the document outline to the left of the scene):
You can then specify the class for this object:
Repeat this for all of your delegate objects:
And finally, you can go to your text field and specify the delegate by control dragging to the delegate object you added to the scene:
Doing it in Interface Builder means that it completely takes care of the instantiation of this delegate object for me and I have to do nothing in view controller's code.
Bottom line, if you want distinct behavior for a UI object, you can just instantiate a separate delegate object that manifests the desired behavior, and use that as the UI object's delegate. This pattern of instantiating separate delegate objects is common in iOS 7 custom transitions (where we have all sorts of delegate objects banging about), but can be used in this context, too.
BTW, you can obviously just subclass the UI control in question, too, and further encapsulate the logic there. That works equally well.
By creating referencing outlet for each controller,for example if you have two UITableView ,You can create outlet for each such as table1 and table2. To set number of rows in a section for these table ,you can code like follow
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
if (tableView == table1) {
return 10;
}
if (tableView == table2) {
return 5;
}
return 1;
}
You can create independent UIViews for each tableView or collection view with it's own .swift and .xib (and maybe if they are very similar you can reuse them). Doing that you will have the tableView and collecionView delegate methods in separated files and everything will be more clear. In your view controller you will just have to place the views, but you won't have any delegate methods there.
Well you are asking to differentiate the views with datasource & delegate in a smarter way. But you are overthinking this thing.
Everyone takes the different tableviews or pickers because they wan't be the same.
Otherwise they can be reused.
Now if they are going to be different from others then ultimately somewhere in your code you have to put the if...else or case statements. For example for tableview while setting the value of an UILabel or any value in your UITableViewCell.
If you are facing a such an issue that you have to put that many scrollviews or something in only one UIViewController then its an impossible situation if you are following the coding standards or may be your app design is faulty.
In my app I have used a UITableViewControllersub class to display a list of data. I finished every thing in the project, but the client needs the top row of the table to stay on top and not scroll with the rest of the content. I know this could easily be achieved using a UIViewController instead of a UITableViewController but would like to avoid this. I'm here for a final attempt to see if there is any way to fix some rows in a table view.
Please note that there are 4 different UITableViewController's in my project and they all have some complex logic in their table view delegate methods. This means a lot of work if I need to change all the UITableViewControllers into UIViewControllers.
Anyone have any ideas?
I have two suggestions for you:
More common practice is to use property of the UITableView
#property(nonatomic, retain) UIView *tableHeaderView
Second way to place your table view as child view and add another subview
UIView
HEADER UIView
UITableView
All you need to do is to implement method (declared in UITableViewDelegate):
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
return self.yourHeaderTopView;
}
You need to create the custom tableheaderview. For more refer this
Add 2 cells
use first as static and fill up the information as u want.
set the Reuse modifier of the second and call that second cell in the
cellAtRowForIndexPath method to use that second cell continuously
I am trying to set up a UITableView inside of a UIViewController. I'm doing this because it allows me to add a top bar with save and cancel buttons. I'm using storyboard and static cells to model the tableview to get input from the user (think of the create new event in Apple's calendar app). I have the view in Xcode, but when running it on my phone or the simulator, the tableview does not display. Here is the simple view in Xcode:
And this is how it displays when running it:
I've read about adding delegates and setting the datasource and such, but really this is all just going to be static cells with text fields, no data being loaded. Why is this happening and what can be done to fix it? Thanks!
#Made2k It looks like you found a solution, but for others who come across this with the same issue, note that you can only use a UITableView with static cells inside of a UITableViewController. This is not a well-documented fact, but apparently only UITableViewController knows how to handle the layout of static cells, whereas a regular UIViewController just needs to implement the UITableViewDelegate and UITableViewDataSource protocols in order to handle display of content in a UITableView added either programmatically or via Storyboard/Nib.
Hopefully this will save someone some time. #Made2k's solution is fine, but does make your storyboard unnecessarily busy. If you don't need to use a UIViewController, then just do your work inside a regular UITableViewController and save yourself a headache.
If you want to use a UITableView in a UIViewController you have to make the ViewController a data source and a delegate of the TableView and then implement methods
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
In case of a static table, in the cellForRowAtIndexPath you'd return outlets of the static cells. For a detailed description check out this answer:
https://stackoverflow.com/a/19110821/3110536
I ended up thinking about this in a different kind of way. The problem I thought I was having was I wanted all the features that a navigation controller provides, but I needed this to be the base of the controller, i.e. nothing to go back to. I was thinking that the only way to do this was to create a UIViewController and add the table view and such in there, but what I came up with is to simply just create a new navigation controller and now this view shows up as the root view like so:
I don't know if this is the best practice, but hopefully it can help somebody else if they are having this problem.
Man, following Hack really works!
You should give it a try!
In my requirement I wanted to add buttons in my Static cells too!and Toggle the visibility of the TableView
[self.tableView setHidden:YES/NO];
and Reload it with new data
[self.tableView reloadData];
and so many things is possible with that way of doing it!
https://stackoverflow.com/a/19110821/1752988
Hope the above link would help you! (Y)
I'm playing with storyboards and for one controller I set up a UITableView that consists of two sections. In both sections I have added a couple of static cells.
However, depending on code paths, I would like to show different headers for my two sections.
As there is no source and no delegate involved, how am I supposed to override tableView:titleForHeaderInSection: ?
With static table views you can (and must) still connect the datasource to a UITableViewController. The key is that if you implement the datasource methods then this will override the static content you have set up in the table view. You can override titleForHeader without any problems since this is what you want to do.
-(NSString*)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
if (section == 0)
return #"HELLO!";
else {
return [super tableView:tableView titleForHeaderInSection:section];
}
}
Sets the title of section 0 in the static table to HELLO!, overriding the title set in the xib. The others are left as the were in the xib.
The key point is that static tables are populated exactly the same way as dynamic tables, except that UITableViewController implements its own versions of all the datasource methods. These methods presumably read the information from the xib file and send back the appropriate information to the table view. If you want the static content, don't implement or call super. If you want your own content, use code similar to that above.
I'm a little confused by your question, perhaps i need some more info. are you saying that in storyboard you had a view controller and dragged a table view object onto that view controller?
if thats the case then this view controller would still be able to be that table view's data source and delegate
in your .h file:
#interface VIEWCONTROLLERNAME: UIViewController < UITableViewDataSource, UITableViewDelegate>
in your .m file you would need to declare the required methods for each of those:
cellForRowAtIndexPath, and numberOfRowsInSection (refer to documentation for required and optional messages) to avoid "incomplete implementation error". this is also where you could override titleForHeaderInSection.
and back in storyboard you would right-click on the table view and drag the data-source and delegate option to your view controller (or do so in the Connections Inspector tab)
if this is not what your doing please provide some more info.
I have a UIView that will need to display two UITableViews, but they are never shown together, by using a SegementedBar you can toggle one or the other.
What would be the best way to handle this? Just create one Table View Controller and change the data source, or create 2 Table View Controllers and just hide one when the other is visible.
The 2 tables will have a completely different layout with different custom cells.
I would keep one datasource & delegate.
This means that all the delegate/datasource methods become more complicated BUT it means that you can retain the one to one relationship between viewController & view.
keep a reference to each of the table views
//vc.h
#property (nonatomic, weak) IBOutlet UITableView* firstTableView;
#property (nonatomic, weak) IBOutlet UITableView* secondTableView;
In the datasource/ delegate methods you need to account for the fact that the method needs to behave differently depending on which table view is in use. e.g.
//vc.m
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
...
if (tableView == self.firstTableView) {
...
} else { // tableView == self.secondTableView
...
}
}
return cell;
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
...
if (tableView.tag == 1) {
...
} else { // tableView == self.secondTableView
...
}
}
tag could be assigned from the .xib.
so no need to have UITableVeiw variable in .h file.
Two table view in .xib needed
Both approach has some pros and cons, but i will personally prefer approach having two separate controller.
Approach 1 - create one Table View Controller and change the data source
This approach help in avoiding extra and repeated code.
With this memory management is good as using one controller only.(Although this is not a big concern till then we won't have a lot of data.)
Issue with this is having complexity.
Approach 2 - 2 Table View Controller
With this approach definitely have extra and repeated code.
But with this is less complexity.
In my current app, I need to have 4 UITableView in a single UIViewController, at once I've to show single table, based on the tab selected by the user, I've added four tables because, all of having different custom cells and functionality, to reduce complexity I took four.
The main benefit of this is that, each time you don't need to call reloadData to update a single table. I just need to properly handle table's show & hide flow. And believe me that's looks cool. Not flicking at all.
In my case, I am creating four tables by code only. And I make a method that will return me a table based upon a tag I've pass.
I keep cellForRowAtIndexPath as small as possible by dividing code into different functions.
Use separate UITableViewControllers and swap the views. It's less code, less complexity and it's the way Apple does it with the TabBar.
As for code complexity, there really isn't any. You simply do the following to switch views when the UISegmentedControl's value has changed:
UIView *previousSuperview = myViewController1.view.superview;
myViewController2.view.frame = myViewController1.view.frame;
[myViewController1.view removeFromSuperview];
[previousSuperview addSubview:myViewController2.view];
Alternatively, you could set the corresponding view's hidden property.