My app uses the Stanford course's CoreDataTableViewController, which is a subclass of UITableViewController.
For particular views of my app I create subclasses of CoreDataTableViewController to manage a particular view.
I've just recently hooked up state encode and restoration throughout most of my app and it all seems to be working ok. The only thing I've left late is implementing the UIDataSourceModelAssociation protocol to allegedly preserve visible and selected rows of UITableViews (I was also hoping it may preserve the editing state of the table view, and of a particular row if I've selected it to delete, but haven't confirmed yet).
I implemented the UIDataSourceModelAssociation within CoreDataTableViewController hoping that it would just then work for all my views, but on debugging it the feature doesn't work, and adding breakpoints I see that neither of the two UIDataSourceModelAssociation methods are ever called when i put my app into the background with the home button, or restore it cold by re-running in XCode.
CoreDataTableViewController and my subclasses of it implement some dataSource methods to actually display the rows in my table view. I've searched around and found that UITableViewController automatically adds itself as the dataSource for the tableView.
I've added sanity asserts in my subclasses viewDidLoad functions of the form
assert(self.tableView);
assert(self.tableView.dataSource == self); <--- fails here
Which fails on checking the dataSource being ourselves. I can see by logging and using the debugger that the dataSource is non-nil at this point.
I guess I just wondered whether when we subclass a class does each class share the same self value, or is it because my UITableViewController has set itself to be the dataSource, but by the time I've subclassed it twice down to my particular view instance it's self is slightly different than the parent UITableViewController. Or is something else intercepting the dataSource and forwarding on as required. As a fix should I consider hardcoding my own subclass to self, or have the CoreDataTableViewController add itself as the dataSource explicitly as this is where the UIDataSourceModelAssociation methods are implemented.
For reference my view hierarchy consists of my UITableViewController subclassed views living inside NavigationControllers which I've heard may cause issues.
Just a little of clarity on how subclassing should work with respect to self pointers across the classes and wondering if anyone could figure out why my protocol methods aren't being called would be really appreciated.
Cheers
I'm still not sure why my tableViewController's dataSource isn't directly set to self. But clearly my dataSource methods are called as my tableView functions. Obviously some sort of object is forwarding on dataSource methods to my actual class for me...
In the end getting the UIDataSourceModelAssociation protocol methods to be called was as simple as ensuring that both the UITableViewController and the UITableView within it had restoration ids.. with just the outer UITableViewController having a restoration id, none of these methods are called.
Sadly now I'm scuppered by these methods being called before my UIManagedDocument has finished loading so I cannot actually do anything useful within them. That's another problem though.
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 iOS app I have a view controller and a UITableView subclass on one of my views. Currently the UITableView manages it's own data (creating connections, and being the delegate which handles the callbacks).
I was wondering if this is best practice? Is it better to run this on the view controller and then pass the data into the table? Does this not matter at all?
Please explain the reasons in addition to answering my question.
Thanks!
It would be more standard to implement the delegate and dataSource methods in the ViewController.
It is quite unusual to subclass the UI*View classes, except to customise drawing. UITableViewCell is a bit of an exception to this rule.
If you find your ViewController getting a bit big you might think about implementing the delegate and dataSource in a separate class.
In addition to mcfedr's answer, views are things that interact with user and it is generally good to separate concepts (unless unavoidable). Also note that historic versions of ios (iirc) view controllers may load/unload views on demand, so any application logic in there may cease to exist.
In this scenario, I generally have only one class... The UIViewController instance that contains the following view hierarchy:
view -> myTableView
With this approach, my UIViewController will:
Have an IBOutlet to a UITableView (myTableView)
Implement methods of UITableViewDelegate
Implement methods of UITableViewDatasource
Set myTableVIew.delegate to self
Set myTableView.datasource to self
When the host UIViewController is the delegate and datasource of your UIViewContoller, all code can exist in one class. This keeps the code of your project tighter but does tightly couple your logic.
The alternative approach would be to subclass UITableView and implement UITableViewDelegate and UITableViewDatasource. This will be a desirable approach if:
You wanted to reuse the UITableView in multiple UIViewController
You wanted to encapsulate the UITableView logic from the UIViewController
Here is the thought process I use:
Am I going to reuse this UITableView and its logic?
Yes -> UIViewController class and UITableView subclass
No -> UIViewController class only
The best practice would depend on the number of programmers involved, the repeatability of the UITableView, coding patterns already established. This is often a matter of preference and I hope my answer shed some light on why you would go either way,
I'm building an app that presents table views in a majority of the screens. They present a handful of different table view cells, but there is one that is presented in 3/5 of the table views. This cell, which displays a video and provides an interface for users to interact with the video (like, comment, flag, delete, play/pause, etc), has a fairly large delegate with seven methods/functions.
My question is:
Would it be a best practice to set up a separate controller that would be a property of my view controller to be assigned as the delegate to the cell, or to subclass a UITableViewController with the methods already implemented?
The problems I see with the latter is that I would then have to implement a weird way to handle the data source (set up methods to return the models, always ensure that videos are stored in that array) and the former just seems a little odd to the standard MVC practice. Any advice?
Update
I began factoring out a data source to use that implements the cell's protocol. Another issue that I seem to be running into is displaying multiple cells, i.e.:
I have a searchDisplayController that displays UserCell's and VideoCell's, based on the selectedScopeIndex of the search bar. One way I could handle this is to create a dataSource for this tableView that handles both cases, or swap out data sources based on changes to the selectedScopeIndex. Are either of the two options looked down upon? Is swapping a table view's data source valid?
I solved this issue by implementing a UITableViewDataSource controller that would also handle the cell's delegates. I was able to shorten the 7 method delegate to a 3 method delegate on the data source, used to push new controllers, remove objects from the data model, and handle fading/updates.
Granted, I needed to pass reference to the UITableView, the UIView, and the UIStoryboard of the source UIViewController, but the code is much more readable and manageable.
I have a class that inherits from UITableViewCell, it has a bunch of IBOutlets. I had previously been using this object in a way that reuses the cell and initializes it as it's needed. This method is too slow, so I decided to create an array of the UITableViewCell objects and then add them as needed in the cellForRowAtIndexPath: method.
Everything gets loaded fine except the IBOutlet objects. awakeFromNib is never called so I assume this has something to do with my issue.
Just to clarify it was getting called fine when I was initializing the cells in the cellForRowAtIndexPath function, it's just when I tried to preload them in the view controllers viewWillAppear method that it breaks.
I know this is an old question. But after reading the conversation, I feel I must give some input. This a multi-layered issue that you are dealing with here.
Firstly, when you say "preload" what is it that you mean exactly.? Are you calling dequeueReusableCellWithIdentifier in your viewWillAppear? Or are you calling an init. Either way this is not an acceptable practice whatsoever.
Remember, the UITableViewCells are "Reusable" meaning that the UITableView, in effect, handles the unloading and loading of the UITableViewCells when they are offscreen to optimize performance (not memory, believe it or not) as well as some other things. Essentially, it's a pretty nifty "hack" to keep table views efficient. So if your UITableView is too slow, then you are doing something wrong, not the implementation of UITableView
When you say you are "preloading" them, that throws up some immediate red flags on your usage. And when you say "the method is too slow", well it's not. The delegate/datasource methods in place for UITableViews and UICollectionViews happen in calculated orders. Don't fight it.
Secondly, for your issue with awakeFromNib not getting called. You aren't providing enough information. How did you initialize it? If you created it in your storyboard/nib, you shouldn't be calling any of the init methods. In fact, as of iOS6 you are guaranteed a non-nil cell from dequeueReusableCellWithIdentifier.
Have you called it through its super class like :
-(void)awakeFromNib
{
[super awakeFromNib]; // Use this line
}
Hope it helps you.
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.