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.
Related
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.
I have created a UITableView inside my UIViewController programmatically using this guide on the Speak Objectively Blog.
However I now have the problem that .reloadData() isn't refreshing the view. However if I drag or move inside the UITableView the data appears correctly.
This answer suggests that I haven't set up the outlet. To my understanding IBOutlets are for connecting elements in the Interface Builder to the code. Please correct me if my understanding here is wrong.
How do I set an outlet if I created the UITableView programmatically?
The guide creates a UITableViewController. It has already an instance of UITableView on it, and it is the one you see in screen. There's no need of creating outlets, so that answer is not actually the answer to your problem (but might be somehow related)
What I can imagine is that you are sending the message somewhere else.
As you can see in the Table View Controller Class Reference , you can get your table view by self.tableView (or just tableView in swift)
So make sure you are sending the message to self.tableView.reloadData()
If it doesn't work, sending part of your code will help us to see better than to imagine where could be the problem ;)
If self.tableView.reloadData() doesn't work on its own, use:
dispatch_async(dispatch_get_main_queue(), {self.tableView.reloadData()})
or
dispatch_async(dispatch_get_main_queue()) {self.tableView.reloadData()}
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.
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.
Please help with this issue that is driving me insane...
I want to populate a table with coredata (in a UIViewcontroleer), not a UITableController, as I need the table to be small and not take the whole screen,
I have tried different approaches, to no avail, and no answer to fix the problems,
Im adapting some code from tutorials that use the whole screen,
example problem, Request for member tableView in something not a structure or union
all my searches for code show the use in iphone with tableview, It would be awesome if you know of a sample for ipad with a view controller (not in split view),
thanks in advance
Your error stems most likely from you using the tableView property which is not available in UIViewController. It's difficult to judge what you did wrong, did you implement the necessary UITableViewDataSource and UITableViewDelegate methods? If you implement these it's no problem to use a UIViewController with core data. But remember the UITableViewController does more than just fill the table, it also resizes the table view when the keyboard is shown. You'll have to do this yourself, too.
UITableView has a rowHeight property that you can set directly or via the delegate method tableView:heightForRowAtIndexPath:.
UITableViewCell has a font property that you can set, to make the font smaller.
UITableView class ref says rowHeight is in points. If that means the same as points in NSFont, it should be easy to coordinate the two.
And of course you can resize the table view’s frame in IB.
So you ought to be able to get what you want using a UITableViewController.
As for the error, do you know what line is generating it? Have you set breakpoints to find out?