collectionView optional in UICollectionViewController - ios

Reading the Apple documentation on the collectionView of a UICollectionViewController, it appears to be optional, as denoted by the ?. This seems odd to me, as if there is a possibility that the collectionView could be nil and then the UICollectionViewController would seem to be pointless. In regards to the tableView of the UITableViewController, the Apple Documentation states that it cannot be nil, as it is marked with a !.
My question is, why would the collectionView be optional, and what must I consider when using a UICollectionViewController? Must I be concerned that the collectionView will be nil and, in addition, at what point is the collectionView initialized?
Thanks.

When a controller is created all its views start out as nil because they haven't been created yet – iOS lazy loads as much as it can for performance reasons. When a view controller's views have been fully loaded its viewDidLoad() method gets called so you know it's all safe to use.

When I write a view controller I try to create my views in loadView method so until view is needed all view properties are nil and thats why they all need to be optionals. I assume it's the same with UICollectionViewController and UITableViewController.
And the reason why there's ? for collection and ! for table? I think it's pretty simple. UITableViewController is much older than UICollectionViewController and view properties of both are annotated differently (nullable vs. null_resetable) which results in different behavior when using Swift.

Related

UITableViewController subclass implements UIDataSourceModelAssociation protocol but its methods are never called

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.

What class should I run async calls from?

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,

UITableViewController vs UIViewController

What all does UITableViewController get me that makes it actually useful? Is there any bonus is using it instead of just using a UIViewController for views with UITableViews in them? I ask because I want my views with UITableViews to inherit from a base view controller (which inherits from UIViewController). If I use a UITableViewController then having a base class for all my views becomes more difficult.
Not much. It provides some conveniences, but you never really need it; I often don't use it.
The main things, aside from the automatic setup when you create one in the storyboard, are the three properties tableView, clearsSelectionOnViewWillAppear, and refreshControl. But they don't do anything you can't do yourself.
Well I do think both Retro and matt's answer are correct, from personal experience it is much easier and less "hackY" to use UITableViewController with UIRefreshControl, then trying to get UIRefreshControl work with UIViewController with a UITableView.
Nothing more then the boilerplate code ready for setting the data source and connection to your tableView object, some getters and setter to make your life easy if you not want to put your effort to do the same thing which is available.
But in your case for SubClass its not fit to have UItableViewController

UICollectionViewCells and Buttons

I've been trying to get my UICollectionView to respond differently to single and double taps but all the answers I have found seem to suggest this is not really feasible because the single taps get recognised first. It works on really slow taps, but anything faster always initiates the default gesture recogniser (if anybody has got this to work I would love to know)...
So anyway, I have now resorted to putting buttons in my UICollectionViewCell (which has it's own class and NIB file).
The question is this:
What is considered the best way to use the button in the UIViewController of the collectionView?
I currently have a protocol in the header of my subclass of UICollectionViewCell and have declared my viewController as the delegate and implemented the required protocol functions.
In collectionView:cellForItemAtIndexPath: I set the VC as the delegate of the cell.
It all works but it seems a bit long-winded, and maybe not a great way of doing this.
The other way I was thinking of was instead of using delegates to simply call addTarget:Action: on the property of the UICollectionViewCell in collectionView:cellForItemAtIndexPath:.
This seems simpler but the delegate pattern looks to me like the better fit.
Any and all advice on which would be better, why, and any more appropriate alternatives welcomed!
Thanks.
You're doing the right think using the delegation pattern. The ultimate responsible object for any action of your views is the viewController who's displaying those views. Therefore, using it as the delegate for you cell's protocol is just right.
create a custom subview of UICollectionViewCell and place your button in the initWithFrame method. Declare the button to be public so you can use it later in your uicollectionviewcontroller or uicollectionview if creating programmatically.

MonoTouch: UITableView outlet variable is null

I am running through the "Navigating with tables" section of Wrox' Professional iPhone Programming with MonoTouch by McClure et al, picking up the basics of putting together a hierarchical UI for iOS, and running into the following problem.
I have created a new "iPhone View with Controller" file (called ParametersViewController), deleted the UIView from it, added a UITableView, created an outlet for it (tableView) and connected the "File's Owner" view outlet to the UITableView, per the tutorial.
In the RowSelected method of this view's parent view, I instantiate my ParametersViewController, calling the default constructor, in which I want to set up the table view's data source:
this.tableView.Source = new DataSource(this, new [] {"one", "two", "three"});
(DataSource is a nested class which inherits from UITableViewSource)
All compiles and runs fine, up until the point where that line is executed. Turns out that this.tableView is null, so I get a NullReferenceException.
tableView is the outlet for the table. How can it be null? Can't I set up the table source here in the constructor? If not, where do I do it?
Solution to this was
Don't create an outlet called tableView
Make the view a subclass of UITableViewController, instead of UIViewController
Refer to the base.TableView property of my UITableViewController, rather than the nonsense outlet I created
I'm not 100% clear on what you're doing where, but it might be that you need to access your UITableView in the UIViewController's ViewDidLoad method. At that point your NIB view(s) should be instantiated.
If that's not the problem, it may help if you could provide some more code so that we can see exactly what's going on. I think the lack of responses might due to people not being sure that they understand the nature of your problem exactly.

Resources