I've got a static TableView that now needs to become a dynamic TableView, because other views need to be placed around the ViewController, and this can not be done using containers in my case.
The question is: how do I efficiently convert the table view from static to dynamic?
I'm aware of having to change the inheritance from UITableView to UIViewController and add the plus the delegate methods.
But how about all of the Table-Sections: I have 3 sections with 6 types of cell in the static table. Do I really need to subclass UITableViewCell for all of these cell-types and deal with everything manually, or is there a more clever way to do this?
You really can't just convert between the two. By merely implementing some of the tables delegate methods, like cellForRowAtIndexPath:, you loose your static content. That being said, the table should be dynamic the entire time. This way, you can define logic to determine whether or not it should show the content that you originally added statically, or the new dynamic content.
Additionally, you don't need a view controller to implement the delegate/datasource methods. If you already have a subclass of UITableView, that's fine. You can set it as its own delegate/datasource, and implement those methods within the subclass.
And to answer your last question, no there really isn't a better way to do that. I recommend that you create one base class that subclasses UITableViewCell that implements everything that the cells will share, and then implement the individual changes in subclasses of this base class. Using multiple cell subclasses in a table view sounds a lot worse then it is.
Related
I have a lot of common logic, therefore all my viewControllers inherit from BaseViewController, BaseDetailsViewController or BaseWebViewController. The last two inherit from BaseViewController which inherits from UIViewController. The problem is I cannot use tableViews with static cells because my BaseDetailsViewController is not inheriting from UITableViewController.
I'v never used and inherited UITableViewController because the same could be achieved by inheriting from UIViewController, wiring it with an added UITableView and having implemented datasource and delegate methods. Therefore for the form type screens (SignUp or other data input screens), I use my own BaseDetailsViewController which adds nice and handy methods (input validation, custom styling, scrolling keyboard, handles input field navigation and etc.)
Using UITableViewController has two real benefits: 1) keyboard scrolling (if you use text fields) and 2) easy creation of your form UI elements inside on screen. The first benefit is irrelevant for me, because I'v already have my own implementation of this stuff inside BaseDetailsViewController. Regarding the second benefit, I'm creating my form inside scrollView which gives a lot of flexibility but it's a little bit more effort (especially when need to update the screen). Therefore I heard some guys implemented their own UITableViewController and then made it to inherit from their BaseViewController. Therefore I started to dig into this approach.
So far I'v came upon these two approaches:
Recreate your own tableVC. Not clear how to make it work for static cells. Currently my demo app crashes if my ReplaceTableViewController is not implementing numberOfRows and cellForRow, and not shows static cells if I implement them with dummy content.
method-swizzling. Not clear how to change class inheritance by injecting baseViewController inheritance for UITableViewController, thought still not clear which methods are needed to be added.
Anybody tried and want to share?
UPDATE I'm not using storyboards as I promote clean MVC - every screen as component should have its own Model, View (Xib), and controller, all stored in separate files to eliminate merge conflicts of multiple devs in large app projects (30+ screens). And therefore container view with embedded segue to tableViewController is also not an option.
UPDATE2 In case anybody would like to take a look why and how I'm doing this, here's a link to my open source template based new project generator framework which I started to publish recently.
Static cells need a UITableViewController. You can't changed this right now.
But to use static cells in a UITableView outside of a UITableViewController you can use a ContainerView in your Non-Table-UIViewController, where you place a real, separate created UITableViewController working with those static cells.
To share common code between multiple view controllers, inheritance is not that best solution as you found out by yourself while subclassing UIViewController vs. UITableViewController. You can use some sort of composition for sharing code or - especially for objective-c - categories.
Categories are not allowed to have its own properties, but there are workarounds possible with objc_setAssociatedObject.
Another approach would be to not use static cells, but dynamic cells in a UITableView with DataSource-Delegate :)
As you see in my screenshot, to reuse a special TableView with static cells, i place it in other ViewControllers in a ContainerView. Since you are not using storyboards, i am pretty sure that this can also be done by code without storyboard.
I have a working UITableViewCell. Now I have a different cell I need to make which is exactly like the previous one - but has a UIWebView instead of UIImageView.
I was thinking of doing a different xib for the cell, but the thing is they have tons of code and logic in common (for the rest of the controls)
What is my best approach in this case?
You can make a parent class: put all the common code there, then make 2 subClasses of this superClass, they'll inherit the superClass code, and put there all the code and objects specific for each of them
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.
I have a custom UITableViewCell class that I use to display quite a complex set of data.
Essentially the cell displays a Match object. But in doing so it displays information about the two Teams, the score, the time elapsed and so on.
Thinking about MVC and clean code.
Should I just pass in the Match object and let the cell do everything? Or is it better practise to expose the different elements of the cell (team1NameLabel, team1ScoreLabel, team2NameLabel, etc...) and set them all individually in the UITableViewController?
The first way makes the UITableViewController cleaner but then I'm relying on the UITableViewCell to "know" about the Match class, the Team class etc...
The second way makes more work for the UITableViewController but then makes the UITableViewCell a "dumb" display. All it does is then lay out the information within the cell. It doesn't know anything about the information it is displaying.
I would follow these rules:
The cell should just have the outlets for displaying the various bits of data. It is a view so it should not contain any logic.
The controller should get the Match data, parse and make calculations if necessary, and populate the cell. It is a controller, so that is its primary function in a MVC context.
IMO it is better and more MVC-like to pass the Match object to your table view cell.
Lot of the code you find on the internet(even Apple examples if I remember well) is not doing that. You can see many times a configureCell method within the view controller that is called in tableView:cellForRowAtIndexPath:.
I prefer to pass the model object object to the cell, it makes my view controller code simpler and also it is simpler to unit test: when I test my view controller I only verify that the model object is passed to the cell, and then in the table view cell tests I verify that the test of the labels is set to the expected values. Someone may say that this is making the view knowing about the model, but I don't see any big problem on that.
Both ways are fine, but personally I would go for second option, i.e. table view exposing #property and, if necessary, outlets.
However, if you really want to go for the first option, I would suggest to have any objects passed to the cell to implements a protocol exposing few methods:
#protocol tableViewCellProtocol
-(NSString*)titleForCell;
-(NSString*)descriptionForCell;
Then you can "pass the protocol" rather than the object.
[mytableCell renderObject:objectImplementingProtocol];
This way you slightly decouple the objects itself, and prepare cell for reuse with other objects.
I have a MainViewController, and I want to add two UITableView's to it, each with different cells. But I don't want to clog up my MainViewController code by checking in the table delegate methods which table it is, and then acting on it. It gets too messy.
So I thought I would subclass UITableView and let it handle the cellForRow and other table methods by itself, and this way when I want to add a table to MainViewController, all I'd have to is
CustomTable *customTable = [[CustomTable alloc] init];
[self.view addSubview:customTable];
and all the delegate methods would be handled in that class, leaving my MainViewController clutter free.
Am I approaching this wrong? Should I be subclassing UITableViewController instead? What's the difference?
When to subclasss UITableView? Not now.
Create two classes, which are member variables of your view controller. Point the table view delegates at each of your two new classes.
In Cocoa you tend to combine classes rather than inherit from them as you usually do in Java and C#.
In 3 years of professional working as a objective-c programmer, I didn't need to subclass UITableView once, the patterns, cocoa is depending on, — MVC and delegations (with using protocols), are just simple yet strong enough. And populating a tableview is just one of the best examples.
Make sure, you understand all this topics, as otherwise you will find yourself fighting the framework constantly.