I just noticed that if one makes a private outlet, that works as well and then one can connect xib objects to that. I wanted to know if there is anyone else who has seen this working as well?
Yes. You can make your outlets private. You can even declare them as instance variables in your #implementation, like this:
#implementation MyViewController {
__unsafe_unretained IBOutlet UILabel *_myLabel;
}
Interface Builder will let you connect that instance variable to an object in your nib.
I usually put my outlets in my .m file, either as properties or just as instance variables, unless I have a reason to expose them in the class's public interface.
Related
I would like to know which place is best for placing the IBOutlets from storyboards:
a) In the header file (.h)
b) In a class extension created on the .m file
Thank you
Regards
You have to keep in mind that .h is a public header.
So place your IBOutlet's there if they should be accessible by other classes.
However, even though you can do that. I would say that exposing the IBOutlet's in a public header is not a good practice (From object orientation perspective) since you are exposing some implementation details that should be only visible to whom is concerned about.
In short, placing the IBOutlet's in a class extension in the .m is a good practice.
From Apple's Resource Programming Guide: Nib Files:
Outlets are generally considered private to the defining class; unless there is a reason to expose the property publicly, hide the property declarations a class extension.
Class extension is the best place if you don't want to expose that outlet publicly. Your .h should be neat and clean and should only contain those methods or properties which are public (available for other programmers).
In this way you won't confuse your teammate by not having unnecessary ivars and methods deceleration in .h file
It's all about managing the code and making less confusion, otherwise there are no private methods/properties in Objective-C
Also if you check any sample of apple they follow the same pattern. e.g. LoadingStatus.m has code
#import "LoadingStatus.h"
#interface LoadingStatus ()
#property (nonatomic, strong) UIActivityIndicatorView *progress;
#property (nonatomic, strong) UILabel *loadingLabel;
#end
Assuming that this is for a view controller, option b is better as you shouldn't be exposing the outlets publicly for other classes to directly interact with. They should be considered your private knowledge. Your controller should expose a different and more appropriate interface.
If it's a view it's a bit more grey how you should approach the problem as MVC would lead you towards exposing the outlets to allow the controller to use them. MVVM would lead you towards hiding the outlets so that the view is passed a view model object and internally configures the outlets.
The #interface can appear in both the .h file (public properties) and the .m file (private properties). The IBOutlets should be declared in the .m file.
If you are interested read this topic.
Cheers!
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.
in my projects I don't use Interface Builder and I've noticed one thing that I don't know how to explain. Yet. So, to the point. When we are using IB and defining elements of user interface like UILabel or UIButton in our controller we use this ugly prefix IBOutlet and a 'weak' modifier. This works like music. But when we decide not to use IB and define whole user interface from code it just doesn't work.
Let's assume that I want to add UILabel to controller (using IB). I will have something like this i *.h file:
#property (nonatomic, weak) IBOutlet UILabel * label;
And I don't have to do anything more in *.m file. But if I remove the *.xib file and try to setup my UILabel in, for example, one of init methods, like this:
self.label = [[UILabel alloc] initWithFrame:CGRectMake(0,0,100,20)];
self.label.text = #"some text";
[self.view addSubview:self.label];
It doesn't work until I alter my *.h file to this:
#property (nonatomic, strong) UILabel * label;
Now, I know the difference between weak and strong but I have no idea why we can use weak for ui elements when using IB? Something must keep a strong pointers to these elements, right? But what?? In second case it is controller, but I don't understand how it behaves in the first case.
The reason why Interface Builder creates weak references for IBOutlets is as follows:
IB knows that a view is retained by its superview. So any object in the tree of views there's no need to have strong references other than to the root object. The view controller keeps this strong reference in its main view property.
Now when the view in unloaded (at least until iOS 5), the UIViewController's view property is set to nil, releasing the main view. If the IBOutlets to subviews of this superview would be strong references they would keep part of the view hierarchy in memory. That's unwanted (and could possibly lead to confusion when accessing these orphaned views).
Something must keep a strong pointers to these elements, right? But what??
Correct, you must have at least 1 strong reference to an object for it to exist. You'll only need to have a strong reference to the root level objects of the UI, anything below this can be weak (as the parent objects will own their children). The .xib file in co-ordination with its Files Owner would have done this for you.
See this document on the workings of xib files. Specifically, this snippit:
You typically need strong references to top-level objects to ensure that they are not deallocated; you don’t need strong references to objects lower down in the graph because they’re owned by their parents, and you should minimize the risk of creating strong reference cycles.
From a practical perspective, in iOS and OS X outlets should be defined as declared properties. Outlets should generally be weak, except for those from File’s Owner to top-level objects in a nib file (or, in iOS, a storyboard scene) which should be strong. Outlets that you create should therefore typically be weak
Despite of accepted answer, this is how you can make it in code:
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(0,0,100,20)]; // strong ref
label.text = #"some text";
[self.view addSubview:label]; // strong ref from superview
self.label = label; // weak ref
// Now you can do `label = nil;`
This is the point when loading from XIB. The label already has superview when it is assigned to your weak property.
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.
I'm currently refactoring a couple of view controllers that share a few IBOutlets and IBAction methods. I moved the outlet declarations and the IBAction method into a superclass, cutting these out of the subclasses.
Now, when I open up Interface Builder, I find that I can't see the outlets or actions declared in the superclass. The connections still exist, as I'd wired them up before the refactoring, but they're grayed out. (It's important to note that the connections also WORK, as my action fires on a button press, and my outlets are modified properly.)
The question is, how can I get interface builder to recognize outlets from a superclass? Is this possible, and, if not, what do you all recommend?
(Just for fun, here's my superclass header file:)
#interface TFMainViewController : UIViewController {
UIImageView *logoImage, *thinkfunImage;
UIButton *buyFullButton;
}
#property (nonatomic, retain) IBOutlet UIImageView *logoImage, *thinkfunImage;
#property (nonatomic, retain) IBOutlet UIButton *buyFullButton;
-(IBAction) buyFullVersion;
#end
EDIT: in case anyone's wondering, I'm using Xcode and IB 3.2.5, with the iOS 4.2 SDK.
I didn't realize it was even possible to connect to superclasses in interface builder until about an hour ago. Since this was the only question I could find regarding how to do this, I'll add my answer, even though this question is old. My answer is with regard to Xcode 4, not Xcode 3.
As far as I can tell, you can't connect to outlets in a superclass using the assistant editor, but you can do it by clicking on "File's Owner" in IB. That should show all the outlets in Utilities->Connections Inspector. You can then Ctrl+Click on the outlet in the inspector (click on the '+' sign), and drag it over to your view in IB.
The solution for the problem with the IBOutlet .. is to change the class type to the Base Class in the identity inspector
connect using Control + drag and drop and
change it back to the child class
This works for me
BTW: i used Xcode 6
IB should be able to see outlets from superclasses, I have done this a number of times with no issues. Are you sure you are importing the superclass correctly (using #import instead of #class)? IB needs some way to track back to the superclass.
Switching between the super and subclass in the identity inspector allows you to connect your outlets across the classes. The only issue I found is when you attempt to do this with a UITableViewCell and its subclass. I wanted to re-assign the default textLabel and detailTextLabel instances to labels I create in Interface Builder. The workaround is to create substitute labels and then override the getters to point to these instead.
I'm pretty sure that IB only looks at the actual class you're using to find outlets, and not at superclasses. I think that the easiest solution would be to leave the instance variable declarations in the superclass, but duplicate the #property lines in each subclass.
I'm doing this in XCode 3.2.6. I started with outlets connected to a class, and then made a subclass with additional outlets. When I changed the File's Owner class to the subclass, IB showed the superclass outlets as greyed out. I switched File's Owner to the superclass, then back to the subclass and now all outlets are showing not greyed out.
The simplest way: create interface and implementation files for your subclass(es)!
Perfect example: Juggleware's awesome ShadowButton Subclass of UIButton.
Make sure to create the .h & .m files in your project.
NOTE: There is no need to #import the header files at all since this is simply a class instance of UIButton.
In Interface Builder:
Select the element you which to connect.
Go to Utilities -> Identity Inspector
Change the Class to your subclass (or superclass). NOTE: You might have to type in your subclass name and hit ENTER.
You're done!
Even if you have declared a basic class (UIButton) as IBOutlet in your header file like so...
// YourViewController.h
#interface YourViewController : UIViewController {
IBOutlet UIButton *mybutton;
}
...the class you've set in Interface Builder (ShadowButton) will overwrite it since it's in the view layer.
The best part about this approach is that your code doesn't have any messy dependency issues.
On the project I am currently working, we have a BaseViewController with a UIScrollView as IBOutlet and handles keyboard appearance/disappearance events and slides the content accordingly. At first, I could not connect to that IBOutlet, than solved the problem like this, which is similar to Sosily's answer:
BaseViewController has an IBOutlet, called contentScrollView. I can see 5 previously connected outlets, which are UIScrollViews on other UIViewControllers, created by people who previously worked on the project
I tried to connect my UIScrollView as the contentScrollView. Although my UIViewController is a subclass of BaseViewController, I cannot connect it.
I tried to connect already connected UIScrollViews as the contentScrollView. Although all UIViewControllers are subclasses of BaseViewController, I cannot connect them again, as well. So, I started to look for a trick.
I have created the same contentScrollView IBOutlet on my own UIViewController, connected the scrollView to my own contentScrollView outlet and removed the one that I have just created.
Now the scrollView is connected as contentScrollView to File's Owner, but the only contentScrollView belongs to the BaseViewController. Tested and verified that keyboard events are handled correctly.
I ran into a similar problem with a superclass, but it was due to a bug in Xcode (8.2) where Interface Builder doesn't show outlets in the Connection Inspector if those outlets have been declared with a _Nullable type annotation for Swift compatibility.
Using nullable inside #property's parentheses appears to work around the problem.
This Xcode bug seems to affect outlets in any class (ie. not just superclasses).
I had the same problem, and it turns out it was because in the superclass I had the IBOutlets declared as "_Nullable".
Example:
#property (nonatomic, strong) IBOutlet UITableView *_Nullable mySuperTableView;
When I removed the _Nullable, suddenly the IBOutlets reappeared in IB and all was good again.
(I had only set them to _Nullable because Xcode was complaining "pointer is missing a nullability type specifier"... (don't know why). )