I'm working on building a UICollectionView subclass to cover up more convenient layouts in a simple way. It needs to have another delegate property to handle messages from custom UICollectionViewLayout class, which the UICollectionView class already has, so I wish the delegate property of UICollectionView handles both UICollectionViewDelegate and another custom delegate protocol.
More specifically, I'd like to build something like the followings.
#interface CPGridCollectionView : UICollectionView <CPGridLayoutDelegate>
// add protocol declaration to existing delegate property
#property (nonatomic, weak, nullable) id<UICollectionViewDelegate, CPGridLayoutDelegate> delegate;
// more additional features
#end
But warning came up as:
Auto property synthesis will not synthesize property 'delegate'; it will be implemented by its superclass, use #dynamic to acknowledge intention
It seems that I need to add attributes to existing property of UICollectionView, without breaking the functionality of UICollectionViewDelegate. Is this possible in Obj-C, and how can this warning be avoided?
Related
How you guys slim down your view controllers?, sometimes you end up implementing a lot of protocols in your view controller, so there’s a lot of code inside the controller itself. Reading about how to slim down view controllers in iOS I found that a common way is to move DataSources (http://www.objc.io/issue-1/lighter-view-controllers.html) to other class, but what about other delegates?, or if you create views by code?. First, I think about move each delegate to a NSObject class, so I try this:
self.locationManager.delegate = [[FRRYPetDescriptionViewControllerLocationDelegate alloc] init];
Then I ask in IRC and somebody suggest categories, so this is what I got so far:
// FRRYPetDescriptionViewController.h
#interface FRRYPetDescriptionViewController : UIViewController
#property (nonatomic) CLLocationManager *locationManager;
#property (nonatomic) TPKeyboardAvoidingScrollView *scrollView;
#property (nonatomic) UIView *contentView;
#end
// FRRYPetDescriptionViewController+Protocols.h
#interface FRRYPetDescriptionViewController (Protocols) <UITextViewDelegate, UIActionSheetDelegate, MFMailComposeViewControllerDelegate, UIGestureRecognizerDelegate, MKMapViewDelegate, UIViewControllerTransitioningDelegate, CLLocationManagerDelegate>
#end
// FRRYPetDescriptionViewController+UIAdditions.h
#interface FRRYPetDescriptionViewController (UIAdditions)
- (void)createScrollView;
- (void)createContentView;
#end
// FRRYPetDescriptionViewController+Callbacks.h
#interface FRRYPetDescriptionViewController (Callbacks)
#end
// FRRYPetDescriptionViewController+LocationAdditions.h
#interface FRRYPetDescriptionViewController (LocationAdditions)
#end
This makes me think, what about “private” methods?, do I need to declare all properties in the view controller header file?. What you guys think about this approach or there’s some common pattern to follow to not end with a fat controller?.
Thank you.
The link that you have referred to has beautiful explanation for less bulky controller's programming. But techniques are bit tricky unless you are a seasoned developer. You have asked about multiple techniques in your question. Please check my views on them below: -
Delegates - I personally prefer to keep the delegate code in my controller itself to avoid unwanted confusion specially when you are working with multiple scenarios within the same controller.
Create Views Programmatically - This is the portion where we can cut the large amount of code from UIViewController. Unless it is a single control such as a single label or button, we should make a custom UIView class and let it set all the common properties for view customisation. Controller should only invoke it with necessary parameters.
Properties in Header File - No, concept of data encapsulation says that we should only make those variables public which are required. Rest should move to private domain so that we external objects can't interfere with the class object functionality. So you should declare these properties in class extension (inside .m file). Moreover it is not required to have all variables as properties, when they are private you can declare them as instance variables as property declaration does nothing but to create the getter/setter for that variable.
Private Methods - Same goes to methods as well. If it doesn't have to be exposed to other objects; it should not be in header file. Keep it in extention as private method.
I know there are a lot of similar questions here but I still need some clarification about this concept.
First of all let me start by saying that I do understand what protocols are and how to use them, what I'm having problem understanding is delegation. I do understand that delegation is when one object in a program acts on behalf of another object, sound very simple but hard to see the whole picture.
1- Is delegation just a way to let the compiler know where to look for the code that will be manipulating the object (UITableView etc.)?
2- Do delegation and protocols work together?
3- Can delegation exist without protocols? If yes, can you show me an example.
4- When we declare a protocol and a class conforms to it, can we say that this class conforming to the protocol is delegating on behave of the protocol?
How much of the above is true?
Thanks a lot
1- Is delegation just a way to let the compiler know where to look for the code that will be manipulating the object (UITableView etc.)?
No, delegation is a design pattern. It's just a concept.
2- Do delegation and protocols work together?
Yes they work well together and it's probably the best practice to use protocol for your delegate.
3- Can delegation exist without protocols? If yes, can you show me an example.
Yes you can. Delegation concept is just to remove intellect of an object and put it in the delegate. For exemple an UITableView does not know how many row it has, or what to do when a cell is clicked, so it asks to its delegate.
But the delegate is still another object.
It's better if it implements a particular protocol, but you can do it without.
For exemple :
I've a MyView that is a subview of a MyCustomViewController.
MyCustomViewController.h
- (void)myViewIsTouched;
MyView.h
#property (nonatomic, weak) MyCustomViewController *delegate
MyView.m
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
[self.delegate myViewIsTouched];
}
There is no protocol in this exemple, but it's still a delegate.
(A better way is still using a protocol instead of declaring the method in the .h)
4- When we declare a protocol and a class conforms to it, can we say that this class conforming to the protocol is delegating on behave of the protocol?
I'm not sure about what're saying. But protocols and delegate are not the same thing. An object implementing a protocol does not mean that it's a delegate.
Delegation allows objects to be able to change their appearance / state based on changes in other parts of your application. Setting a
delegate property on an object will allow the compiler to do some
checks at build-time.
Delegation is often achieved by using protocols, since it allows the
delegate object to be of any class instead of a sub-class of a class
with specific behaviour.
Yes, but this would result in your classes becoming tightly coupled since Foo needs to know about Bar and vice-versa. Using protocols allows you to use any class, hence id property, resulting in a loosely coupled system.
Example:
#class Foo;
#interface Bar : NSObject
- (void)respondToSomeAction:(Foo *)obj;
#end
#implementation Bar
- (void)respondToSomeAction:(Foo *)obj {
NSLog("responding to %#",obj);
}
#end
#interface Foo : NSObject
#property (nonatomic, weak) Bar *delegate
#end
#implementation Foo
- (void)someActionTriggered {
[self.delegate respondToSomeAction:self]
}
#end
When a class conforms to a protocol, the class is compelled to adopt the behaviours of the protocol (implement the methods). It only becomes a delegate if it is given some task to do on behalf of another class, e.g. supply the number of rows in a table.
I have used #protocols many times but I think I have been doing it wrong all the time. They have worked always well, but now I want to improve my level so I am trying to do it the better I can.
I always have created a delegate like this:
#protocol CommentViewDelegate;
#interface LZCommentView : UIView
#property (assign, nonatomic) id <CommentViewDelegate> delegate;
#end
#protocol CommentViewDelegate
-(void)showAndHideCommentView;
#end
Now, I have seen that almost all the delegate methods that I see send their own object. Something like this:
-(void)showAndHideCommentView:(LZCommentView *)commentView;
What is the difference between what I did and this? Is one of them better than the other? I have seen that almost everyone who does this, does not use the object in the ViewController.
Another question is, should I use <NSObject> in the #protocol definition?
And the last one, what is better create the #property with assign or strong?
Thank you
Generally, the object that you pass to the delegate can be used so that the same delegate class can be used in different contexts. This gives you more flexibility in cases when a delegate class could potentially be reused.
For example, if showAndHideCommentView needs to interact with a view being shown or hidden, it has two ways of doing it:
Get the view as an argument, or
Reference the view directly, knowing that this delegate is attached to a particular view.
Example of the first approach:
#implementation MyDelegate
-(void)showAndHideCommentView:(LZCommentView *)commentView {
if (isShowing) {
[commentView doSomething];
}
}
#end
Example of the second approach:
#implementation MyDelegate
-(void)showAndHideCommentView {
if (isShowing) {
[self.commentView doSomething];
}
}
#end
The first approach is more flexible than the second one, because it lets you reuse the same code.
According to Apple, it’s best practice to define your protocols to conform to the NSObject protocol, so the answer to your second question is "yes".
As far as the third question goes, the best way to declare delegate properties is with the weak attribute to avoid retain cycles.
1) You should always make your protocol conform to the NSObject protocol. This lets you make use of all of the methods in that protocol.
#protocol CommentViewDelegate <NSObject>
2) Unless you have a good reason to do otherwise, most properties for delegates should be defined as weak. This avoids reference cycles and it ensure the delegate is automatically set to nil if the delegate object is deallocated.
#property (nonatomic, weak) id<CommentViewDelegate> delegate;
3) It's best to include the object in the protocol methods because it offers the most flexibility. It also allows a class to be the delegate of more than one instance. Then the class can tell which instance the protocol method is being called for. Think of a view controller handling multiple buttons or having two or more table views.
I've got a handful of UIViews subclasses as a part of my interface and I'm looking for an easy human readable way to differentiate them in the code -- like the label you set in the "Document" section of the UI editor.
I believe the "Accessibility Label" is exposed, but that doesn't seem like the correct use of that variable.
Am I stuck documenting which Object ID each object has or is there a more intelligible way?
There are lots of ways to do what you want to be done. Here are several of them:
#properties
The first thing that came to my mind are properties. Use your view controller as a storage class by adding following property declarations in your header file:
#property (nonatomic, weak) IBOutlet UIView *myFantasticView;
#property (nonatomic, weak) IBOutlet UIView *myGorgeousView;
...
Then, just simply connect your views to specific outlets in Interface Builder.
The only drawback is that you will have to declare a lot of properties which can become a little confusing.
Custom runtime attributes (another use of #properties)
If you're using UIView's subclasses (I assume you do), you can use your UIView subclass and declare an "identifier" property:
#interface MyView : UIView
#property (nonatomic, strong) NSString *myViewID;
...
#end
Then, assign this value using code (simple property setting) or in Interface Builder:
Now you can identify your views using one of those method. But remember that you can come up with a better, more suitable solution! :)
My problem is a follows
I have a UIViewController subclass which holds a UISegmentedController and four tableviews that I layed out in interface builder.
#interface MultiTableHoldingView : UIViewController{
}
#property (strong, nonatomic) IBOutlet DataTV *dsDataTV;
#property (strong, nonatomic) IBOutlet EnviroTV *dsEnvironmentTV;
#property (strong, nonatomic) IBOutlet LocationTV *dsLocationTV;
#property (strong, nonatomic) IBOutlet Note_AnimalTV *dsNoteAnimal;
#property (strong, nonatomic) IBOutlet UISegmentedControl *diveElementSegmentController;
#property (strong, nonatomic) DiveSite* currentSite;
- (IBAction)diveElementSegmentControllerDidChange:(UISegmentedControl *)sender;
-(void) setFreshWaterColor;
-(void) setSaltwaterColor;
#end
setFreshWaterColor and setSaltWaterColour just set the background colour properties of the MultiTableHoldingView instances UIView and the four tableviews it contains. Both these method work fine when called from MultiTableHoldingView's viewDidLoad method. Heres one of them
-(void) setSaltwaterColor{
DLog(#"in set salt water colour");
self.view.backgroundColor= SaltWaterColor;
_dsLocationTV.backgroundColor=SaltWaterColor;
_dsDataTV.backgroundColor=SaltWaterColor;
_dsEnvironmentTV.backgroundColor=SaltWaterColor;
_dsNoteAnimal.backgroundColor=SaltWaterColor;
}
The other is the same except sets to FreshWaterColor - both are #define i have set up.
I use the segmentedController to turn the hidden properties of the various tableviews on and off. All nice and simple. The tableviews are pulling in their data. Working fine.
When selecting one of my tableview cells on one of the tableViews I want to change the background colour of both my tableview ( in fact all of my tableviews ) and the UIView that is the superview
self.superview.backgroundColor = FreshWaterColor;
works fine for reaching back and changing the instance of MultiTableHoldingView views background property but I want to call the instance of MultiTableHoldingView's setFreshWaterColor and setSaltwaterColor methods.
I have imported MultiTableHoldingViews header into the relevant tableview (EnviroTV), so it knows about it its superviews methods. But if I try to call either of the two methods on self.superview the methods do not show up and if i type them in full I get an the following error
no visible interface for 'UIView' shows the selector 'setFreshWaterColor'
So i checked what kind of object the superview was and its a "class of superview UIViewControllerWrapperView"
I search on this and its apparently "
This is a private view used by the framework. You're not supposed to modify it or whatsoever."
I'm obviously missing something here - how should i call the method on the instance of MultiTableHoldingView ?
Thanks in advance
Simon
Doh - its just delegation as danypata mentions in the comments - i've posted exactly how I did this as an answer below. Tried to make it as clear as possible how delegation works
THE SOLUTION
Step one - get more sleep before coding .
This really is basic objective-c stuff - I just went off at a tangent, looking for someway else to do it, getting confused by my discovery of UIViewControllerWrapperView along the way.
The solution, as danypata rightly suggests in the comments, is to use delegate -a common design pattern in Objective-C - just like you do, for example, when you use another class to supply tableview data
As I've been a numpty and wasted hours of my time today I'll try and make the 'how' clear for other relative newbies or people having an off day and not thinking straight.
In my case I set this up as follows
In my subview class's interface file - EnviroTV.h - I define the following protocol just before the #interface declaration
#protocol EnviroTVProtocol <NSObject>
-(void) setFreshWaterColor;
-(void) setSaltwaterColor;
#end
Then in the #interface section of the same file I add a property of type id which must conform the protocol I just declared .
#property (nonatomic, strong ) id<EnviroTVProtocol> colorChangeDelegate;
You make the type an id - a generic object - as you really don't care what kind of object is going to act as your delegate just that it implement the methods that you need it to run. When an object declares itself to implement a protocol its just promising to implement the method(s) that are required by the protocol
So, when I want to run the methods on the superviews class I call
[self.colorChangeDelegate setFreshWaterColor];
Or
[self.colorChangeDelegate setSaltWaterColor];
The final piece of the delegation pattern is to go into the class thats going to be the delegate (in this case my MultiTableHoldingView class ) and state that it conforms to the protocol
I do this in the MultiTableHoldingView.h file
Changing this line :
#interface MultiTableHoldingView : UIViewController
into this line :
#interface MultiTableHoldingView : UIViewController <EnviroTVProtocol>
means this class promises to implement all the required methods of the EnviroTVProtocol.
Luckily I had already written the two methods. So when I compiled it ran correctly
Newbies - don't be afraid of delegation - its awesome and not as complex as you first imagine it to be
Meanwhile, if anyone can explain what UIViewControllerWrapperView is .....