This has been driving me mad - I have a Class ParentViewController (no xib) with #property picTable, and then ChildViewController (subclass of ParentViewController) with xib and the picTable part of File's Owner linked up.
I noticed the problem as I need to add in more data from a URLRequest - I have the data in an NSArray, but calling reloadData does nothing - and breakpoints are showing that self.picTable is then null - but it is definitely linked (checked via breakpoints) earlier in the view's lifecycle.
I've tried using NSNotificationCenter, a dispatch queue and [self performSelectorOnMainThread... all to no avail - my refreshTableView is always called but the breakpoints show the table view as null.
Tried -
#property (nonatomic, strong) IBOutlet UITableView *picTable;
And -
#property (nonatomic, retain) IBOutlet UITableView *picTable;
Links to the two classes:
Parent:
https://github.com/gileze33/Ticklr/blob/master/Ticklr/GWTPicPresentingViewController.h
https://github.com/gileze33/Ticklr/blob/master/Ticklr/GWTPicPresentingViewController.m
Subclass:
https://github.com/gileze33/Ticklr/blob/master/Ticklr/GWTPicViewController.h
https://github.com/gileze33/Ticklr/blob/master/Ticklr/GWTPicViewController.m
Even though you call [super initWithNibNamed...] from the subclass, I'm doubtful that the nib is hooking up the tableview to the superclass. Even if it is important to your design to have the tableView property defined in the superclass, try moving the tableView property down to the subclass and see if it works then.
Related
As I read in another question, a rule of thumb was:
When a parent has a reference to a child object, you should use a strong reference. When a child has a reference to its parent object, you should use a weak reference or a unsafe_unretained one (if the former is not available). A typical scenario is when you deal with delegates. For example, a UITableViewDelegate doesn't retain a controller class that contains a table view.
Well, in this case I have a view controller with a table view IBOutlet. This table view also uses the view controller as a delegate and data source. By the rule above, the view controller acts as the parent and so I should have a strong reference to the table view - the table view should have a weak reference to the view controller.
I'm not sure how to define that latter weak reference - I'm setting
tableView.delegate = self;
tableView.dataSource = self;
How am I supposed to make myself weak? Or is this not the right way to do it?
You can just drag the outlet from your UITableView to your .h file. Xcode will create appropriate property for you. You can choose weak/strong type from there as well while naming the Outlet.
If you would however like to create it manually, you can add this line to your .h file:
#property (weak, nonatomic) IBOutlet UITableView *tblView;
But do keep in mind that even after adding this line to your .h. file, you will still have to connect it to your UITableView.
Also don't forget to make your ViewController conform to UITableViewDelegate like below:
#interface MyTableViewController : UIViewController <UITableViewDelegate, UITableViewDataSource>
In your title however, you seem to be confused about the difference between Weak and Strong properties. Ole Begemann has answered about the difference splendidly on This question.
The code you wrote is fine. You don't have to make yourself weak. The delegate and datasource properties are declare as weak in the UITableView definition.
delegate and datasource are already weak as per UITableView.h.
#property (nonatomic, weak, nullable) id <UITableViewDataSource> dataSource;
#property (nonatomic, weak, nullable) id <UITableViewDelegate> delegate;
If any instance is set as delegate and datasource won't get retained by tableview instance
And when you make a IBOutlet make it like this. Choose weak if its strong by default its weak as shown in image
#property (weak, nonatomic) IBOutlet UITableView *tableview;
As the view is already retained by the view it's added (i.e self.view)
It is just like a view in UIKit Control to display data in list form. It is not a child controller. All UIKit items are referenced as weak.
So I've created a custom UIView subclass and have it assigned to a UIView in my main storyboard. When the view loads everything is displayed properly.
The issue I'm having is that I need to be able to access properties of said custom UIView since the view is data driven.
JSON_table.h:
#import <UIKit/UIKit.h>
#interface JSON_table : UIView
#property (strong, nonatomic) IBOutlet UIView *view;
#property (weak, nonatomic) IBOutlet UISearchBar *searchbar;
#property (weak, nonatomic) IBOutlet UITableView *table_view;
#property (weak, nonatomic) NSString *data_header;
#property (weak, nonatomic) NSString *data_list;
#end
JSON_table.m:
#import "JSON_table.h"
#implementation JSON_table
- (id) initWithCoder:(NSCoder *)aDecoder{
self = [super initWithCoder:aDecoder];
if (self) {
[[NSBundle mainBundle] loadNibNamed:#"JSON_table" owner:self options:nil];
[self addSubview:self.view];
}
return self;
}
#end
(I know I'm missing delegates for tableview, ill be adding these later)
The issue I'm having is when I right click on my UIView on my storyboard I get:
The problem is when I try to connect "view" to my header file "
ViewController.h" it doesn't let me create a IBOutlet, so I cannot reference my view and its properties in code.
This is what I am trying to accomplish:
"Table" is of type UIView
Idea:
Would this have anything to do with the UIView being on the second view in my storyboard? I noticed that I don't seem to have any problem attaching to anything on the first page, but the second one I can't.
You can only connect the outlets of a view to it's class object. You are trying to connect outlets of JSON_table object to UIViewController object.
If you need to access those properties in UIViewController object. You need to import
JSON_table.h
in your view controller. And create and instantiate a object of it.
JSON_table * customView = [[JSON_table alloc]init];
Now you can access all the properties of it as:
customView.searchbar, customView.view etc.
Added by theshadow124:
Thanks to everyone who attempted to help me solve my problem. Due to being fairly new to coding for iOS I didn't realize I had to assign a custom class to every UIViewController in my storyboard(I thought they they would inherit from the base if I didn't specify). simply creating a new subclass of UIViewController and assigning it under the Identity inspector fixed my problem and now I can properly assign outlets.
Im going to accept this answer because it was one of the issues I ran into after fixing the subclass on the storyboard issue.
Please make sure that in assistant editor your are opening the same class that your custom class is contained in .
I have a view called FeedView, handled by FeedViewController.
I also have a XIB called "NearestStore" which is handled by a view controller named "NearestStoreViewController". NearestStore xib has labels, buttons, etc. In the view controller I have outlets that are connected to the subviews in NearestStore.xib.
NearestStore inherits from UIButton (so it's easier to handle click event).
On FeedViewController.xib I have a UIButton that has been set to be of type NearestStore.
So far so good. This is on my FeedViewController:
__weak IBOutlet NearestStoreButton *btn_nearestStore;
The outlet is connected on the xib to the outlet.
NearestStoreViewController has several outlets to subviews like:
#property (nonatomic, weak) IBOutlet UILabel *lbl_distance;
#property (nonatomic, weak) IBOutlet UIImageView *img_distance;
For some reason, on my FeedViewController the reference to btn_nearestStore is fine, but all the subviews are nil.
For example:
btn_nearestStore.lbl_distance
is nil
What am I missing?
This sounds exactly as the system is supposed to work. It is not easy to create a custom widget using xibs.
Here's how it works:
Your FeedViewController will preform xib loading for the corresponding FeedView.
During this load, it notices the NearestStoreButton subview. As a consequence, it creates such a view using the - (id)initWithCoder: message on the NearestStoreButton class. It will not magically notice the corresponding .xib nor the corresponding viewController.
If you need to use a xib within a xib, you need to do the loading manually for all subviews. Keep in mind that you somehow need to create/use the appropriate owners (view controllers) for these secondary xibs.
It's hard to tell from your description, but this sounds like a problem with "owner" of the loaded NearestStoreButton XIB. When you load a NIB, you give the loader an owner, and its this owner on which most outlet bindings and actions are made. If you're loading your NearestStoreButton with UINib, then when you call instantiateWithOwner:options:, make sure you pass the object on which the outlets should be set as the owner.
When are you calling the outlet? If you are trying to access the property in the initWithCoder method of the view, it's not guaranteed that object has been instantiated.
If you access your property in the awakeFromNib method within the view you should be able to get it. For instance, I have a custom view, and my code looks as such:
- (id)initWithCoder:(NSCoder *)aDecoder
{
self = [super initWithCoder:aDecoder];
if (self) {
//Don't style the subviews in here since they won't be initialized
}
return self;
}
- (void)awakeFromNib
{
[super awakeFromNib];
[self styleViews];
}
- (void)styleViews
{
//access my properties and style them in here
}
Following post contains detailed explanation about creating custom views using Nib:
creating custom view using Nib
After I create the customInit as mentioned in the post, I am able to get the IBOutlets allocated.
I'm writing iOS apps using ARC and targeting iOS 5+.
Suppose I write a custom view object that has a delegate property. In declaring the delegate property, I make it a weak reference to avoid a retain cycle, so that when the actual delegate object (the controller) is destroyed, my custom view will also be destroyed, as follows:
#interface MyCustomView : UIView
#property (nonatomic, weak) id<MyCustomViewDelegate> delegate;
#end
All is good.
Ok, so now I'm writing the controller object, and it has references to two view objects: my custom view and an Apple-supplied UIKit view, both of which declare delegate properties, and the controller is the delegate for both views. Maybe it looks something like this:
#interface MyViewController : UIViewController <MyCustomViewDelegate, UITableViewDataSource, UITableViewDelegate>
#property (nonatomic, strong) MyCustomView *customView;
#property (nonatomic, strong) UITableView *tableView;
#end
#implementation MyViewController
- (void)viewDidLoad
{
self.customView.delegate = self;
self.tableView.dataSource = self;
self.tableView.delegate = self;
}
#end
My question is this: Do I need to override dealloc to set either or both delegates to nil?
I mean, as I understand it, the delegate property of the UIKit view (in this case, tableView) isn't actually declared to be a weak reference, but rather an __unsafe_unretained reference, for backwards compatibility with non-ARC version of iOS. So maybe I need to write
- (void)dealloc
{
_tableView.dataSource = nil;
_tableView.delegate = nil;
}
Now, if I do have to override dealloc, I still don't have to set _customView.delegate = nil, right? Because that was declared (by me) to be a weak reference, so it should be set to nil automatically upon the destruction of MyViewController.
But on the other hand, I'm not targeting non-ARC versions of iOS, nor do I intend to. So maybe I don't need to override dealloc at all?
Setting non-weak delegates to nil is generally a good idea unless you know you don't have to. For UITableView and UIScrollView, I've experienced crashes on previous iOS versions with the following steps (it may help to run with zombies enabled):
Scroll really fast.
Press Done or the back button or whatever to dismiss the VC.
This appears to happen because the scrolling animation is retaining a reference to the view, so the view outlives the VC. It crashes when sending the scroll event.
I've also seen crashes after dismissing a VC containing a UIWebView while a request is being loaded, where simply setting the delegate to nil was not sufficient (I think the workaround was to call [webView loadRequest:nil]).
If the only strong reference to said tableView is your sole MyViewController controller, you don't need to manually set UITableViewDelegate or UITableViewDataSource to nil.
The reason is that once the dealloc method on your MyViewController is called, the tableview will also be destroyed along with the controller (that is, once again, as long as the only reference to it is your sole controller MyViewController class).
If you have other strong references to this tableview, such as other controllers, it would then be possible that the tableview could then exist longer than the MyViewController class. In such a case, it would be necessary to set the UITableViewDelegate and UITableViewDataSource to nil in the dealloc method of MyViewController because, as you mentioned, these properties are NOT weak references and will not automatically be set to nil.
However, this sort of situation is pretty rare in my experience though.
Most of the time, I don't worry about setting these to nil honestly, but it is a defensive programming practice.
See this post also:
In dealloc method set any delegate to nil is needed or not needed
The only reason you would want to explicitly set the delegate and dataSource to nil is if the customView or the tableView could out live the view controller. Setting them to nil would guard against the delegate or dataSource referencing a deallocated object.
If the customView and tableView will be deallocated along with the view controller, there is no need to nil out the delegate and dataSource.
I have added my custom UICollectionViewController as an object (A) to the interface builder, and given it the proper custom class.
this EOCollectionViewController is also an IBOutlet in my main Viewcontroller
I have added the UICollectionView to the main view of the application.
I have made all the links possible. UICollectionView has the object (A) as a delegate and a datasource.
The object (A) has the UICollectionView linked to the view property. (not the self.collectionView property, as this is not there)
It all works well. Collection view get's filled. The CollectionViewController resides in the property of my main view controller.
Only the self.collectionView is nil...when I try to reach it from within the UICollectionViewController.
My first guess is that "extra" UIControllers used in IB do not get initalized in the same way. I also tested, and viewDidLoad and init never get called for these extra objects.
Attached the header of EONoteController (and the IBOutlet added as a work-around)
#interface EONoteController : UICollectionViewController<UIGestureRecognizerDelegate>{
__weak IBOutlet UICollectionView *cvNotes;
}
#property (nonatomic) EDAMNotebook* notebook;
#property id <EODragHandler> draghandler;
#end
My first guess was to "fix" it, in the viewDidLoad, as follows
self.collectionView = self.view, but viewDidLoad never get's called.
What am i Missing?