UIImageView subclassing - ios

I want to create a custom class RoundedImageView by subclassing the UIImageView class.
In order to make it round, I use the following code:
self.layer.cornerRadius = self.frame.size.width/2;
self.layer.masksToBounds = YES;
Where should I place this code? In the initializer? Or maybe in layoutSubviews? I know there's not a good idea to access properties from self within the initializer(because self may not be fully initialized), that is why I am confused and I'm not sure where to place this code.

The canonical initialization for a UIView (or any class derived from UIView) looks like this
- (void)setup
{
// do any initialization here
}
- (void)awakeFromNib
{
[self setup];
}
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self)
[self setup];
return self;
}
This works for views that are created by storyboard and views that are created programmatically. Source: the CS193P videos.

The initialisation method is definitely the right place, since you want to set those properties only once.
About your concern related to acessing properties, the idea is that a property accessor could be overridden in a derived class and thus access portions of the object not already properly initialised. This is not a great concern in your case, I would say, unless you plan to override layer's getter in some not fully sensible ways.

Related

Conceptual q about custom UIKit objects

Simple question: what is the standard method of creating a customized version of say a UILabel, UIButton, etc. such that I can easily use it in multiple places? Is it simply to extend the appropriate class:
import UIKit
class FormField: UITextField {
override init()
{
super.init()
// borderStyle = UITextBorderStyle.None
}
}
Basically just want to get some default values set for some UI objects so I can easily drop them into the interface when necessary. Not really sure how to get this working.
It is very rare to subclass something like UILabel.
The most common approach is a HAS-A pattern, where you let a controller (often a UIViewController) manage the view for you and you reuse that. Alternately, you may make a UIView that contains the view you want to customize, and customizes it for you (passing along things that need to be passed along).
You can also have a "configure my view this way" function that you can call on an existing standard view. I haven't seen this very often in reusable code. My experience is that these kind of configuration functions turn out to be very app specific, but they're reasonable common.
Things like UITextField have delegate methods already, and a common way to customize them is to create a reusable delegate that applies certain behaviors.
It depends of course on what you're trying to achieve, but subclassing is pretty far down on the list of patterns, unless it's a class explicitly designed and documented to be subclassed (like UIView or UIViewController).
UIView and its subclasses have two designated initializers, -initWithFrame: and -initWithCoder:. The first is for programmatic instantiation while the latter is for views being unpacked from a Storyboard or NIB. Because of this, the common pattern for subclassing UIView subclasses is the following (I'm using ObjC but the Swift code should be easy to figure out):
- (instancetype)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if(self) {
[self commonInit];
}
return self;
}
- (instancetype)initWithCoder:(NSCoder *)aDecoder {
self = [super initWithCoder:aDecoder];
if(self) {
[self commonInit];
}
return self;
}
- (void)commonInit {
// Do your special setup here!
}
Then, if you're using the views in Interface Builder, go to the Identity tab on the right-assistant-editor-sidebar, and in the top box where it says UILabel or UIButton, put in your custom button class name.
Hopefully this clears things up a bit.

What "self" is being returned in objective-c init methods?

- (id)init {
self = [super init];
if (self) {
// initialize instance variables here
}
return self;
}
Having read Apple's documentation, I still am confused by the above. I am familiar with the concept of self and this in other languages, but here we are assigning self to superclass init self = [super init]; then checking if that returned nil or not. But then we are returning self in the last line. I mean, usually returning self means "I am returning myself" but here since we assigned self to super init earlier, aren't we returning super as self instead of ourselves as self?
Edit - I have tried to be as clear as possible above but using self in a sentence is tricky. Hopefully, someone can help.
No!
Every initializer must call [super init]. This means the super initializer you are calling in your example does the same.
All these initializers can prepare the object as they need to do, so that it finally is ready to be used as the class inheritance describes it. They all work on the same object. (That's the point of OO. An object can do everything its (super)classes describe. So all (super)classes must initialize the object.)
self is nothing more than a pointer to the object the method/initializer is currently working on. It does not have a special meaning like "I am returning myself".
Be aware of that no init method allocates the object. This done with [AClass alloc].
self is the same as this in Java or C#.
The line
self = [super init];
is allowing the parent class to do it's initialization first. If everything is good (in other words, self != nil) then we can do any initialization we need to do.
Finally we return self.
You have to remember that self or ourselves as you describe it is made up of the initialization done by the parent class and this class.
Update
This class and parent class need to cooperate to initialize self. We need to give the parent class first shot by calling [super init]. When it's done it returns an object that we refer to as self. We then further initialize self with the values for the instance variables that this child class defines. But both initializers are working on the same object.
Let me put this in layman's language.
You must have read about Cars and how a Prius is an object of Car when studying object oriented programming.
You basically have a superclass Car from which all cars inherit, like Prius, Focus, etc. For Prius to be a car, in object oriented terminology, and inheritance, Prius must first inherit from Car, and then add Prius specific attributes/properties.
Same way, every class you make in Objective-C (and pretty much every object oriented programming language), you must first inherit from a parent class. The root class being NSObject (objective-c).
[super init] initializes the parent class, with all it's properties. After that is successful, you add your own attributes/properties, specific to your class.
A common example is when you override the init method when subclassing a UITableViewCell.
- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
UIImageView* imageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"Foo"]];
imageView.frame = CGRectMake(0, 0, 100, 50);
[self.contentView addSubview:imageView];
}
return self;
}
Here, initWithStyle:reuseIdentifier: is overridden. It first calls [super initWithStyle:style reuseIdentifier:reuseIdentifier]. This initializes a default UITableViewCell with the style and reuseIdentifier supplied to it. You have access to all properties of UITableViewCell here. Additionally, an imageView is added to the cell, which is specific to this cell only.
The Object Initialization document is really good to help understand this.

Explain data management of storyboards?

I have this UIImageView in one of my view controllers
I've set it as a subclass of this cutsom UIImageView, ABProfileImageView
Here's that ProfileImageView class:
#import "ABProfileImageView.h"
#implementation ABProfileImageView
-(id)init {
self = [super initWithImage:[UIImage imageNamed:#"X4Hibreb.jpeg"]];
if (self) {
self.image = [UIImage imageNamed:#"X4Hibreb.jpeg"];
}
return self;
}
#end
and I have an image name X4Hibreb.jpeg in the project folder,
So I'm not sure why the imageview doesn't have the picture I set it to be? I think it's some higher understanding thing that I don't get, I usually do programmatic UI's, this is the first time I'm using a storyboard.
You have implemented init, however you also need to implement initWithCoder:, which is the initializer called when decoding objects from NIB files and storyboards.
Usually a good practice is to move common code from init and initWithCoder: to a _commonInit method and call it in the init.
You need to initialize the imageview.

Where do you set a subview's layer properties? Why not in the initWithCoder

I created a custom UITableViewCell (for this example, let's just say the subclass is MyViewCell) which has an Nib file associated to it MyViewCell.xib. The nib contains a UITableViewCell with one subview UIView (named cardContainer) that's simply a rectangle with a blue background. I want to add a drop shadow around the UIView, so I added set the layer properties in the -initWithCoder call:
#implementation MyViewCell
- (id)initWithCoder:(NSCoder *)aDecoder
{
self = [super initWithCoder:aDecoder];
if (self) {
[self initView];
}
return self;
}
- (void) initView
{
UIBezier Path*shadowPath =[UIBezierPath bezierPathWithRect:view.bounds];
[self.cardContainer.layer setShadowColor: [UIColor blackColor].CGColor];
[self.cardContainer.layer setShadowOpacity: 0.8];
[self.cardContainer.layer setShadowRadius:3.0];
[self.cardContainer.layer setShadowOffset: CGSizeMake(2.0,2.0)];
view.layer.shadowPath = shadowPath.CGPath;
}
#end
The problem I'm having is that these layer properties aren't being drawn. If I call the -initView call within awakeFromNib or drawRect it's drawn as expected. My question: why doesn't my original code work? Where should I be calling initView? Is there some view lifecycle? I understand that the initWithCoder doesn't have the outlets connected, but it didn't crash at runtime.
I read through Apple documentation around Views and searched through the SO questions without finding an answer. I found this SO answer, but again doesn't explain.
Hey I found a better way to do this ,just add some runtime attributes for your subview cardContainer
like this
no more code in .m file anymore.
EDIT:
From:NSNibAwaking Protocol
Important: Because the order in which objects are instantiated from an archive is not guaranteed, your initialization methods should not send messages to other objects in the hierarchy. Messages to other objects can be sent safely from within awakeFromNib—by which time it’s assured that all the objects are unarchived and initialized (though not necessarily awakened, of course).
You need to add this,
self.cardContainer.layer.masksToBounds = NO;

Is there a init method in iOS that is always called?

Is there a method that is always called in Cocoa? Many classes have init or initWith, but even worse they can be loaded from a nib or something. I don't want to have to scrape around and find how it does this in this case. I just want to set some initial variables and other things, and I want a method to subclass that I can depend on no matter if it's a UIView, UIViewController or UITableViewCell etc.
No there is not such a method. init comes from NSObject so every object can use it, and as well subclasses define their own initialization methods. UIView, for example, defines initWithFrame: and furthermore there are init methods from protocols, such as NSCoding which defines initWithCoder:. This is the dynamic nature of objective-C, anything can be extended at any time. That being said, there are some patterns. UIViewController almost always takes initWithNibName:bundle: and UIView almost always takes initWithFrame: or initWithCoder:. What I do is make an internal initialize method, and just have the other inits call it.
- (void)initialize
{
//Do stuff
}
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if(self)
{
[self initialize];
}
}
- (id)initWithCoder:(NSCoder *)aCoder
{
self = [super initWithCoder:aCoder];
if(self)
{
[self initialize];
}
}
Not 100% sure that it is always called, but I am pretty sure that this is a viable option. To be perfectly honest, I can't recall that I have ever seen this method used in practice and I usually shy away from using this method (I have absolutely no idea why, probably because it's just not the cleanest and most comprehensive method to achieve this...):
-didMoveToSuperview()
From documentation:
Tells the view that its superview changed.
The default implementation of this method does nothing. Subclasses can override it to perform additional actions whenever the superview changes.
There's many ways you can write a custom initializer.
- (id)initWithString:(NSString *)string {
if((self == [super init])) {
self.string = string;
}
return self;
}
That's just how I write my initializers in general. For example, the one above takes a string. (you don't have to pass strings if you don't want).
Btw, init is a method. According to the header for NSObject, init has a method implementation.

Resources