Different methods for IBOutlet creation - ios

There are at least 3 methods of creating an IBOutlet in Objective-C, for making iOS 10 App, in Xcode 8.
Method 1: in ViewController.h
#interface ViewController : UIViewController
#property (nonatomic, strong) UILabel *textLabel;
#end
Method 2: in the interface of ViewController.m
#interface ViewController () {
IBOutlet UILabel *textLabel;
}
#end
Method 3: in the interface of ViewController.m, using #property
#interface ViewController ()
#property (nonatomic, strong) UILabel *textLabel;
#end
Given that the textLabel has to be accessed & its text is needed to be updated frequently, which method is the correct way to do so?

That all depends on whether you need your outlet to be accessible to classes outside of the containing one; generally I would discourage this because it is good practice to keep your view controllers responsible for updating your UI and not pass this task around to other classes. With this being said, Method 3 would be the best option, however, if you do have to access your object from another class, then simply use Method 1 so it is exposed in your class header.
Method 2 utilises iVars rather than object properties and is not the proper way to declare outlets, it may even cause unexpected behaviour so it is best to avoid this method.

Your code contains no proper IBOutlet. Outlets are connections to Storyboard.
Method 1
This is a property. As it is in .h file, it can be reached from outside. The Objective-C pattern for public.
Method 2
This is an iVar. Do not use iVars if you do not have to.
Method 3
This is a property. As it is in .m file, it can not be reached from outside. The Objective-C pattern for private.
Method 4
A proper IBOutlet looks like this:
#interface ViewController ()
#property (nonatomic, weak) IBOutlet UILabel *label;
#end
It is a simple property. You have to decide if you put it in .h or .m file depending on whether or not you want to publish it.
The IBOutlet simply makes the property connect-able to Storyboard. It's an annotation for Xcode and does not alter the semantic of your code.
Edit 1:
As Sulthan correctly mentions in the comments:
In most situations the correct design pattern is to hide outlets because it's an implementation detail. External classes should not set data directly using views.
Edit 2:
Why "not to use iVars if you do not have to" (2)
Opinion based:
I consider it as good OOP practice to use getters & setters (and thus not to access the variables directly). Also code is easier to read as you know while reading what x = self.variable (property) and x = variable (local variable) are.
If you have to use iVars for some reason, it is common to (and I would recommend to) prefix the name with _. x = _variable (iVar).

Related

Properties vs instance variables, poor class encapsulation?

I'm a fan of not exposing class variables unless needed. In most objective-c code I see, the variables are declared as properties even if they are never to be used by an outsider.
#interface DetailViewController : UIViewController {
__weak IBOutlet UILabel *name;
}
vs
#interface DetailViewController : UIViewController
#property (weak, nonatomic) UILabel *name;
As a student of Software Engineering, this seams to me to be a pretty bad violation of principles such as encapsulation and could potentially lead to unwanted coupling in a large project.
I do understand the KVC aspects of using properties, but not why one would expose variables which are clearly only meant to be used internally in the class, such as the UILabel above.
Could someone explain why this is the preferred way when working with Objective-C on iOS?
Properties encapsulate the memory management (eg assign, retain, copy, strong, weak) of a iVar, while direct access to an iVar (instance variable) does not. This greatly reduces memory bugs.
Non-public properties can be declared at the top of the .m so there's no reason for them to be in the header:
#interface DetailViewController ()
#property (weak, nonatomic) NSString *name;
#end
Properties do create ivars that can be accessed. For the example above, with an explicitly synthesized property, the ivar would be named name while an implicitly synthesized synthesized property will have a leading underscore _name.
IBOutlets are declared in the header even though other classes don't need access to them as they are required so that Interface Builder connect to them and the nib loading system can populate the outlets. IBOutlets are most often going to be views, such as your UILabel.
Edit:
The previous paragraph about IBOulets is a legacy method required for Xcode 3 and earlier. However, newer versions of Xcode can use outlets defined in the implementation file just as the property above thanks to tighter integration of the InterfaceBuilder to the rest of the IDE.
What you see is an old style. Earlier Objective-C compilers required that you declare instance variables in the interface. However, by default they are #protected, so not everyone can just use them.
Current best practice is that you don't declare instance variables at all but use properties, unless you need to declare them (if you have a custom getter for a readonly property, or both custom getter and setter for a readwrite property, no instance variable is generated automatically), that you declare them in your .m file unless someone really needs to access them, that you declare properties and methods in your .m file unless someone needs to access them, and that you don't declare methods at all unless needed.
It's also quite common to declare a property as readonly in the header file, and redeclare it as read/write in the implementation.
In other words, hide what you can hide.
The first example indicates that you want to use the label as an outlet for a Xib or Storyboard. This answer sheds some light on that case: https://stackoverflow.com/a/1236985/171933
In general, however, you don't need to declare internal instance variables as properties. Actually, you can move them completely out of the header by putting them into your .m file like so:
#implementation DetailViewController
{
NSInteger _someValue;
UILabel *_someLabel;
}
That way you can really only keep the things in the header that should be visible to the outside. And those things would typically either be properties or plain old methods.

Access human readable UIElement label from IOS app

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! :)

iOS SDK - Correct methodology in making connections with an outlet?

I know that if I use
#interface TPN : UIViewController{
IBOutlet UIView *testView;
}
#property (strong, nonatomic) IBOutlet UIView *testView;
I know that the first one is essentially a private variable that is only accessed within the class. and the second one "#property" is able to be accessed from an instantiated object. I find it odd in most tutorials that people tend to set properties when they are usually changing an outlet from within the class itself. Is there any guideline I should be following?
You no longer need to specify the ivar at all. Nor is there a need to use #synthesize.
Use a property, but make sure it is weak, not strong
#interface TPN : UIViewController
#property (weak, nonatomic) IBOutlet UIView *testView;
In the implementation you can now access the ivar as _testView.
For a private property (above is public) instead put the #property within a category in the implementation file:
#import "TPN.h"
#interface TPN ()
#property (weak, nonatomic) IBOutlet UIView *testView;
#end
#implementation TPN
....
You are right. If you are only going to use the instance variable inside the class, there is no point to make it a property. A property simply means a pair of getter/setter. If you don't need to do anything specially when getting/setting, you can just use the instance variable directly.
Prior to ARC, there was a side benefit to using properties everywhere, in that it was easier to do memory management for object pointers -- properties declared as retain would automatically release and retain for you when you set it, without you typing that code yourself like you would have to do with an instance variable directly. However, now in ARC, the compiler does that for you anyway when you assign, if it's a strong instance variable. So this benefit is now irrelevant.
I would suspect that a lot of the reason people use properties for outlets is because of perpetuation of that usage in almost all tutorials and examples (possibly due to the previous benefit with retain memory management), and they simply don't realize they can do something different.

Accessing a method on a views superview - What is UIViewControllerWrapperView?

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 .....

Connecting IBOutlets, variable, property or both?

When using IB in combination with assistant view you control-drag an element in the IB to the .h file and create an outlet. You can drag it to one of 2 place, either inside the variable declaration block or outside the block.
If you drag it inside the variable block you get something like this:
#interface MyViewController : UIViewController {
IBOutlet UIButton *foo;
}
dragging it outside the block gives you something like....
#interface ViewController : UIViewController {
}
#property (retain, nonatomic) IBOutlet UIButton *foo;
I've thought about how they are different and I'm a little confused. Ok, I understand synthesized properties do some magic and create instance variables at runtime (only on 64bit/ARM). So I believe I understand how the 2 options work.
What's the best option though? First option generates less code and seems simpler.
Second version offers public accessors/mutators, but I rarely access outlets from outside my class (and if I do, it's almost always with encapsulation). From the time I've started iOS work I've exclusively used this option.
Am I missing anything or should I make the switch to variable based outlets in most cases?
The ability to declare IBOutlet in the property declaration is relatively new #property (retain, nonatomic) IBOutlet UIButton *foo;
Previously, you had to declare IBOutlet UIButton *foo inside the curly braces and then synthesize the property. Now, declaring the IBOutlet in the curly braces is redundant.
You have two options to declaring the properties now. Option 1 is to declare it in your .h file, which will make it public. Alternatively, you can create a private interface in your .m file using:
#interface MYCLASS()
#end
and declare your properties there. This is my preferred way of doing it unless I need public access to that property (which should be the exception, not the norm if you are obeying MVC conventions).
Short answer: It doesn't make a much of a difference either way.
Long answer: If you want set/mutator methods, then drag outside of the block. If you don't care about methods and are just going to access the variables directly then putting them in as straight variables inside the block is probably the way to go.
Public visibility:
If you just specify the IBOutlet as a variable then you can use #private or #protected to prevent outside access. If you really want a #property for some reason you can still control public visibility by moving the property out of the .h and into a class extension in the .m file.
Conclusion: Myself, I'm sticking with the straight variable declaration and save the other options for when I need something extra.
IBOutlets are best inside of the block, unless you really plan on working with it in the .m file.
Remember, you can have both. The one inside of the variable block is essentially, in all basics, just for when you use it in IBActions.
The property can be used in the .m file for further customization.
Again, you can use both, it just depends on the extent you're using it.

Resources