I got confused on subclassing a UIViewController in iOS, I have a parent viewcontroller which conforms the UICollectionViewDataSource protocol (in its private interface inside the implementation file).
/* Parent.m */
#interface Parent () <UICollectionViewDataSource>
// this CollectionView is connected to storyboard
#property (weak, nonatomic) IBOutlet UICollectionView *CollectionView;
#end
#implementation Parent
- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView
{
return 1;
}
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
{
return self.somecount;
}
#end
And then I create a child view controller which inherited from parent. The child knows nothing about UICollectionViewDataSource as the datasource implemented in parent's private interface.
/* child.h */
#interface child : parent
// nothing was mentioned here that parent has a method to set the count using 'somecount'
#end
Then I set the viewcontroller from mainstoryboard as the child view controller.
how come ios get the value from parent's property 'somecount' and set the value for child?
thanks.
You ask:
how come ios get the value from parent's property somecount and set the value for child?
A subclass always inherits the properties and methods of its super class. They may or may not be public interfaces (you haven't shown us the declaration of somecount, so we don't know), but regardless, they are there and will be resolved at runtime (unless you override those methods/properties in the child, which you don't appear to be doing). If there are private methods and properties in parent, you might not be visible at compile-time from the child, but they're still there and will behave properly at runtime.
So, when the scene with the collection view specifies the child as the data source for the collection view, if child doesn't implement those UICollectionViewDataSource methods, it will automatically end up invoking those of the parent. Likewise, when any of those methods refer to somecount, if the child doesn't override it, it again will end up calling the appropriate accessor methods of the parent. Bottom line, child automatically inherits all of the behaviors, methods, and properties of the parent.
Related
This question already has answers here:
Passing data between view controllers
(45 answers)
Closed 8 years ago.
I have 2 views, ParentViewController and ChildViewController; I want to nest ChildViewController inside ParentViewController. I have designed ParentViewController and ChildViewController in Storyboard. ParentViewController.m contains the logic for the parent and ChildViewController.m contains the logic for the child. In ParentViewController.m I add the child like so:
ChildViewController *childVC = [self.storyboard instantiateViewControllerWithIdentifier:#"ChildSBI"];
[self addChildViewController:childVC];
My Question: How can I receive information (like an NSString) from the child back to the parent? Should I do this through delegation?
A common pattern would be to have the child as a property of the parent, and have the parent be a delegate of the child. The first thing you need to do is make your own protocol.
// MyProtocol.h
- (void)heyParentSomethingHappened:(Something)something;
Next, make the child a property of the parent so they can talk through delegation.
// ParentVC.m
#interface ParentVC()
#property (nonatomic) ChildVC *child
#end
Now that the parent has the child as a property, they need some way to talk. This is where the delegation comes in. Have the parent conform to MyProtocol.
// ParentVC.h
#interface ParentVC : UIViewController <MyProtocol>
Now that the parent conforms to your special protocol, have the child make it a delegate.
//ChildVC.h
#interface ChildVC : UIViewController
#property (nonatomic) id <MyProtocol> delegate.
#end
Now that the child has the delegate property, set it to the parent and you are good to go.
// ParentVC.m
- (id)init {
// do your init
self.child.delegate = self // both (child.delegate, self) conform to <MyProtocol>, so no type mismatch.
}
Now when your child needs to alert your parent of something, they have a formal way of talking through the protocol + delegation.
// ChildVC.m
- (void)someFunc {
[self.delegate heyParentSomethingHappend:[Something new]];
}
Remember to always include the protocol file when using it.
I've got a ViewController that has a UITableView within it. When I'm watching tutorials people are using things like this:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return _Title.count;
}
How am I able to generate the stubs without firstly creating the class with them in. When I made the class I selected it as a UIViewController. I've been playing around trying to auto generate the stubs but all to no avail.
Simply add the UITableViewDataSource (and most likely the UITableViewDelegate) to your UIViewController declaration. Example:
// MyViewController.h
#interface MyViewController : UIViewController <UITableViewDataSource, UITableViewDelegate>
// ...
#end
After that your implementation file MyViewcontroller.m should help you with the code completion.
One note: don't forget to set yourself as dataSource:
_tableview.dataSource = self;
If you added the tableview by code, you need to create a property (weak) in order to have a reference to your table view after adding it to your view controller's subview. If you add it by using interface builder, you need to create a iboutlet property that will allow you to "bind" your table view property with the xib/storyboard file representing your view controller. Alternatively, you can use UITableViewController as the parent class of your view controller. This class already has a property to access the table view in your view controller.
Tell your controller that you need to conform to the table view protocols and they will start to auto-complete when you try to type them in. You can check the docs of a protocol to find the available methods. Checking the UITableView docs would tell you about the relevant data source and delegate:
The data source must adopt the UITableViewDataSource protocol and the delegate must adopt the UITableViewDelegate protocol.
In your header file:
#interface MyViewController : UIViewController <UITableViewDataSource, UITableViewDelegate>
You have a couple of options.
You could make your class inherit from UITableViewController instead of UIViewController. This will give you a tableView so you don't need to make one.
Or...
Your UIViewController could implement the protocols UITableViewDataSource and UITableViewDelegate. Then set the dataSource and delegate properties of your table view to self (your view controller containing the table).
-First of all you may need to add datasource and delegate of UITableViewController in your UIViewController header file
#interface MyViewController : UIViewController <UITableViewDataSource, UITableViewDelegate>
and then implement the required and optional methods to populate the data in your _tableView.
Sample Code for TableView demonstration by Apple:
https://developer.apple.com/library/ios/samplecode/TableViewSuite/Introduction/Intro.html#//apple_ref/doc/uid/DTS40007318
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?
I have a container view controller with 3 child UIViewController subclasses (added with addChildViewController). I want one of my child view controllers to do something when something is dropped from my container view controller onto it. I'm having trouble grasping how this communication should happen. If I try making a delegate, I get an error in my child view controller because I would both subclasses to import each other.
It sounds like you're having a problem compiling your app because of mutual .h files importing each other, right?
Edit: upon reading your question again, I'm not 100% clear on which view controller needs to call which other one. If I mixed up the
roles of parent and child view controller in my solution, just switch
them. The techniques below let you communicate between any two view
controllers (parent and child, sibling and sibling, etc.)
There's a number of ways to handle this. If you want to stay with a delegate pattern, you could simply rewrite the header to avoid the #import in one of the .h files:
ParentViewController.h:
#import "ChildViewController.h"
#interface ParentViewController: UIViewController {
#private
ChildViewController* childVc;
}
- (void) doSomething;
ChildViewController.h
#class ParentViewController; // NOT #import!
#interface ChildViewController: UIViewController {
#private
ParentViewController* parentVc;
}
ChildViewController.m
#import "ParentViewController.h"
This should avoid the circular dependency that keeps your app from compiling.
Now, although the above works, I might choose another solution, for the sake of cleanliness. Use a protocol. The parent can implement the protocol and then the child only needs to have a delegate that implements the protocol:
#import "MyProtocol.h"
#interface ParentViewController: UIViewController<MyProtocol> {
}
- (void) doSomething;
In MyProtocol.h:
#protocol MyProtocol
- (void) doSomething;
#end
Then in ChildViewController.h
#import "MyProtocol.h"
#interface ChildViewController: UIViewController {
#private
id<MyProtocol> delegate;
}
#property (nonatomic, assign) id<MyProtocol> delegate;
And in ChildViewController.m:
[delegate doSomething];
Or, you could avoid using delegates altogether, and communicate between the controllers using NSNotificationCenter, which decouples them a bit, and avoids your compiler circularity (bidirectional dependency).
Here are the Apple docs on NSNotificationCenter
Couldn't you just go:
MyChildViewController *myChildViewController = (MyChildViewController *)[self.childViewControllers objectAtIndex:0];
[myChildViewController doWhatever];
?
That should let you message the child view controller at the first index of the array childViewControllers (which is a property on UIViewController).